Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/rust-warnings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Surface Rust warnings on PRs that touch any Rust code.
# Not a required check so we never block people over new warnings
# that might come from a new Rust version being released.
name: Rust warnings
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- '**.rs'
- '!**.inc.rs'
merge_group:

concurrency:
group: ${{ github.workflow }} / ${{ startsWith(github.event_name, 'pull') && github.ref_name || github.sha }}
cancel-in-progress: ${{ startsWith(github.event_name, 'pull') }}

permissions:
contents: read

jobs:
make:
env:
GITPULLOPTIONS: --no-tags origin ${{ github.ref }}

runs-on: ubuntu-24.04

if: >-
${{!(false
|| contains(github.event.head_commit.message, '[DOC]')
|| contains(github.event.head_commit.message, 'Document')
|| contains(github.event.pull_request.title, '[DOC]')
|| contains(github.event.pull_request.title, 'Document')
|| contains(github.event.pull_request.labels.*.name, 'Documentation')
|| (github.event_name == 'push' && github.event.pull_request.user.login == 'dependabot[bot]')
)}}

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Install Rust
run: rustup default beta

- name: Rust warnings
run: |
set -euo pipefail
cargo check --quiet --all-features --message-format=json \
| jq -r 'select(.reason == "compiler-message" and .message.level == "warning") | .message.rendered' \
> warnings.txt
cat warnings.txt
! grep --quiet '[^[:space:]]' warnings.txt
10 changes: 7 additions & 3 deletions .github/workflows/zjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
include:
- test_task: 'zjit-check'
configure: '--enable-yjit=dev --enable-zjit'
rust_version: "1.85.0"

- test_task: 'ruby' # build test for combo build
configure: '--enable-yjit --enable-zjit'
Expand Down Expand Up @@ -81,14 +82,17 @@ jobs:
# Set fetch-depth: 10 so that Launchable can receive commits information.
fetch-depth: 10

- name: Install Rust
if: ${{ matrix.rust_version }}
run: |
rustup install ${{ matrix.rust_version }} --profile minimal
rustup default ${{ matrix.rust_version }}

- uses: taiki-e/install-action@v2
with:
tool: nextest@0.9
if: ${{ matrix.test_task == 'zjit-check' }}

- name: Install Rust # TODO(alan): remove when GitHub images catch up past 1.85.0
run: rustup default 1.85.0

- name: Run configure
run: ../src/configure -C --disable-install-doc ${{ matrix.configure }}

Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/zjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ jobs:

- test_task: 'zjit-check'
configure: '--enable-yjit --enable-zjit=dev'
rust_version: '1.85.0'

- test_task: 'zjit-test-all'
configure: '--enable-zjit=dev'
Expand Down Expand Up @@ -98,7 +99,10 @@ jobs:
fetch-depth: 10

- name: Install Rust
run: rustup default 1.85.0
if: ${{ matrix.rust_version }}
run: |
rustup install ${{ matrix.rust_version }} --profile minimal
rustup default ${{ matrix.rust_version }}

- name: Install rustfmt
if: ${{ matrix.test_task == 'zjit-bindgen' }}
Expand Down
2 changes: 2 additions & 0 deletions .rdoc_options
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ autolink_excluded_words:
- RDoc
- Ruby
- Set
- ZJIT
- YJIT

canonical_root: https://docs.ruby-lang.org/en/master
6 changes: 3 additions & 3 deletions ruby.c
Original file line number Diff line number Diff line change
Expand Up @@ -1819,8 +1819,10 @@ ruby_opt_init(ruby_cmdline_options_t *opt)

if (rb_namespace_available())
rb_initialize_main_namespace();
rb_namespace_init_done();
ruby_init_prelude();

// Initialize JITs after prelude because JITing prelude is typically not optimal.
// Initialize JITs after ruby_init_prelude() because JITing prelude is typically not optimal.
#if USE_YJIT
rb_yjit_init(opt->yjit);
#endif
Expand All @@ -1831,8 +1833,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
}
#endif

