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

76 Коммитов

Автор SHA1 Сообщение Дата
Nobuyoshi Nakada 4e4c4fab3c
YJIT: Use proper size prefix and conversion where IL32LLP64 2021-12-29 14:09:37 +09:00
Alan Wu 7e27de2f1e
YJIT: Remove unused branch_t::src_ctx field
No one reads it at the moment and it's heap allocated.
2021-12-15 16:13:23 -05:00
Alan Wu 82bb9cedd3 YJIT: Fix leak in compilation loop
Previously, when there are too many blocks in a batch, the last block in
the batch is not tracked in the array of batches and not freed.
2021-12-08 16:59:52 -05:00
Alan Wu 6beb05c2ad YJIT: Undo add_block_version() in OOM code path
Preivously, [1] failed to undo the effect of applying
add_block_version() to a block, leaving dangling pointers in the iseq
when compilation fails.

[1]: d0772632bf
2021-12-07 17:20:56 -05:00
Alan Wu 794b9a28b5 YJIT: Add integrity checks for blockid
Verify that the iseq idx pair for the block is valid in
invalidate_block_version(). While we are at it, bound loop
iterating over instructions to `iseq_body->iseq_size`.
2021-12-06 20:27:15 -05:00
Aaron Patterson 157095b3a4 Mark JIT code as writeable / executable depending on the situation
Some platforms don't want memory to be marked as writeable and
executable at the same time. When we write to the code block, we
calculate the OS page that the buffer position maps to.  Then we call
`mprotect` to allow writes on that particular page.  As an optimization,
we cache the "last written" aligned page which allows us to amortize the
cost of the `mprotect` call.  In other words, sequential writes to the
same page will only call `mprotect` on the page once.

When we're done writing, we call `mprotect` on the entire JIT buffer.
This means we don't need to keep track of which pages were marked as
writeable, we let the OS take care of that.

Co-authored-by: John Hawthorn <john@hawthorn.email>
2021-12-01 12:45:59 -08:00
Alan Wu d0772632bf YJIT: Fail gracefully while OOM for new entry points
Previously, YJIT crashes with rb_bug() when asked to compile new methods
while out of executable memory.

To handle this situation gracefully, this change keeps track of all the
blocks compiled each invocation in case YJIT runs out of memory in the
middle of a compliation sequence. The list is used to free all blocks in
case compilation fails.

yjit_gen_block() is renamed to gen_single_block() to make it distinct from
gen_block_version(). Call to limit_block_version() and block_t
allocation is moved into the function to help tidy error checking in the
outer loop.

limit_block_version() now returns by value. I feel that an out parameter
with conditional mutation is unnecessarily hard to read in code that
does not need to go for last drop performance. There is a good chance
that the optimizer is able to output identical code anyways.
2021-12-01 12:25:28 -05:00
Alan Wu b5b6ab4194
YJIT: Add ability to exit to interpreter from stubs
Previously, YJIT assumed that it's always possible to generate a new
basic block when servicing a stub in branch_stub_hit(). When YJIT is out
of executable memory, for example, this assumption doesn't hold up.

Add handling to branch_stub_hit() for servicing stubs without consuming
more executable memory by adding a code path that exits to the
interpreter at the location the branch stub represents. The new code
path reconstructs interpreter state in branch_stub_hit() and then exits
with a new snippet called `code_for_exit_from_stub` that returns
`Qundef` from the YJIT native stack frame.

As this change adds another place where we regenerate code from
`branch_t`, extract the logic for it into a new function and call it
regenerate_branch(). While we are at it, make the branch shrinking code
path in branch_stub_hit() more explicit.

This new functionality is hard to test without full support for out of
memory conditions. To verify this change, I ran
`RUBY_YJIT_ENABLE=1 make check -j12` with the following patch to stress
test the new code path:

```diff
diff --git a/yjit_core.c b/yjit_core.c
index 4ab63d9806..5788b8c5ed 100644
--- a/yjit_core.c
+++ b/yjit_core.c
@@ -878,8 +878,12 @@ branch_stub_hit(branch_t *branch, const uint32_t target_idx, rb_execution_contex
                 cb_set_write_ptr(cb, branch->end_addr);
             }

+if (rand() < RAND_MAX/2) {
             // Compile the new block version
             p_block = gen_block_version(target, target_ctx, ec);
+}else{
+    p_block = NULL;
+}

             if (!p_block && branch_modified) {
                 // We couldn't generate a new block for the branch, but we modified the branch.
```

We can enable the new test along with other OOM tests once full support
lands.

