The `f(arg, *arg, **arg, **arg)` case was previously not optimized.
The optimizer didn't optimize this case because of the multiple
keyword splats, and the compiler didn't optimize it because the
`f(*arg, **arg, **arg)` optimization added in
0ee3960685 didn't apply.
I found it difficult to apply this optimization without changing
the `setup_args_core` API, since by the time you get to the ARGSCAT
case, you don't know whether you were called recursively or directly,
so I'm not sure if it was possible to know at that point whether the
array allocation could be avoided.
This changes the dup_rest argument in `setup_args_core` from an int
to a pointer to int. This allows us to track whether we have allocated
a caller side array for multiple splats or splat+post across
recursive calls. Check the pointed value (*dup_rest) to determine the
`splatarray` argument. If dup_rest is 1, then use `splatarray true`
(caller-side array allocation), then set *dup_rest back to 0, ensuring
only a single `splatarray true` per method call.
Before calling `setup_args_core`, check whether the array allocation
can be avoided safely using `splatarray false`. Optimizable cases are:
```
// f(*arg)
SPLAT
// f(1, *arg)
ARGSCAT
LIST
// f(*arg, **arg)
ARGSPUSH
SPLAT
HASH nd_brace=0
// f(1, *arg, **arg)
ARGSPUSH
ARGSCAT
LIST
HASH nd_brace=0
```
If so, dup_rest is set to 0 instead of 1 to avoid the allocation.
After calling `setup_args_core`, check the flag. If the flag
includes `VM_CALL_ARGS_SPLAT`, and the pointed value has changed,
indicating `splatarray true` was used, then also set
`VM_CALL_ARGS_SPLAT_MUT` in the flag.
My initial attempt at this broke the `f(*ary, &ary.pop)` test,
because we were not duplicating the ary in the splat even though
it was modified later (evaluation order issue). The initial attempt
would also break `f(*ary, **ary.pop)` or `f(*ary, kw: ary.pop)` cases
for the same reason. I added test cases for those evaluation
order issues.
Add setup_args_dup_rest_p static function that checks that a given
node is safe. Call that on the block pass node to determine if
the block pass node is safe. Also call it on each of the hash
key/value nodes to test that they are safe. If any are not safe,
then set dup_rest = 1 so that `splatarray true` will be used to
avoid the evaluation order issue.
This new approach has the affect of optimizing most cases of
literal keywords after positional splats. Previously, only
static keyword hashes after positional splats avoided array
allocation for the splat. Now, most dynamic keyword hashes
after positional splats also avoid array allocation.
Add allocation tests for dynamic keyword keyword hashes after
positional splats.
setup_args_dup_rest_p is currently fairly conservative. It
could definitely be expanded to handle additional node types
to reduce allocations in additional cases.
The M:N threading stack cleanup machinery tries to call MADV_FREE on the native
thread's stack, and calls rb_bug if it fails. Unfortunately, there's no way to
distinguish between "You passed bad parameters to madvise" and "MADV_FREE is
not supported on the kernel you are running on"; both cases just return EINVAL.
This means that if you have a Ruby on a system that was built on a system with
MADV_FREE and run it on a system without it, you get a crash in nt_free_stack.
I ran into this because rr actually emulates MADV_FREE by just returning EINVAL
and pretending it's not supported (since it can otherwise introduce
nondeterministic behaviour). So if you run bootstraptest/test_ractor.rb under
rr, you get this crash.
I think we should just get rid of the error handling here; freeing memory like
this is strictly optional anyway.
[Bug #20632]
If the symbol node is interpolated like this `:"#{foo}"` the instruction
sequence should be `putstring` followed by `intern`. In this case it was
a `putobject` causing the `test_yjit` tests to fail. Note that yjit is
not required to reproduce - the instructions are `putstring` and
`intern` for yjit and non-yjit with the original parser.
To fix I moved `pm_interpolated_node_compile` out of the else, and
entirely removed the conditional. `pm_interpolated_node_compile` knows
how / when to use `putstring` over `putobject` already. The `intern` is
then added by removing the conditional.
Before:
```
== disasm: #<ISeq:<main>@test2.rb:1 (1,0)-(1,11)>
0000 putobject :foo ( 1)[Li]
0002 leave
```
After:
```
== disasm: #<ISeq:<main>@test2.rb:1 (1,0)-(1,11)>
0000 putstring "foo" ( 1)[Li]
0002 intern
0003 leave
```
Fixes the test `TestYJIT#test_compile_dynamic_symbol`. Related to ruby/prism#2935
* YJIT: Allow dev_nodebug to disasm release-mode code
* Revert "YJIT: Squash canary before falling back"
This reverts commit f05ad373d8.
The stray canary issue should have been solved by
def7023ee4, alleviating this codegen
accommodation.
* s/runtime_assertions/runtime_checks/
---------
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
We checking completeness of a SpecSet, we should always ignore
dependencies not relevant for the current platform, since the resolver
and the lockfile ignore those too.
https://github.com/rubygems/rubygems/commit/c4b0c6d84e
When we have an empty hash the iseq should have a `newhash` but instead
had a `duphash`. To fix, check if the node's elements are equal to `0`.
If so we want a `newhash`, otherwise use the original `duphash`
instructions.
Before:
```
== disasm: #<ISeq:<main>@test2.rb:1 (1,0)-(1,2)>
0000 duphash {} ( 1)[Li]
0002 leave
```
After:
```
== disasm: #<ISeq:<main>@test2.rb:1 (1,0)-(1,2)>
0000 newhash 0 ( 1)[Li]
0002 leave
```
Fixes the test `TestYJIT#test_compile_newhash`. Related to ruby/prism#2935
As `__has_attribute` macro is always defined in internal/compilers.h,
gcc warns `-Wunguarded-availability-new` as unknown option. Check if
the warning option is usable instead.
While writing some Markdown documentation for Rails, I came across an
interesting case where trying to link to an instance method at the start
of a line would instead parse as an H1 heading:
```markdown
#response_body=
```
Expected:
```html
<a href=""><code>#response_body=</code></a>
```
Actual:
```html
<h1>response_body=</h1>
```
According to the CommonMark spec:
> At least one space or tab is required between the # characters and the
> heading’s contents, unless the heading is empty. Note that many
> implementations currently do not require the space. However, the space
> was required by the original ATX implementation, and it helps prevent
> things like the following from being parsed as headings:
>
> Example 64
So while some implementations do not follow this requirement, I believe
RDoc should because it makes it easy to write text similar to Example 64
(which was used in the new test) and it also enables automatically
linking to instance methods at the start of a line.
This PR tweaked the documentation to correct an error encountered
when running the example code of `Prism::Dispatcher`.
This aims to make understanding the example smoother.
https://github.com/ruby/prism/commit/165a1a0e78