| Age | Commit message (Collapse) | Author |
|
|
|
* ZJIT: Add dump to file for --zjit-stats
* ZJIT: Rename --zjit-stats=quiet to --zjit-stats-quiet
|
|
## Components
This PR adds functionality to visualize HIR using the [Iongraph](https://spidermonkey.dev/blog/2025/10/28/iongraph-web.html) tool first created for use with Spidermonkey.
## Justification
Iongraph's viewer is (as mentioned in the article above) a few notches above graphviz for viewing large CFGs. It also allows easily inspecting different compiler optimization passes and multiple functions in the same browser window. Since Spidermonkey is using this format, it may be beneficial to use it for our own JIT development.
The requirement for JSON is downstream from that of the Iongraph format. As for writing the implementation myself, ZJIT leans towards having fewer dependencies, so this is the preferred approach.
## How does it look?
<img width="902" height="957" alt="image" src="https://github.com/user-attachments/assets/e4e0991b-572a-41fd-9fed-1215bd1926c3" />
<img width="770" height="624" alt="image" src="https://github.com/user-attachments/assets/01398373-1f75-46b8-b1aa-7f5d4cbca6b8" />
Right now, it's aesthetically minimal, but is fairly robust.
## Functionality
Using `--zjit-dump-hir-iongraph` will dump all compiled functions into a directory named `/tmp/zjit-iongraph-{PROCESS_PID}`. Each file will be named `func_{ZJIT_FUNC_NAME}.json`. In order to use them in the Iongraph viewer, you'll need to use `jq` to collate them to a single file. An example invocation of `jq` is shown below for reference. The name of the file created does not matter to my understanding.
`jq --slurp --null-input '.functions=inputs | .version=2' /tmp/zjit-iongraph-{PROCESS_PID}/func*.json > ~/Downloads/foo.json`
From there, you can use https://mozilla-spidermonkey.github.io/iongraph/ to view your trace.
### Caveats
- The upstream Iongraph viewer doesn't allow you to click arguments to an instruction to find the instruction that they originate from when using the format that this PR generates. (I have made a small fork at https://github.com/aidenfoxivey/iongraph that fixes that functionality via https://github.com/aidenfoxivey/iongraph/commit/9e9c29b41c4dbb35cf66cb6161e5b19c8b796379.patch)
- The upstream Iongraph viewer can sometimes show "exiting edges" in the CFG as being not attached to the box representing its basic block.
<img width="1814" height="762" alt="image" src="https://github.com/user-attachments/assets/afbbaa16-332f-498f-849e-11c69a8cb0cc" />
(Image courtesy of @tekknolagi)
This is because the original tool was (to our understanding) written for an SSA format that does not use extended basic blocks. (Extended basic blocks let you put a jump instruction, conditional or otherwise, anywhere in the basic block.) This means that our format may generate more outgoing edges than the viewer is written to handle.
|
|
This implements Shopify#854:
- Splits boot-time and enable-time initialization,
tracks progress with `InitializationState` enum
- Introduces `RubyVM::ZJIT.enable` Ruby method for
enabling the JIT lazily, if not already enabled
- Introduces `--zjit-disable` flag, which can be
used alongside the other `--zjit-*` flags but
prevents enabling the JIT at boot time
- Adds ZJIT infra to support JIT hooks, but this
is not currently exercised (Shopify/ruby#667)
Left for future enhancements:
- Support kwargs for overriding the CLI flags in
`RubyVM::ZJIT.enable`
Closes Shopify#854
|
|
ZJIT: Fix --zjit-mem-size and resurrect --zjit-exec-mem-size
|
|
so that it can be easily specified with `--zjit-dump-lir=`.
|
|
|
|
|
|
|
|
|
|
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
|
|
|
|
When we investigate guard failure issues, we sometimes need to use
profile num around 100k (e.g. `lobsters` in ruby-bench).
This change is to allow that.
|
|
|
|
Add side exit tracing functionality for ZJIT
|
|
* ZJIT: Allow testing JIT code on zjit-test
* Resurrect TestingAllocator tests
|
|
* Using the shared jit crate, support for a single global_allocator can
function
* Solves --zjit-mem-size
|
|
Similar to YJIT's --yjit-stats=quiet, this option allows ZJIT to collect
statistics and make them available via the Ruby API without printing them
at exit. This is useful for programmatic access to stats without the
output noise.
- Added print_stats field to Options struct
- Modified option parsing to support --zjit-stats=quiet
- Added rb_zjit_print_stats_p primitive to check if stats should be printed
- Updated zjit.rb to only register at_exit handler when print_stats is true
- Update the help text shown by `ruby --help` to indicate that
--zjit-stats now accepts an optional =quiet parameter.
- Added test for --zjit-stats=quiet option
|
|
|
|
|
|
|
|
|
|
|
|
Specialize monomorphic `GetIvar` into:
* `GuardType(HeapObject)`
* `GuardShape`
* `LoadIvarEmbedded` or `LoadIvarExtended`
This requires profiling self for `getinstancevariable` (it's not on the operand
stack).
This also optimizes `GetIvar`s that happen as a result of inlining
`attr_reader` and `attr_accessor`.
Also move some (newly) shared JIT helpers into jit.c.
|
|
|
|
This fixes issues related to the process changing directory and not
having only a relative path.
Thanks, Alan.
|
|
Also add a check in the bisect script that can assign blame to the HIR
optimizer.
|
|
* ZJIT: Add --zjit-exec-mem-size
* Add a comment about the limit
|
|
This is moderately useful just in stdout (copy and paste into a renderer) but potentially more useful alongside a tool that parses stdout looking for `digraph G { ... }` and renders those automatically.
|
|
|
|
Add support for `--zjit-allowed-iseqs=SomeFile` and
`--zjit-log-compiled-iseqs=SomeFile` so we can restrict and inspect
which ISEQs get compiled.
Then add `jit_bisect.rb` which we can run to try and narrow a failing
script. For example:
plum% ../tool/zjit_bisect.rb ../build-dev/miniruby "test.rb"
I, [2025-07-29T12:41:18.657177 #96899] INFO -- : Starting with JIT list of 4 items.
I, [2025-07-29T12:41:18.657229 #96899] INFO -- : Verifying items
I, [2025-07-29T12:41:18.726213 #96899] INFO -- : step fixed[0] and items[4]
I, [2025-07-29T12:41:18.726246 #96899] INFO -- : 4 candidates
I, [2025-07-29T12:41:18.797212 #96899] INFO -- : 2 candidates
Reduced JIT list:
bar@test.rb:8
plum%
We start with 4 compiled functions and shrink to just one.
|
|
|
|
This lets us ZJIT compiled functions show up in the profiles of, say,
perf, or samply.
Fix https://github.com/Shopify/ruby/issues/634
|
|
* ZJIT: Profile each instruction at most num_profiles times
* Use saturating_add for num_profiles
|
|
Notes:
Merged-By: k0kubun <takashikkbn@gmail.com>
|
|
Now we can dump HIR, optimized HIR, LIR, and assembly.
Notes:
Merged-By: k0kubun <takashikkbn@gmail.com>
|
|
* Add --zjit-profile-interval option
* Fix min to max
* Avoid rewriting instructions for --zjit-call-threshold=1
* Rename the option to --zjit-num-profiles
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
(https://github.com/Shopify/zjit/pull/96)
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
* Remove redundant statements
* Remove .clone() since A64 implements Copy
* Remove .clone() since InsnId implements Copy
.
* Dereference since *const rb_call_data implements Copy
* Remove unnecessary return statement
* Remove unnecessary braces
* Use .is_empty() over length checks
* Remove unnecessary conversion handling
Since i32 can always fit into i64 (the inner type in Opnd::Imm), the conversion is infallibile.
* Use slice notation in lieu of Vec
https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
* Simplify match statement
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
(https://github.com/Shopify/zjit/pull/30)
* Implement FixnumAdd and stub PatchPoint/GuardType
Co-authored-by: Max Bernstein <max.bernstein@shopify.com>
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
* Clone Target for arm64
* Use $create instead of use create
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
* Fix misindentation from suggested changes
* Drop an unneeded variable for mut
* Load operand into a register only if necessary
---------
Co-authored-by: Max Bernstein <max.bernstein@shopify.com>
Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com>
Co-authored-by: Alan Wu <XrXr@users.noreply.github.com>
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
(https://github.com/Shopify/zjit/pull/16)
* Add zjit_* instructions to profile the interpreter
* Rename FixnumPlus to FixnumAdd
* Update a comment about Invalidate
* Rename Guard to GuardType
* Rename Invalidate to PatchPoint
* Drop unneeded debug!()
* Plan on profiling the types
* Use the output of GuardType as type refined outputs
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
(https://github.com/Shopify/zjit/pull/17)
* Rename --zjit-dump-ssa to --zjit-dump-hir
* Update comments
* Add a comment on iseq_to_hir
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
As a preparation for introducing a profiling layer, we need to be able
to raise the threshold to run a few cycles for profiling.
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
to print debug messages
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|
|
Notes:
Merged: https://github.com/ruby/ruby/pull/13131
|