1558 Commits
Author SHA1 Message Date
nagachika 1efcec846a merge revision(s) 3a55e5c6d3:
[PATCH] Discard unreachable exception table entries

	Dead labels can leave invalid catch entries that make ensure clauses
	run for code that cannot be reached.
2026-10-25 22:50:27 +09:00
nagachika 7096aad15f merge revision(s) 79e6d37b55: [Backport #22218]
[PATCH] Preserve line events across jump optimization. (#18122)

	[Bug #22218]
2026-08-09 16:03:39 +09:00
nagachika 6162535e5d merge revision(s) 034fa35ff0: [Backport #22099]
[PATCH] CI shape comparison needs to take into account # of kwargs given

	If number of keyword arguments are not taken into account when comparing CI
	shapes, invalid bindings happen:

	```
	class Foo
	  def bar(a: nil, b: nil)
	    p [:body, a: a, b: b]
	  end
	end

	foo = Foo.new

	foo.bar(a: 1, b: 2)   #=> [:body, {a: 1, b: 2}] # prime CC
	foo.bar(99, a: 0)     #=> [:body, {a: 99, b: nil}] # BAD!
	```

	Also, set max keywords to 2**16-1 so we don't grow the cc_entries_entry struct.

	Fixes [Bug #22099]
2026-08-09 15:06:30 +09:00
nagachika 8a159ffbd6 merge revision(s) 1f473ed633: [Backport #22126]
[PATCH] [Bug #22126] Fix partial DCE with loop back-edges

	The old `remove_unreachable_chunk` walked the dead code once and tried
	to do two things at the same time -- count label references inside that
	chunk and figure out what exactly to delete.

	That breaks for loops -- the loop label comes before the jump back to it;
	on the first walk, the label looks like it has no references inside the
	chunk yet, so the optimizer thinks something outside the dead code still
	needs it. It stops there and deletes only the setup code before the loop,
	leaving the loop body behind.

	I've changed the algorithm to use two passes to mitigate the issue;
	I believe the performance overhead is neglible

	```shell
	$ ruby --parser parse.y -e 'if []; else; a => [*, 42, *]; end'
	$ ruby --parser prism -e   'if []; else; a => [*, 42, *]; end'
	-- raw disasm--------
	   trace: 1
	   0000 putnil                                                           (   1)
	   0001 leave                                                            (   1)
	 <L018> [sp: 1, unremovable: 0, refcnt: 1]
	   0002 dup                                                              (   1)
	   0003 topn                 2                                           (   1)
	   0005 opt_le               <calldata:<=, 1>                            (   1)
	   0007 branchunless         <L021>                                      (   1)
	   0009 topn                 3                                           (   1)
	   0011 topn                 1                                           (   1)
	   0013 opt_aref             <calldata:[], 1>                            (   1)
	   0015 putobject            42                                          (   1)
	   0017 dupn                 2                                           (   1)
	   0019 checkmatch           2                                           (   1)
	   0021 dup                                                              (   1)
	   0022 branchif             <L023>                                      (   1)
	   0024 putspecialobject     1                                           (   1)
	   0026 putobject            "%p === %p does not return true"            (   1)
	   0028 topn                 3                                           (   1)
	   0030 topn                 5                                           (   1)
	   0032 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
	   0034 setn                 10                                          (   1)
	   0036 putobject            false                                       (   1)
	   0038 setn                 12                                          (   1)
	   0040 pop                                                              (   1)
	   0041 pop                                                              (   1)
	 <L023> [sp: 4, unremovable: 0, refcnt: 1]
	   0042 setn                 2                                           (   1)
	   0044 pop                                                              (   1)
	   0045 pop                                                              (   1)
	   0046 branchif             <L020>                                      (   1)
	   0048 putobject_INT2FIX_1_                                             (   1)
	   0049 opt_plus             <calldata:+, 1>                             (   1)
	   0051 jump                 <L018>                                      (   1)
	 <L021> [sp: 1, unremovable: 0, refcnt: 1]
	*  0053 adjuststack          3                                           (   1)
	   0055 putspecialobject     1                                           (   1)
	   0057 putobject            "%p does not match to find pattern"         (   1)
	   0059 topn                 2                                           (   1)
	   0061 opt_send_without_block <calldata:core#sprintf, 2>                (   1)
	   0063 setn                 4                                           (   1)
	   0065 putobject            false                                       (   1)
	   0067 setn                 6                                           (   1)
	   0069 pop                                                              (   1)
	   0070 pop                                                              (   1)
	   0071 jump                 <L012>                                      (   1)
	 <L020> [sp: 1, unremovable: 0, refcnt: 1]
	   0073 adjuststack          3                                           (   1)
	   0075 pop                                                              (   1)
	   0076 jump                 <L003>                                      (   1)
	 <L012> [sp: -1, unremovable: 0, refcnt: 1]
	   0078 pop                                                              (   1)
	   0079 putspecialobject     1                                           (   1)
	   0081 topn                 4                                           (   1)
	   0083 branchif             <L024>                                      (   1)
	   0085 putobject            NoMatchingPatternError                      (   1)
	   0087 putspecialobject     1                                           (   1)
	   0089 putobject            "%p: %s"                                    (   1)
	   0091 topn                 4                                           (   1)
	   0093 topn                 7                                           (   1)
	   0095 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
	   0097 opt_send_without_block <calldata:core#raise, 2>                  (   1)
	   0099 jump                 <L025>                                      (   1)
	 <L024> [sp: -1, unremovable: 0, refcnt: 1]
	   0101 putobject            NoMatchingPatternKeyError                   (   1)
	   0103 putspecialobject     1                                           (   1)
	   0105 putobject            "%p: %s"                                    (   1)
	   0107 topn                 4                                           (   1)
	   0109 topn                 7                                           (   1)
	   0111 opt_send_without_block <calldata:core#sprintf, 3>                (   1)
	   0113 topn                 7                                           (   1)
	   0115 topn                 9                                           (   1)
	   0117 opt_send_without_block <calldata:new, 3>                         (   1)
	   0119 opt_send_without_block <calldata:core#raise, 1>                  (   1)
	 <L025> [sp: -1, unremovable: 0, refcnt: 1]
	   0121 adjuststack          7                                           (   1)
	   0123 putnil                                                           (   1)
	   0124 leave                                                            (   1)
	 <L003> [sp: -1, unremovable: 0, refcnt: 1]
	   0125 adjuststack          6                                           (   1)
	   0127 putnil                                                           (   1)
	   0128 leave                                                            (   1)
	---------------------
	-e:1: argument stack underflow (-2)
	-e: compile error (SyntaxError)
	```

	Reproducible on every version since `3.4.0-preview2`
	(`docker run -e ALL_RUBY_SHOW_DUP=yes -e ALL_RUBY_SINCE=3.4 --rm rubylang/all-ruby ./all-ruby -We 'if []; else; a => [*, 42, *]; end'`)

	There seem to be another issue related to Prism's DCE & loops:

	```shell
	$ ruby --parser parse.y --dump insn -e 'if []; else; while true; end; end'
	== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,33)>
	0000 putnil                                                           (   1)[Li]
	0001 leave
	$ ruby --parser prism --dump insn -e 'if []; else; while true; end; end'
	== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,33)>
	0000 newarray                               0                         (   1)[Li]
	0002 branchunless                           11
	0004 putnil
	0005 leave
	0006 pop
	0007 jump                                   11
	0009 putnil
	0010 pop
	0011 jump                                   11
	0013 putnil
	0014 leave
	```

	the reason `branchunless` is not eliminated is that prism does not emit
	`NODE_ZLIST` like `parse.y` does and instead emits `PM_ARRAY_NODE-0`
	which is not treated as a always-truthy value by the peephole-optmizer.
2026-08-09 14:32:59 +09:00
nagachika fdac94eee1 merge revision(s) 7f4f3c8ee7: [Backport #22004]
[PATCH] [Bug #22004] Fix short-circuited loop conditions
2026-05-04 21:31:57 +09:00
viralpraxis 5d2a244bfd [Bug #21644] compile.c: fix newrange INSN peephole optimization for chilled string
ref: https://bugs.ruby-lang.org/issues/21644

```shell
$ ruby -v -e '("a" || "b").."c"'
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [x86_64-linux]
-e:1: warning: possibly useless use of .. in void context
-e:1: [BUG] Stack consistency error (sp: 7, bp: 6)
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0002 p:0013 s:0007 e:000005 EVAL   -e:1 [FINISH]
c:0001 p:0000 s:0003 E:001920 DUMMY  [FINISH]

-- Ruby level backtrace information ----------------------------------------
-e:1:in '<main>'

-- Threading information ---------------------------------------------------
Total ractor count: 1
Ruby thread count for this ractor: 1

-- C level backtrace information -------------------------------------------
ruby/3.4.7/lib/libruby.so.3.4(rb_print_backtrace+0x8) [0x78aa9573c882] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm_dump.c:823
ruby/3.4.7/lib/libruby.so.3.4(rb_vm_bugreport) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm_dump.c:1155
ruby/3.4.7/lib/libruby.so.3.4(rb_bug_without_die_internal+0x6b) [0x78aa9544c62f] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/error.c:1097
ruby/3.4.7/lib/libruby.so.3.4(rb_bug) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/error.c:1115
ruby/3.4.7/lib/libruby.so.3.4(vm_stack_consistency_error+0x1f) [0x78aa9544f091] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm_insnhelper.c:6523
ruby/3.4.7/lib/libruby.so.3.4(vm_get_cref) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/insns.def:1134
ruby/3.4.7/lib/libruby.so.3.4(vm_setclassvariable) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm_insnhelper.c:1630
ruby/3.4.7/lib/libruby.so.3.4(vm_setclassvariable) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm_insnhelper.c:1627
ruby/3.4.7/lib/libruby.so.3.4(vm_exec_core) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/insns.def:253
ruby/3.4.7/lib/libruby.so.3.4(vm_exec_loop+0xa) [0x78aa95724959] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm.c:2622
ruby/3.4.7/lib/libruby.so.3.4(rb_vm_exec) /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/vm.c:2598
ruby/3.4.7/lib/libruby.so.3.4(rb_ec_exec_node+0xa5) [0x78aa95525695] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/eval.c:281
ruby/3.4.7/lib/libruby.so.3.4(ruby_run_node+0x83) [0x78aa95529333] /tmp/ruby-build.20251010151551.31019.jR04SY/ruby-3.4.7/eval.c:319
ruby/3.4.7/bin/ruby(rb_main+0x21) [0x59d86f5e0186] ./main.c:43
ruby/3.4.7/bin/ruby(main) ./main.c:68
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_call_main+0x7a) [0x78aa9502a1ca] ../sysdeps/nptl/libc_start_call_main.h:58
/lib/x86_64-linux-gnu/libc.so.6(call_init+0x0) [0x78aa9502a28b] ../csu/libc-start.c:360
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main_impl) ../csu/libc-start.c:347
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main) (null):0
[0x59d86f5e01d5]
```

The optimization in question:

https://github.com/ruby/ruby/blob/957c832db137e67289e93dfd9fd9e915b1f2fc87/compile.c\#L3453-L3480

Before entering the `newrange` optimization, the iseq looks like this:

```
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(1,17)>
0000 putchilledstring                       "a"                       (   1)[Li]
0002 dup
0003 branchif                               8
0005 pop
0006 putchilledstring                       "b"
0008 putchilledstring                       "c"
0010 newrange                               0
0012 leave
```

So the optimization constructs a new range using the wrong operands (`"b"` and `"c"` instead of `"a"` and `"c"`).

I tried to fix this by checking whether the two previous instructions are labeled.
2025-10-20 09:24:12 -07:00
Takashi Kokubun 22c2262b83 merge revision(s) 354d47ae5b: [Backport #21569]
IBF: Avoid unaligned load on 32 bit platforms

	[Bug #21569]
2025-09-29 14:36:06 -07:00
Takashi Kokubun 88a96147a2 merge revision(s) ef2b26cc3e: [Backport #21611]
`struct iseq_catch_table` is packed
2025-09-29 14:31:05 -07:00
Jean Boussier 6882012473 [3.4] compile.c: Handle anonymous variables in outer_variable_cmp (#13493) 2025-09-16 14:17:51 -07:00
John Hawthorn 6d338434e3 Add missing write barrier for hash on iseq
Found by wbcheck
2025-09-05 09:05:06 -07:00
John Hawthorn 7de09550da Add write barrier to rb_cArray_empty_frozen
Found by wbcheck
2025-09-05 09:05:06 -07:00
John Hawthorn f6448d6584 Add write barrier to rb_cHash_empty_frozen
Found by wbcheck
2025-09-05 09:05:06 -07:00
John Hawthorn 3fb7c4f7e6 Use write barriers when loading catch table
Found by wbcheck
2025-09-05 09:05:06 -07:00
John Hawthorn eb2f79ff60 Add missing write barriers to ibf_load
Found by wbcheck
2025-09-05 09:05:06 -07:00
John Hawthorn d2caeb1e70 Fix a missing write barrier to mandatory_only_iseq
Found by wbcheck
2025-09-05 09:05:06 -07:00
Takashi Kokubun 3a06b3d9f5 Revert "merge revision(s) ff222ac27a: [Backport #21370]"
This reverts commit acb19e8707.
2025-07-14 14:17:12 -07:00
Takashi Kokubun acb19e8707 merge revision(s) ff222ac27a: [Backport #21370]
compile.c: Handle anonymous variables in `outer_variable_cmp`

	[Bug #21370]
2025-07-14 13:54:31 -07:00
Takashi Kokubun 1b6f9cd6ea merge revision(s) 0d6263bd41: [Backport #21220]
Fix coverage measurement for negative line numbers

	Fixes [Bug #21220]

	Co-Authored-By: Mike Bourgeous <mike@mikebourgeous.com>
	Co-Authored-By: Jean Boussier <jean.boussier@gmail.com>
2025-04-14 16:25:55 +09:00
Takashi Kokubun 3fdf7279a0 merge revision(s) e0d600ec19: [Backport #21012]
Avoid opt_aset_with optimization inside multiple assignment

	Previously, since the opt_aset_with optimization was introduced,
	use of the opt_aset_with optimization inside multiple assignment
	would result in a segfault or incorrect instructions.

	Fixes [Bug #21012]

	Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
2025-02-13 21:08:44 -08:00
tompng e06b3b5ad1 [Bug #20927] Fix compile_shareable_literal_constant for hash with keyword splat
Compilation of NODE_HASH in compile_shareable_literal_constant does not support hash that contains keyword splat.
If there is a keyword splat, fallback to default case.
2024-12-15 16:55:30 +09:00
ydah 6ccaa37eb3 [Bug #20926] Fix a crashes with shareable_constant_value: experimental_everything using parse.y's parser
https://bugs.ruby-lang.org/issues/20926
2024-12-06 12:32:20 +09:00
Yusuke Endoh 7f34c75b8b Use RSTRING_PTR instead of StringValuePtr
... since it is certain to be a String in this context.

Also, I want to avoid the anxious use of `StringValuePtr(str)` and
`RSTRING_LEN(str)` as arguments in the same function call.
2024-11-30 03:15:03 +09:00
Nobuyoshi Nakada de89bff122 INIT_ANCHOR no longer needed usually 2024-11-28 19:02:56 +09:00
Nobuyoshi Nakada b8f248ee5a Initialize LINK_ANCHOR totally 2024-11-28 18:53:10 +09:00
Nobuyoshi Nakada 319ac31529 Assert that non-empty LINK_ANCHOR does not loop
After any `LINK_ELEMENT` sequence is added, `LINK_ANCHOR` must not
loop.
2024-11-28 15:13:52 +09:00
Randy StaunerandAndrew Novoselac 1dd40ec18a Optimize instructions when creating an array just to call include? (#12123)
* Add opt_duparray_send insn to skip the allocation on `#include?`

If the method isn't going to modify the array we don't need to copy it.
This avoids the allocation / array copy for things like `[:a, :b].include?(x)`.

This adds a BOP for include? and tracks redefinition for it on Array.

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* YJIT: Implement opt_duparray_send include_p

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>

* Update opt_newarray_send to support simple forms of include?(arg)

Similar to opt_duparray_send but for non-static arrays.

* YJIT: Implement opt_newarray_send include_p

---------

Co-authored-by: Andrew Novoselac <andrew.novoselac@shopify.com>
2024-11-26 14:31:08 -05:00
6deeec5d45 Mark strings returned by Symbol#to_s as chilled (#12065)
* Use FL_USER0 for ELTS_SHARED

This makes space in RString for two bits for chilled strings.

* Mark strings returned by `Symbol#to_s` as chilled

[Feature #20350]

`STR_CHILLED` now spans on two user flags. If one bit is set it
marks a chilled string literal, if it's the other it marks a
`Symbol#to_s` chilled string.

Since it's not possible, and doesn't make much sense to include
debug info when `--debug-frozen-string-literal` is set, we can't
include allocation source, but we can safely include the symbol
name in the warning message, making it much easier to find the source
of the issue.

Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>

---------

Co-authored-by: Étienne Barrié <etienne.barrie@gmail.com>
Co-authored-by: Jean Boussier <jean.boussier@gmail.com>
2024-11-13 09:20:00 -05:00
Koichi Sasada ab7ab9e450 Warning[:strict_unused_block]
to show unused block warning strictly.

```ruby
class C
  def f = nil
end

class D
  def f = yield
end

[C.new, D.new].each{|obj| obj.f{}}
```

In this case, `D#f` accepts a block. However `C#f` doesn't
accept a block. There are some cases passing a block with
`obj.f{}` where `obj` is `C` or `D`. To avoid warnings on
such cases, "unused block warning" will be warned only if
there is not same name which accepts a block.
On the above example, `C.new.f{}` doesn't show any warnings
because there is a same name `D#f` which accepts a block.

We call this default behavior as "relax mode".

`strict_unused_block` new warning category changes from
"relax mode" to "strict mode", we don't check same name
methods and `C.new.f{}` will be warned.

[Feature #15554]
2024-11-06 11:06:18 +09:00
Takashi Kokubun 478e0fc710 YJIT: Replace Array#each only when YJIT is enabled (#11955)
* YJIT: Replace Array#each only when YJIT is enabled

* Add comments about BUILTIN_ATTR_C_TRACE

* Make Ruby Array#each available with --yjit as well

* Fix all paths that expect a C location

* Use method_basic_definition_p to detect patches

* Copy a comment about C_TRACE flag to compilers

* Rephrase a comment about add_yjit_hook

* Give METHOD_ENTRY_BASIC flag to Array#each

* Add --yjit-c-builtin option

* Allow inconsistent source_location in test-spec

* Refactor a check of BUILTIN_ATTR_C_TRACE

* Set METHOD_ENTRY_BASIC without touching vm->running
2024-11-04 11:14:28 -05:00
257f78fb67 Show where mutated chilled strings were allocated
[Feature #20205]

The warning now suggests running with --debug-frozen-string-literal:

```
test.rb:3: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)
```

When using --debug-frozen-string-literal, the location where the string
was created is shown:

```
test.rb:3: warning: literal string will be frozen in the future
test.rb:1: info: the string was created here
```

When resurrecting strings and debug mode is not enabled, the overhead is a simple FL_TEST_RAW.
When mutating chilled strings and deprecation warnings are not enabled,
the overhead is a simple warning category enabled check.

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-10-21 12:33:02 +02:00
Kevin Newton e17243d325 Point keyword->table into iseq local table 2024-10-18 14:16:02 -04:00
Nobuyoshi Nakada 9a90cd2284 Cast via uintptr_t function pointer between object pointer
- ISO C forbids conversion of function pointer to object pointer type
- ISO C forbids conversion of object pointer to function pointer type
2024-10-08 23:29:49 +09:00
Luke Gruber d592ddd5e6 Fix compile issue with a short-circuited if/unless condition and defined?
This caused an issue when `defined?` was in the `if` condition. Its
instructions weren't appended to the instruction sequence even though it was compiled
if a compile-time known logical short-circuit happened before the `defined?`. The catch table
entry (`defined?` compilation produces a catch table entry) was still on the iseq even though the
instructions weren't there. This caused faulty exception handling in the method.
The solution is to no add the catch table entry for `defined?` after a compile-time known logical
short circuit.

This shouldn't touch much code, it's only for cases like the following,
which can occur during debugging:

    if false && defined?(Some::CONSTANT)
    "more code..."
    end

Fixes [Bug #20501]
2024-10-01 02:12:56 +09:00
Yudai Takada 0209b9554d Extract setup_branch
From duplicate code in `decl_branch_base` and `add_trace_branch_coverage`.
2024-09-27 01:03:40 +09:00
Peter Zhu e02a6097e6 Set node_id to -1 in add_adjust_info
add_adjust_info will increment the insns_info_index, so we need to set
the node_id to -1 to prevent a "Conditional jump or move depends on
uninitialised value" in Valgrind.
2024-09-24 08:59:15 -04:00
Peter Zhu 2230ac4a28 Fix potentially missing write barrier in iseq_build_kw
We're writing objects to the iseq but not firing the write barrier.
2024-09-19 14:51:21 -04:00
Peter Zhu ead280fe8b Replace RB_OBJ_WRITTEN with RB_OBJ_WRITE in iseq_set_arguments_keywords 2024-09-19 14:51:21 -04:00
Jeremy Evans 29f2cb83fb Fix evaluation order issue in f(**h, &h.delete(key))
Previously, this would delete the key in `h` before keyword
splatting `h`.  This goes against how ruby handles `f(*a, &a.pop)`
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0012 splatkw
0013 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT>, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 <calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE>
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0018 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT>, nil
0021 leave
```

This is the same as 07d3bf4832, except that
it removes unnecessary hash allocations when using the prism compiler.

Fixes [Bug #20640]
2024-09-18 12:46:07 -07:00
Jeremy Evans 9c12c39ed1 Revert "Fix evaluation order issue in f(**h, &h.delete(key))"
This reverts commit 07d3bf4832.

No failures in the pull request CI, but there are now allocation
test failures.
2024-09-18 11:26:10 -07:00
Jeremy Evans 07d3bf4832 Fix evaluation order issue in f(**h, &h.delete(key))
Previously, this would delete the key in h before keyword
splatting h.  This goes against how ruby handles f(*a, &a.pop)
and similar expressions.

Fix this by having the compiler check whether the block pass
expression is safe.  If it is not safe, then dup the keyword
splatted hash before evaluating the block pass expression.

For expression: `h=nil; f(**h, &h.delete(:key))`

VM instructions before:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 getlocal_WC_0                          h@0
0006 getlocal_WC_0                          h@0
0008 putobject                              :key
0010 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0012 splatkw
0013 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT>, nil
0016 leave
```

VM instructions after:

```
0000 putnil                                                           (   1)[Li]
0001 setlocal_WC_0                          h@0
0003 putself
0004 putspecialobject                       1
0006 newhash                                0
0008 getlocal_WC_0                          h@0
0010 opt_send_without_block                 <calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE>
0012 getlocal_WC_0                          h@0
0014 putobject                              :key
0016 opt_send_without_block                 <calldata!mid:delete, argc:1, ARGS_SIMPLE>
0018 send                                   <calldata!mid:f, argc:1, ARGS_BLOCKARG|FCALL|KW_SPLAT|KW_SPLAT_MUT>, nil
0021 leave
```

Fixes [Bug #20640]
2024-09-18 11:18:29 -07:00
Étienne BarriéandJean Boussier bf9879791a Optimized instruction for Hash#freeze
If a Hash which is empty or only using literals is frozen, we detect
this as a peephole optimization and change the instructions to be
`opt_hash_freeze`.

[Feature #20684]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-09-05 12:46:02 +02:00
Étienne BarriéandJean Boussier a99707cd9c Optimized instruction for Array#freeze
If an Array which is empty or only using literals is frozen, we detect
this as a peephole optimization and change the instructions to be
`opt_ary_freeze`.

[Feature #20684]

Co-authored-by: Jean Boussier <byroot@ruby-lang.org>
2024-09-05 12:46:02 +02:00
Jeremy EvansandPablo Herrero a3562c2a0a Remove incorrect setting of KW_SPLAT_MUT flag
Fixes [Bug #20701]

Co-authored-by: Pablo Herrero <pablodherrero@gmail.com>
2024-08-27 09:08:22 -07:00
Nobuyoshi Nakada f73d435262 Check compile_branch_condition results 2024-08-20 12:26:02 +09:00
tompng 992596fb7a Fix next inside block argument stack underflow
[Bug #20344]
Fix compile_next adding removable adjust label
2024-08-12 18:09:32 +09:00
Jean Boussier ca5b7276c6 compile.c: don't allocate empty default values list
It just wastes memory.
2024-08-11 15:04:35 +02:00
tomoya ishida 1870505f47 Fix wrong unreachable chunk remove when jump destination label is unremovable 2024-07-30 15:31:58 +09:00
Randy Stauner acbb8d4fb5 Expand opt_newarray_send to support Array#pack with buffer keyword arg
Use an enum for the method arg instead of needing to add an id
that doesn't map to an actual method name.

$ ruby --dump=insns -e 'b = "x"; [v].pack("E*", buffer: b)'

before:

```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring                       "x"                       (   1)[Li]
0002 setlocal_WC_0                          b@0
0004 putself
0005 opt_send_without_block                 <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 newarray                               1
0009 putchilledstring                       "E*"
0011 getlocal_WC_0                          b@0
0013 opt_send_without_block                 <calldata!mid:pack, argc:2, kw:[#<Symbol:0x000000000023110c>], KWARG>
0015 leave
```

after:

```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring                       "x"                       (   1)[Li]
0002 setlocal_WC_0                          b@0
0004 putself
0005 opt_send_without_block                 <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 putchilledstring                       "E*"
0009 getlocal                               b@0, 0
0012 opt_newarray_send                      3, 5
0015 leave
```
2024-07-29 16:26:58 -04:00
Nobuyoshi Nakada 5f1eb0dc79 Fix wrong conversion in disasm dump
`LINK_ELEMENT::type` is an `enum` not a `VALUE`, `FIX2LONG` doesn't
make sense.
2024-07-26 11:24:53 +09:00
yui-knk f2728c3393 Change RESBODY Node structure
Extracrt exception variable into `nd_exc_var` field
to keep the original grammar structure.

For example:

```
begin
rescue Error => e1
end
```

Before:

```
@ NODE_RESBODY (id: 8, line: 2, location: (2,0)-(2,18))
+- nd_args:
|   @ NODE_LIST (id: 2, line: 2, location: (2,7)-(2,12))
|   +- as.nd_alen: 1
|   +- nd_head:
|   |   @ NODE_CONST (id: 1, line: 2, location: (2,7)-(2,12))
|   |   +- nd_vid: :Error
|   +- nd_next:
|       (null node)
+- nd_body:
|   @ NODE_BLOCK (id: 6, line: 2, location: (2,13)-(2,18))
|   +- nd_head (1):
|   |   @ NODE_LASGN (id: 3, line: 2, location: (2,13)-(2,18))
|   |   +- nd_vid: :e1
|   |   +- nd_value:
|   |       @ NODE_ERRINFO (id: 5, line: 2, location: (2,13)-(2,18))
|   +- nd_head (2):
|       @ NODE_BEGIN (id: 4, line: 2, location: (2,18)-(2,18))
|       +- nd_body:
|           (null node)
+- nd_next:
    (null node)
```

After:

```
@ NODE_RESBODY (id: 6, line: 2, location: (2,0)-(2,18))
+- nd_args:
|   @ NODE_LIST (id: 2, line: 2, location: (2,7)-(2,12))
|   +- as.nd_alen: 1
|   +- nd_head:
|   |   @ NODE_CONST (id: 1, line: 2, location: (2,7)-(2,12))
|   |   +- nd_vid: :Error
|   +- nd_next:
|       (null node)
+- nd_exc_var:
|   @ NODE_LASGN (id: 3, line: 2, location: (2,13)-(2,18))
|   +- nd_vid: :e1
|   +- nd_value:
|       @ NODE_ERRINFO (id: 5, line: 2, location: (2,13)-(2,18))
+- nd_body:
|   @ NODE_BEGIN (id: 4, line: 2, location: (2,18)-(2,18))
|   +- nd_body:
|       (null node)
+- nd_next:
    (null node)
```
2024-07-26 07:29:32 +09:00