rb_namespace_init_done();
ruby_init_prelude();
ruby_set_script_name(opt->script_name);
require_libraries(&opt->req_list);
}
Expand Down
50 changes: 42 additions & 8 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
return unless JITSupport.zjit_supported?

class TestZJIT < Test::Unit::TestCase
def test_enabled
assert_runs 'false', <<~RUBY, zjit: false
RubyVM::ZJIT.enabled?
RUBY
assert_runs 'true', <<~RUBY, zjit: true
RubyVM::ZJIT.enabled?
RUBY
end

def test_call_itself
assert_compiles '42', <<~RUBY, call_threshold: 2
def test = 42.itself
Expand Down Expand Up @@ -1497,6 +1506,22 @@ def test(val) = val.class
}, call_threshold: 2
end

def test_string_concat
assert_compiles '"123"', %q{
def test = "#{1}#{2}#{3}"

test
}, insns: [:concatstrings]
end

def test_string_concat_empty
assert_compiles '""', %q{
def test = "#{}"

test
}, insns: [:concatstrings]
end

private

# Assert that every method call in `test_script` can be compiled by ZJIT
Expand Down Expand Up @@ -1547,14 +1572,23 @@ def assert_runs(expected, test_script, insns: [], assert_compiles: false, **opts
end

# Run a Ruby process with ZJIT options and a pipe for writing test results
def eval_with_jit(script, call_threshold: 1, num_profiles: 1, stats: false, debug: true, timeout: 1000, pipe_fd:)
args = [
"--disable-gems",
"--zjit-call-threshold=#{call_threshold}",
"--zjit-num-profiles=#{num_profiles}",
]
args << "--zjit-stats" if stats
args << "--zjit-debug" if debug
def eval_with_jit(
script,
call_threshold: 1,
num_profiles: 1,
zjit: true,
stats: false,
debug: true,
timeout: 1000,
pipe_fd:
)
args = ["--disable-gems"]
if zjit
args << "--zjit-call-threshold=#{call_threshold}"
args << "--zjit-num-profiles=#{num_profiles}"
args << "--zjit-stats" if stats
args << "--zjit-debug" if debug
end
args << "-e" << script_shell_encode(script)
pipe_r, pipe_w = IO.pipe
# Separate thread so we don't deadlock when
Expand Down
10 changes: 5 additions & 5 deletions yjit.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# frozen_string_literal: true
# :markup: markdown

# This module allows for introspection of \YJIT, CRuby's just-in-time compiler.
# This module allows for introspection of YJIT, CRuby's just-in-time compiler.
# Everything in the module is highly implementation specific and the API might
# be less stable compared to the standard library.
#
# This module may not exist if \YJIT does not support the particular platform
# This module may not exist if YJIT does not support the particular platform
# for which CRuby is built.
module RubyVM::YJIT
# Check if \YJIT is enabled.
# Check if YJIT is enabled.
def self.enabled?
Primitive.cexpr! 'RBOOL(rb_yjit_enabled_p)'
end
Expand All @@ -33,8 +33,8 @@ def self.reset_stats!
Primitive.rb_yjit_reset_stats_bang
end

# Enable \YJIT compilation. `stats` option decides whether to enable \YJIT stats or not. `log` decides
# whether to enable \YJIT compilation logging or not. Optional `mem_size` and `call_threshold` can be
# Enable YJIT compilation. `stats` option decides whether to enable YJIT stats or not. `log` decides
# whether to enable YJIT compilation logging or not. Optional `mem_size` and `call_threshold` can be
# provided to override default configuration.
#
# * `stats`:
Expand Down
4 changes: 2 additions & 2 deletions yjit/src/backend/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,13 +528,13 @@ pub enum Insn {
impl Insn {
/// Create an iterator that will yield a non-mutable reference to each
/// operand in turn for this instruction.
pub(super) fn opnd_iter(&self) -> InsnOpndIterator {
pub(super) fn opnd_iter(&self) -> InsnOpndIterator<'_> {
InsnOpndIterator::new(self)
}

/// Create an iterator that will yield a mutable reference to each operand
/// in turn for this instruction.
pub(super) fn opnd_iter_mut(&mut self) -> InsnOpndMutIterator {
pub(super) fn opnd_iter_mut(&mut self) -> InsnOpndMutIterator<'_> {
InsnOpndMutIterator::new(self)
}

Expand Down
33 changes: 30 additions & 3 deletions zjit.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# frozen_string_literal: true

# This module allows for introspection of \ZJIT, CRuby's just-in-time compiler.
# This module allows for introspection of ZJIT, CRuby's just-in-time compiler.
# Everything in the module is highly implementation specific and the API might
# be less stable compared to the standard library.
#
# This module may not exist if \ZJIT does not support the particular platform
# This module may not exist if ZJIT does not support the particular platform
# for which CRuby is built.
module RubyVM::ZJIT
# Avoid calling a Ruby method here to avoid interfering with compilation tests
Expand All @@ -14,9 +14,15 @@ module RubyVM::ZJIT
end

class << RubyVM::ZJIT
# Check if ZJIT is enabled
def enabled?
Primitive.cexpr! 'RBOOL(rb_zjit_enabled_p)'
end

# Return ZJIT statistics as a Hash
def stats
stats = Primitive.rb_zjit_stats
return nil if stats.nil?

if stats.key?(:vm_insns_count) && stats.key?(:zjit_insns_count)
stats[:total_insns_count] = stats[:vm_insns_count] + stats[:zjit_insns_count]
Expand All @@ -32,15 +38,29 @@ def stats_string
stats = self.stats

[
:compile_time_ns,
:profile_time_ns,
:gc_time_ns,
:invalidation_time_ns,
:total_insns_count,
:vm_insns_count,
:zjit_insns_count,
:ratio_in_zjit,
].each do |key|
# Some stats like vm_insns_count and ratio_in_zjit are not supported on the release build
next unless stats.key?(key)
value = stats[key]
if key == :ratio_in_zjit

case key
when :ratio_in_zjit
value = '%0.1f%%' % value
when /_time_ns\z/
key = key.to_s.sub(/_time_ns\z/, '_time')
value = "#{number_with_delimiter(value / 10**6)}ms"
else
value = number_with_delimiter(value)
end

buf << "#{'%-18s' % "#{key}:"} #{value}\n"
end
buf
Expand All @@ -54,6 +74,13 @@ def assert_compiles # :nodoc:
# :stopdoc:
private

def number_with_delimiter(number)
s = number.to_s
i = s.index('.') || s.size
s.insert(i -= 3, ',') while i > 3
s
end

# Print ZJIT stats
def print_stats
$stderr.write stats_string
Expand Down
10 changes: 8 additions & 2 deletions zjit/src/backend/lir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,13 +549,13 @@ pub enum Insn {
impl Insn {
/// Create an iterator that will yield a non-mutable reference to each
/// operand in turn for this instruction.
pub(super) fn opnd_iter(&self) -> InsnOpndIterator {
pub(super) fn opnd_iter(&self) -> InsnOpndIterator<'_> {
InsnOpndIterator::new(self)
}

/// Create an iterator that will yield a mutable reference to each operand
/// in turn for this instruction.
pub(super) fn opnd_iter_mut(&mut self) -> InsnOpndMutIterator {
pub(super) fn opnd_iter_mut(&mut self) -> InsnOpndMutIterator<'_> {
InsnOpndMutIterator::new(self)
}

Expand Down Expand Up @@ -2233,6 +2233,12 @@ impl Assembler {
out
}

pub fn sub_into(&mut self, left: Opnd, right: Opnd) -> Opnd {
let out = self.sub(left, right);
self.mov(left, out);
out
}

#[must_use]
pub fn mul(&mut self, left: Opnd, right: Opnd) -> Opnd {
let out = self.new_vreg(Opnd::match_num_bits(&[left, right]));
Expand Down
Loading