For upstreaming, we want functions we export either prefixed with "rb_"
or made static. Historically we haven't been following this rule, so we
were "leaking" a lot of symbols as `make leak-globals` would tell us.
This change unifies everything YJIT into a single compilation unit,
yjit.o, and makes everything unprefixed static to pass `make leak-globals`.
This manual "unified build" setup is similar to that of vm.o.
Having everything in one compilation unit allows static functions to
be visible across YJIT files and removes the need for declarations in
headers in some cases. Unnecessary declarations were removed.
Other changes of note:
- switched to MJIT_SYMBOL_EXPORT_BEGIN which indicates stuff as being
off limits for native extensions
- the first include of each YJIT file is change to be "internal.h"
- undefined MAP_STACK before explicitly redefining it since it
collide's with a definition in system headers. Consider renaming?
We need to reconstruct interpreter state before calling into the
routines to be able to raise exceptions. I'm getting a crash in
debug build with:
make test-all 'TESTS=test/ruby/variable.rb' RUN_OPTS='--yjit-call-threshold=1 --yjit-max-versions=1'
The old heap object check is not as efficient as the one in
jit_guard_known_klass(). Also, the old code saves cfp->sp after popping
the operands off the stack, which might cause the operands to be not
marked by the GC in some circumstances.
When calling "to_s" on an instance of String, the method simply returns
self. In this situation most of the work comes from setting up the
method call. It turns out that both railsbench and liquid-render do this
a lot.
When generating code for opt_send_without_block, we already generate a
known class guard, so we can detect when the receiver is a String
instance. Since gen_send_cfunc() is also used for gen_invokesuper(), and
gen_invokesuper() doesn't generate a known class guard, a new nullable
parameter for specialized codegen function is added.
Closes GH-245
Before this change, when we encounter a constant cache that is specific
to a lexical scope, we unconditionally exit. This change falls back to
the interpreter's cache in this situation.
This should help constant expressions in `class << self`, which is popular
at Shopify due to the style guide.
This change relies on the cache being warm while compiling to detect the
need for checking the lexical scope for simplicity.
YJIT expects the VM to invalidate opt_getinlinecache when updating the
constant cache, and the invalidation used to happen even when YJIT can't
use the cached value.
Once the first invalidation happens, the block for opt_getinlinecache
becomes a stub. When the stub is hit, YJIT fails to compile the
instruction as the cache is not usable. The stub becomes a block that
exits for opt_getinlinecache which can be invalidated again. Some
workloads that bust the interpreter's constant cache can create an
invalidation loop with this behavior.
Check if the cache is usable become doing invalidation to fix this
problem.
In the test harness, evaluate the test script in a lambda instead of a
proc so `return` doesn't return out of the harness.
Send instructions currently generate the exact same side exit twice.
Cache the exit the first time we generate it. Also add a comment
explaining what side exits do.
Closes GH-117.
We need to fire the write barrier during ivar set. This function
extracts the write barrier function then calls it.
Co-Authored-By: John Hawthorn <john@hawthorn.email>
We have a check to ensure we don't have to push args on the stack to
call a cfunc with many args. However we never need to use the stack for
variadic cfuncs, so we shouldn't care about the number of arguments.
The code path for leave that returns to the interpreter
(gen_leave() -> yjit_gen_leave_exit()) used to have the logic:
```
cfp->sp++;
cfp->sp[-1] = return_val;
cfp->sp--;
return return_val;
```
The SP changes it made was unnecessary and this change removes it.
After this change, `leave` doesn't adjust the `cfp->sp` of the caller
and only writes `cfp->sp[0]`. To accomodate this in the JIT-to-JIT
return case, return stubs have an `sp_offset` of 1.
The change removes sp adjustment from the JIT-to-JIT return case, too,
making it more efficient. Also, since the C method case of `send`
has an `sp_offset` of 1 after the call, this change enables block
version sharing.
Previously checktype only supported heap objects, however it's not
uncommon to receive an immediate, for example when string interpolating
a Symbol or Integer.
This change fixes some cases where YJIT fails to fire tracing events.
Most of the situations YJIT did not handle correctly involves enabling
tracing while running inside generated code.
A new operation to invalidate all generated code is added, which uses
patching to make generated code exit at the next VM instruction
boundary. A new routine called `jit_prepare_routine_call()` is
introduced to facilitate this and should be used when generating code
that could allocate, or could otherwise use `RB_VM_LOCK_ENTER()`.
The `c_return` event is fired in the middle of an instruction as opposed
to at an instruction boundary, so it requires special handling. C method
call return points are patched to go to a fucntion which does everything
the interpreter does, including firing the `c_return` event. The
generated code for C method calls normally does not fire the event.
Invalided code should not change after patching so the exits are not
clobbered. A new variable is introduced to track the region of code that
should not change.
RUBY_DEBUG have a very significant performance overhead. Enough that
YJIT with RUBY_DEBUG is noticeably slower than the interpreter without
RUBY_DEBUG.
This makes it hard to collect yjit-stats in production environments.
By allowing to collect JIT statistics without the RUBy_DEBUG overhead,
I hope to make such use cases smoother.
The FIXME is there so we remember to investigate why insns clears the
temporary array. Is this necessary? If it's not we can remove it from
both.
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>