Other small changes:
 * yjit_utils.c (print_str): Update to work with new native frame shape.
       Follow up for 8fa0ee4d40.
 * yjit_iface.c (rb_yjit_init): Run yjit_init_core() after
       yjit_init_codegen() so `cb` and `ocb` are available.
2021-11-26 18:00:42 -05:00
Alan Wu 13d1ded253 YJIT: Make block invalidation more robust
This commit adds an entry_exit field to block_t for use in
invalidate_block_version(). By patching the start of the block while
invalidating it, invalidate_block_version() can function correctly
while there is no executable memory left for new branch stubs.

This change additionally fixes correctness for situations where we
cannot patch incoming jumps to the invalidated block. In situations
such as Shopify/yjit#226, the address to the start of the block
is saved and used later, possibly after the block is invalidated.

The assume_* family of function now generate block->entry_exit before
remembering blocks for invalidation.

RubyVM::YJIT.simulate_oom! is introduced for testing out of memory
conditions. The test for it is disabled for now because OOM triggers
other failure conditions not addressed by this commit.

Fixes Shopify/yjit#226
2021-11-22 18:23:28 -05:00
Maxime Chevalier-Boisvert cdebf57ec6
Add --yjit-no-type-prop so we can test YJIT without type propagation (#5135)
* Add --yjit-no-type-prop so we can test YJIT without type propagation

* Fix typo in command line option

* Leave just two test workflows enable for YJIT
2021-11-18 10:44:31 -05:00
Maxime Chevalier-Boisvert 2421527d6e
YJIT code pages refactoring for code GC (#5073)
* New code page allocation logic

* Fix leaked globals

* Fix leaked symbols, yjit asm tests

* Make COUNTED_EXIT take a jit argument, so we can eliminate global ocb

* Remove extra whitespace

* Change block start_pos/end_pos to be pointers instead of uint32_t

* Change branch end_pos and start_pos to end_addr, start_addr
2021-11-04 16:05:41 -04:00
Alan Wu f6da559d5b Put YJIT into a single compilation unit
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?
2021-10-20 18:19:42 -04:00
Alan Wu f36a5a98c0 style: line break before "else" 2021-10-20 18:19:41 -04:00
Alan Wu ec4998bd69 style: switch statements indent
Case labels get half an indent and the opening brace is on the same line
as "switch".
2021-10-20 18:19:41 -04:00
Alan Wu a10cf74e5c style: align pointer "*" to the right 2021-10-20 18:19:41 -04:00
Maxime Chevalier-Boisvert 0c1aa17556 Remove a few more uses of the global cb/ocb 2021-10-20 18:19:41 -04:00
Maxime Chevalier-Boisvert f1eb48cb23 Step 2 to remove the global cb/ocb objects. 2021-10-20 18:19:41 -04:00
Alan Wu 82405ac48a Add counters for tracking invalidations 2021-10-20 18:19:41 -04:00
John Hawthorn fbde1d9bee Store block callee_cme in darray
This allows a block version to have dependencies on multiple CMEs.
2021-10-20 18:19:39 -04:00
Aaron Patterson 0ca04e2dd4 Only clear the JIT function when we invalidate the entry block
We should only clear the JIT function when the entry point is
invalidated.  Right now we only support compiling functions with a PC
offset of zero (functions that take optional parameters can start at
non-zero PC), so this patch just checks that the index is 0 before
clearing the jit function
2021-10-20 18:19:39 -04:00
Alan Wu bd876c243a TracePoint support
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.
2021-10-20 18:19:39 -04:00
Jean Boussier 0dc3bba6f2 Allow to compile with --yjit-stats support but not the full RUBY_DEBUG
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.
2021-10-20 18:19:39 -04:00
John Hawthorn 8fa0ee4d40 Use callee-saved regs for REG_SP, REG_EP, REG_CFP 2021-10-20 18:19:39 -04:00
John Hawthorn ed8aa3409a Detach mapping to local in ctx_set_local_type
Similar to the previous fix to ctx_clear_local_types, we must detach
mappings to a local if we are changing its value.
2021-10-20 18:19:38 -04:00
John Hawthorn 6d852e847e Fix stack size check for ctx_get_opnd_type
Previously all stack operands would become unknown once the stack grew
beyond a certain size. This worked, but unnecessarily hid available
information.
2021-10-20 18:19:38 -04:00
John Hawthorn 48dca3348a Move yjit_type_of_value into yjit_core.c 2021-10-20 18:19:38 -04:00
John Hawthorn d78ea4abec Implement verify_ctx for debugging 2021-10-20 18:19:38 -04:00
John Hawthorn 2e707ee66f Don't generate entry point when PC != 0
If we hit this at PC > 0 (ie. with an optional argument) the provided
types and context are likely incorrect and it is likely this block can't
be used.
2021-10-20 18:19:38 -04:00
John Hawthorn 641ef34dde Allow upgrading first N types when stack is large 2021-10-20 18:19:38 -04:00
John Hawthorn 5d155fc8a1 Improve comments for mapping functions 2021-10-20 18:19:38 -04:00
John Hawthorn 42574768a1 Fix ctx_clear_local_types 2021-10-20 18:19:38 -04:00
John Hawthorn 250b97da9b Make ctx_diff aware of mappings 2021-10-20 18:19:38 -04:00
John Hawthorn 6c80150d40 Introduce ctx_{get,set}_opnd_mapping 2021-10-20 18:19:38 -04:00
John Hawthorn 4ea69e5c0b Rename to ctx_upgrade_opnd_type 2021-10-20 18:19:38 -04:00
Aaron Patterson e8617d0e7e Make sure we can still compile with the JIT disabled
If `--disable-jit-support` is passed to configure, then `jit_func` is
removed from the iseq body and we can't compile YJIT.  This commit
detects when the JIT function pointer is gone and disables YJIT in that
case.
2021-10-20 18:19:38 -04:00
Aaron Patterson 8fd3e0e9ff Fix BOP invalidation
Instead of mutating the iseqs, just clear the JIT function.
2021-10-20 18:19:38 -04:00
Aaron Patterson 05b5a7f011 Add a guard that we start executing on the first PC
Methods with optional parameters don't always start executing at the
first PC, but we compile all methods assuming that they do.  This commit
adds a guard to ensure that we're actually starting at the first PC for
methods with optional params
2021-10-20 18:19:37 -04:00
Aaron Patterson 0fdcdd267f fix alignment 2021-10-20 18:19:37 -04:00
John Hawthorn cb5571eece Flatten mappings when clearing locals
We clear locals when we know their values might change (ex. when
performing a method call). However previously values on the stack which
were originally pushed from a local would still point back to that
local.

With this commit, when clearing locals, we'll now iterate over the
mappings of the stack and copy the known type from the local to the
stack mapping, removing the association to the local.

This should mean both that we'll retain any information we already know
about the local type, and that if a local is modified we won't
incorrectly infer it's new type from the existing value on the stack.
2021-10-20 18:19:37 -04:00
Noah Gibbs 25792c17d4 Convert yjit static stat variables to counters 2021-10-20 18:19:36 -04:00
Maxime Chevalier-Boisvert 7030cae969 Try running with more YJIT options in CI to surface more bugs 2021-10-20 18:19:36 -04:00
Maxime Chevalier-Boisvert f54e6e1310 Update comment 2021-10-20 18:19:36 -04:00
Maxime Chevalier-Boisvert ecd4487a48 Remove #define MAX_VERSIONS, now using command-line option 2021-10-20 18:19:36 -04:00
Maxime Chevalier-Boisvert 99341d4a18 Fix issue in yjit_free_block causing segfault
This addresses issue #55
2021-10-20 18:19:35 -04:00
Maxime Chevalier-Boisvert bce6dea72d Fix assertions in `invalidate_block_version()`, add small repro (#14)
* Fix block invalidation assertions

* Add Alan's small repro for double invalidation bug
2021-10-20 18:19:34 -04:00
Maxime Chevalier-Boisvert 96f4f918b0 Implement greedy versioning. Refactor versioning logic. (#10)
* Implement eager versioning. Refactor versioning logic.

* Add --version-limit and --greedy-versioning command-line args
2021-10-20 18:19:34 -04:00
Maxime Chevalier-Boisvert 60496b6666 Pass self type through method calls 2021-10-20 18:19:34 -04:00
Maxime Chevalier-Boisvert 0cc73ca2a9 Malloc branch entries (#112)
* Malloc branch entries

* Add ASM comment for stack overflow check

* WIP

* Fix branch GC code. Add rb_darray_remove_unordered().

* Fix block end_pos after branch rewriting. Remove dst_patched bits.
2021-10-20 18:19:33 -04:00
Maxime Chevalier-Boisvert 646c7b3ff6 Temporarily increase MAX_BRANCHES until we have a better solution 2021-10-20 18:19:33 -04:00
Maxime Chevalier-Boisvert dac830553e Diff the local types in ctx_diff() 2021-10-20 18:19:33 -04:00