summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/auto_request_review.yml1
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock89
-rw-r--r--Cargo.toml51
-rw-r--r--common.mk13
-rw-r--r--configure.ac97
-rw-r--r--defs/gmake.mk1
-rw-r--r--defs/jit.mk53
-rw-r--r--gc/mmtk/Cargo.toml2
-rw-r--r--jit.rs4
-rw-r--r--template/Makefile.in9
-rw-r--r--vm.c4
-rw-r--r--yjit/Cargo.toml27
-rw-r--r--yjit/bindgen/Cargo.toml2
-rw-r--r--yjit/yjit.mk57
-rw-r--r--zjit.c1
-rw-r--r--zjit/Cargo.toml17
-rw-r--r--zjit/bindgen/Cargo.toml2
-rw-r--r--zjit/build.rs3
-rw-r--r--zjit/zjit.mk58
20 files changed, 297 insertions, 197 deletions
diff --git a/.github/auto_request_review.yml b/.github/auto_request_review.yml
index 113db986c3..c4c94681f0 100644
--- a/.github/auto_request_review.yml
+++ b/.github/auto_request_review.yml
@@ -10,6 +10,7 @@ files:
'zjit/src/cruby_bindings.inc.rs': []
'doc/zjit*': [team:jit]
'test/ruby/test_zjit*': [team:jit]
+ 'defs/jit.mk': [team:jit]
options:
ignore_draft: true
# This currently doesn't work as intended. We want to skip reviews when only
diff --git a/.gitignore b/.gitignore
index 0f3574115b..3e8d3310f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -246,6 +246,9 @@ lcov*.info
/yjit-bench
/yjit_exit_locations.dump
+# Rust
+/target
+
# /wasm/
/wasm/tests/*.wasm
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000000..65131406d3
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,89 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "capstone"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "015ef5d5ca1743e3f94af9509ba6bd2886523cfee46e48d15c2ef5216fd4ac9a"
+dependencies = [
+ "capstone-sys",
+ "libc",
+]
+
+[[package]]
+name = "capstone-sys"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "dissimilar"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
+
+[[package]]
+name = "expect-test"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63af43ff4431e848fb47472a920f14fa71c24de13255a5692e93d4e90302acb0"
+dependencies = [
+ "dissimilar",
+ "once_cell",
+]
+
+[[package]]
+name = "jit"
+version = "0.0.0"
+dependencies = [
+ "yjit",
+ "zjit",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "yjit"
+version = "0.1.0"
+dependencies = [
+ "capstone",
+]
+
+[[package]]
+name = "zjit"
+version = "0.0.1"
+dependencies = [
+ "capstone",
+ "expect-test",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000000..48ce497497
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,51 @@
+# Using Cargo's workspace feature to build all the Rust code in
+# into a single package.
+# TODO(alan) notes about rust version requirements. Undecided yet.
+
+[workspace]
+members = ["zjit", "yjit"]
+
+[package]
+name = "jit"
+version = "0.0.0"
+edition = "2024"
+rust-version = "1.85.0"
+publish = false # Don't publish to crates.io
+
+[dependencies]
+yjit = { path = "yjit", optional = true }
+zjit = { path = "zjit", optional = true }
+
+[lib]
+crate-type = ["staticlib"]
+path = "jit.rs"
+
+[features]
+disasm = []
+runtime_checks = []
+yjit = [ "dep:yjit" ]
+zjit = [ "dep:zjit" ]
+
+[profile.dev]
+opt-level = 0
+debug = true
+debug-assertions = true
+overflow-checks = true
+
+[profile.dev_nodebug]
+inherits = "dev"
+
+[profile.stats]
+inherits = "release"
+
+[profile.release]
+# NOTE: --enable-yjit and zjit builds use `rustc` without going through Cargo. You
+# might want to update the `rustc` invocation if you change this profile.
+opt-level = 3
+# The extra robustness that comes from checking for arithmetic overflow is
+# worth the performance cost for the compiler.
+overflow-checks = true
+# Generate debug info
+debug = true
+# Use ThinLTO. Much smaller output for a small amount of build time increase.
+lto = "thin"
diff --git a/common.mk b/common.mk
index 7bf6d28b53..1eaeb31d04 100644
--- a/common.mk
+++ b/common.mk
@@ -187,10 +187,9 @@ COMMONOBJS = array.$(OBJEXT) \
weakmap.$(OBJEXT) \
$(PRISM_FILES) \
$(YJIT_OBJ) \
- $(YJIT_LIBOBJ) \
$(ZJIT_OBJ) \
- $(ZJIT_LIBOBJ) \
$(JIT_OBJ) \
+ $(RUST_LIBOBJ) \
$(COROUTINE_OBJ) \
$(DTRACE_OBJ) \
$(BUILTIN_ENCOBJS) \
@@ -346,7 +345,7 @@ YJIT_RUSTC_ARGS = --crate-name=yjit \
-C opt-level=3 \
-C overflow-checks=on \
'--out-dir=$(CARGO_TARGET_DIR)/release/' \
- $(top_srcdir)/yjit/src/lib.rs
+ '$(top_srcdir)/yjit/src/lib.rs'
ZJIT_RUSTC_ARGS = --crate-name=zjit \
--crate-type=staticlib \
@@ -355,8 +354,8 @@ ZJIT_RUSTC_ARGS = --crate-name=zjit \
-C lto=thin \
-C opt-level=3 \
-C overflow-checks=on \
- '--out-dir=$(ZJIT_CARGO_TARGET_DIR)/release/' \
- $(top_srcdir)/zjit/src/lib.rs
+ '--out-dir=$(CARGO_TARGET_DIR)/release/' \
+ '$(top_srcdir)/zjit/src/lib.rs'
all: $(SHOWFLAGS) main
@@ -736,8 +735,8 @@ clean-local:: clean-runnable
$(Q)$(RM) probes.h probes.$(OBJEXT) probes.stamp ruby-glommed.$(OBJEXT) ruby.imp ChangeLog $(STATIC_RUBY)$(EXEEXT)
$(Q)$(RM) GNUmakefile.old Makefile.old $(arch)-fake.rb bisect.sh $(ENC_TRANS_D) builtin_binary.inc
$(Q)$(RM) $(PRISM_BUILD_DIR)/.time $(PRISM_BUILD_DIR)/*/.time yjit_exit_locations.dump
- -$(Q)$(RMALL) yjit/target
- -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine yjit \
+ -$(Q)$(RMALL) target
+ -$(Q) $(RMDIR) enc/jis enc/trans enc $(COROUTINE_H:/Context.h=) coroutine target \
$(PRISM_BUILD_DIR)/*/ $(PRISM_BUILD_DIR) tmp \
2> $(NULL) || $(NULLCMD)
diff --git a/configure.ac b/configure.ac
index 3c01b51239..3b4a031dd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3924,46 +3924,33 @@ AC_ARG_ENABLE(yjit,
CARGO=
CARGO_BUILD_ARGS=
YJIT_LIBS=
+JIT_CARGO_SUPPORT=no
AS_CASE(["${YJIT_SUPPORT}"],
[yes|dev|stats|dev_nodebug], [
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
- AS_IF([test x"$ZJIT_SUPPORT" != "xno"],
- AC_MSG_ERROR([YJIT cannot be enabled when ZJIT is enabled])
- )
AS_CASE(["${YJIT_SUPPORT}"],
[yes], [
- rb_rust_target_subdir=release
],
[dev], [
- rb_rust_target_subdir=debug
- CARGO_BUILD_ARGS='--features disasm,runtime_checks'
+ rb_cargo_features='disasm,runtime_checks'
+ JIT_CARGO_SUPPORT=dev
AC_DEFINE(RUBY_DEBUG, 1)
],
[dev_nodebug], [
- rb_rust_target_subdir=dev_nodebug
- CARGO_BUILD_ARGS='--profile dev_nodebug --features disasm'
+ rb_cargo_features='disasm'
+ JIT_CARGO_SUPPORT=dev_nodebug
AC_DEFINE(YJIT_STATS, 1)
],
[stats], [
- rb_rust_target_subdir=stats
- CARGO_BUILD_ARGS='--profile stats'
+ JIT_CARGO_SUPPORT=stats
AC_DEFINE(YJIT_STATS, 1)
])
- AS_IF([test -n "${CARGO_BUILD_ARGS}"], [
- AC_CHECK_TOOL(CARGO, [cargo], [no])
- AS_IF([test x"$CARGO" = "xno"],
- AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])
- ]))
-
- YJIT_LIBS="yjit/target/${rb_rust_target_subdir}/libyjit.a"
- AS_CASE(["$target_os"],[openbsd*],[
- # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
- LDFLAGS="$LDFLAGS -lpthread -lc++abi"
- ])
+ YJIT_LIBS="target/release/libyjit.a"
+ RUST_LIB='$(YJIT_LIBS)'
YJIT_OBJ='yjit.$(OBJEXT)'
JIT_OBJ='jit.$(OBJEXT)'
AS_IF([test x"$YJIT_SUPPORT" != "xyes" ], [
@@ -3974,38 +3961,23 @@ AS_CASE(["${YJIT_SUPPORT}"],
AC_DEFINE(USE_YJIT, 0)
])
-ZJIT_CARGO_BUILD_ARGS=
ZJIT_LIBS=
AS_CASE(["${ZJIT_SUPPORT}"],
[yes|dev], [
AS_IF([test x"$RUSTC" = "xno"],
AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install])
)
- AS_IF([test x"$YJIT_SUPPORT" != "xno"],
- AC_MSG_ERROR([ZJIT cannot be enabled when YJIT is enabled])
- )
AS_CASE(["${ZJIT_SUPPORT}"],
[yes], [
- rb_rust_target_subdir=release
],
[dev], [
- rb_rust_target_subdir=debug
- ZJIT_CARGO_BUILD_ARGS='--profile dev --features disasm'
+ JIT_CARGO_SUPPORT=dev
AC_DEFINE(RUBY_DEBUG, 1)
])
- AS_IF([test -n "${ZJIT_CARGO_BUILD_ARGS}"], [
- AC_CHECK_TOOL(CARGO, [cargo], [no])
- AS_IF([test x"$CARGO" = "xno"],
- AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install])
- ]))
-
- ZJIT_LIBS="zjit/target/${rb_rust_target_subdir}/libzjit.a"
- AS_CASE(["$target_os"],[openbsd*],[
- # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by yjit
- LDFLAGS="$LDFLAGS -lpthread -lc++abi"
- ])
+ ZJIT_LIBS="target/release/libzjit.a"
+ RUST_LIB='$(ZJIT_LIBS)'
ZJIT_OBJ='zjit.$(OBJEXT)'
JIT_OBJ='jit.$(OBJEXT)'
AS_IF([test x"$ZJIT_SUPPORT" != "xyes" ], [
@@ -4016,18 +3988,57 @@ AS_CASE(["${ZJIT_SUPPORT}"],
AC_DEFINE(USE_ZJIT, 0)
])
+# if YJIT+ZJIT release build, or any build that requires Cargo
+AS_IF([test x"$JIT_CARGO_SUPPORT" != "xno" -o \( x"$YJIT_SUPPORT" != "xno" -a x"$ZJIT_SUPPORT" != "xno" \)], [
+ AC_CHECK_TOOL(CARGO, [cargo], [no])
+ AS_IF([test x"$CARGO" = "xno"],
+ AC_MSG_ERROR([cargo is required. Installation instructions available at https://www.rust-lang.org/tools/install]))
+
+ YJIT_LIBS=
+ ZJIT_LIBS=
+
+ AS_IF([test x"${YJIT_SUPPORT}" != x"no"], [
+ rb_cargo_features="$rb_cargo_features,yjit"
+ ])
+ AS_IF([test x"${ZJIT_SUPPORT}" != x"no"], [
+ rb_cargo_features="$rb_cargo_features,zjit"
+ ])
+ # if YJIT and ZJIT release mode
+ AS_IF([test "${YJIT_SUPPORT}:${ZJIT_SUPPORT}" = "yes:yes"], [
+ JIT_CARGO_SUPPORT=release
+ ])
+ CARGO_BUILD_ARGS="--profile ${JIT_CARGO_SUPPORT} --features ${rb_cargo_features}"
+ AS_IF([test "${JIT_CARGO_SUPPORT}" = "dev"], [
+ RUST_LIB="target/debug/libjit.a"
+ ], [
+ RUST_LIB="target/${JIT_CARGO_SUPPORT}/libjit.a"
+ ])
+])
+
+# In case either we're linking rust code
+AS_IF([test -n "$RUST_LIB"], [
+ AS_CASE(["$target_os"],[openbsd*],[
+ # Link libc++abi (which requires libpthread) for _Unwind_* functions needed by rust stdlib
+ LDFLAGS="$LDFLAGS -lpthread -lc++abi"
+ ])
+
+ # absolute path to stop the "target" dir in src dir from interfering through VPATH
+ RUST_LIB="$(pwd)/${RUST_LIB}"
+])
+
dnl These variables end up in ::RbConfig::CONFIG
-AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
AC_SUBST(RUSTC)dnl Rust compiler command
AC_SUBST(CARGO)dnl Cargo command for Rust builds
AC_SUBST(CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
-AC_SUBST(ZJIT_CARGO_BUILD_ARGS)dnl for selecting Rust build profiles
-AC_SUBST(YJIT_LIBS)dnl for optionally building the Rust parts of YJIT
+AC_SUBST(YJIT_SUPPORT)dnl what flavor of YJIT the Ruby build includes
+AC_SUBST(YJIT_LIBS)dnl the .a library of YJIT
AC_SUBST(YJIT_OBJ)dnl for optionally building the C parts of YJIT
AC_SUBST(ZJIT_SUPPORT)dnl what flavor of ZJIT the Ruby build includes
-AC_SUBST(ZJIT_LIBS)dnl for optionally building the Rust parts of ZJIT
+AC_SUBST(ZJIT_LIBS)dnl path to the .a library of ZJIT
AC_SUBST(ZJIT_OBJ)dnl for optionally building the C parts of ZJIT
AC_SUBST(JIT_OBJ)dnl for optionally building C glue code for Rust FFI
+AC_SUBST(RUST_LIB)dnl path to the rust .a library that contains either or both JITs
+AC_SUBST(JIT_CARGO_SUPPORT)dnl "no" or the cargo profile of the rust code
}
[begin]_group "build section" && {
diff --git a/defs/gmake.mk b/defs/gmake.mk
index 6e696c4631..87fc8021b2 100644
--- a/defs/gmake.mk
+++ b/defs/gmake.mk
@@ -443,6 +443,7 @@ endif
include $(top_srcdir)/yjit/yjit.mk
include $(top_srcdir)/zjit/zjit.mk
+include $(top_srcdir)/defs/jit.mk
# Query on the generated rdoc
#
diff --git a/defs/jit.mk b/defs/jit.mk
new file mode 100644
index 0000000000..84f429ffcb
--- /dev/null
+++ b/defs/jit.mk
@@ -0,0 +1,53 @@
+# Make recipes that deal with the rust code of YJIT and ZJIT.
+
+# Because of Cargo cache, if the actual binary is not changed from the
+# previous build, the mtime is preserved as the cached file.
+# This means the target is not updated actually, and it will need to
+# rebuild at the next build.
+RUST_LIB_TOUCH = touch $@
+
+ifneq ($(JIT_CARGO_SUPPORT),no)
+$(RUST_LIB):
+ $(Q)if [ '$(ZJIT_SUPPORT)' != no -a '$(YJIT_SUPPORT)' != no ]; then \
+ echo 'building YJIT and ZJIT ($(JIT_CARGO_SUPPORT:yes=release) mode)'; \
+ elif [ '$(ZJIT_SUPPORT)' != no ]; then \
+ echo 'building ZJIT ($(JIT_CARGO_SUPPORT) mode)'; \
+ elif [ '$(YJIT_SUPPORT)' != no ]; then \
+ echo 'building YJIT ($(JIT_CARGO_SUPPORT) mode)'; \
+ fi
+ +$(Q)CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
+ CARGO_TERM_PROGRESS_WHEN='never' \
+ $(CARGO) $(CARGO_VERBOSE) build --manifest-path '$(top_srcdir)/Cargo.toml' $(CARGO_BUILD_ARGS)
+ $(RUST_LIB_TOUCH)
+endif
+
+RUST_LIB_SYMBOLS = $(RUST_LIB:.a=).symbols
+$(RUST_LIBOBJ): $(RUST_LIB)
+ $(ECHO) 'partial linking $(RUST_LIB) into $@'
+ifneq ($(findstring darwin,$(target_os)),)
+ $(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(RUST_LIB_SYMBOLS) $(RUST_LIB)
+else
+ $(Q) $(LD) -r -o $@ --whole-archive $(RUST_LIB)
+ -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
+endif
+
+rust-libobj: $(RUST_LIBOBJ)
+rust-lib: $(RUST_LIB)
+
+# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
+# Unfortunately, using wildcard like '_rb_*' with -exported-symbol does not work, at least
+# not on version 820.1. Assume llvm-nm, so XCode 8.0 (from 2016) or newer.
+#
+# The -exported_symbols_list pulls out the right archive members. Symbols not listed
+# in the list are made private extern, which are in turn made local as we're using `ld -r`.
+# Note, section about -keep_private_externs in ld's man page hints at this behavior on which
+# we rely.
+ifneq ($(findstring darwin,$(target_os)),)
+$(RUST_LIB_SYMBOLS): $(RUST_LIB)
+ $(Q) $(tooldir)/darwin-ar $(NM) --defined-only --extern-only $(RUST_LIB) | \
+ sed -n -e 's/.* //' -e '/^$(SYMBOL_PREFIX)rb_/p' \
+ -e '/^$(SYMBOL_PREFIX)rust_eh_personality/p' \
+ > $@
+
+$(RUST_LIBOBJ): $(RUST_LIB_SYMBOLS)
+endif
diff --git a/gc/mmtk/Cargo.toml b/gc/mmtk/Cargo.toml
index 66af77fe61..c3f46aa046 100644
--- a/gc/mmtk/Cargo.toml
+++ b/gc/mmtk/Cargo.toml
@@ -35,3 +35,5 @@ default = []
# When moving an object, clear its original copy.
clear_old_copy = []
+
+[workspace]
diff --git a/jit.rs b/jit.rs
new file mode 100644
index 0000000000..b66b2d21ca
--- /dev/null
+++ b/jit.rs
@@ -0,0 +1,4 @@
+#[cfg(feature = "yjit")]
+pub use yjit::*;
+#[cfg(feature = "zjit")]
+pub use zjit::*;
diff --git a/template/Makefile.in b/template/Makefile.in
index aed81bf1ef..96c8d8031b 100644
--- a/template/Makefile.in
+++ b/template/Makefile.in
@@ -107,15 +107,14 @@ JIT_OBJ=@JIT_OBJ@
YJIT_SUPPORT=@YJIT_SUPPORT@
YJIT_LIBS=@YJIT_LIBS@
YJIT_OBJ=@YJIT_OBJ@
-YJIT_LIBOBJ = $(YJIT_LIBS:.a=.@OBJEXT@)
ZJIT_SUPPORT=@ZJIT_SUPPORT@
ZJIT_LIBS=@ZJIT_LIBS@
ZJIT_OBJ=@ZJIT_OBJ@
-ZJIT_LIBOBJ = $(ZJIT_LIBS:.a=.@OBJEXT@)
-CARGO_TARGET_DIR=@abs_top_builddir@/yjit/target
+JIT_CARGO_SUPPORT=@JIT_CARGO_SUPPORT@
+CARGO_TARGET_DIR=@abs_top_builddir@/target
CARGO_BUILD_ARGS=@CARGO_BUILD_ARGS@
-ZJIT_CARGO_BUILD_ARGS=@ZJIT_CARGO_BUILD_ARGS@
-ZJIT_CARGO_TARGET_DIR=@abs_top_builddir@/zjit/target
+RUST_LIB=@RUST_LIB@
+RUST_LIBOBJ = $(RUST_LIB:.a=.@OBJEXT@)
LDFLAGS = @STATIC@ $(CFLAGS) @LDFLAGS@
EXE_LDFLAGS = $(LDFLAGS)
EXTLDFLAGS = @EXTLDFLAGS@
diff --git a/vm.c b/vm.c
index 6613218ee7..5d5b44ebeb 100644
--- a/vm.c
+++ b/vm.c
@@ -452,7 +452,9 @@ jit_compile(rb_execution_context_t *ec)
rb_zjit_compile_iseq(iseq, ec, false);
}
}
-#elif USE_YJIT
+#endif
+
+#if USE_YJIT
// Increment the ISEQ's call counter and trigger JIT compilation if not compiled
if (body->jit_entry == NULL && rb_yjit_enabled_p) {
body->jit_entry_calls++;
diff --git a/yjit/Cargo.toml b/yjit/Cargo.toml
index dd5b853e41..ad7dd35ecf 100644
--- a/yjit/Cargo.toml
+++ b/yjit/Cargo.toml
@@ -9,9 +9,6 @@ edition = "2021" # Rust 2021 edition to compile with
rust-version = "1.58.0" # Minimally supported rust version
publish = false # Don't publish to crates.io
-[lib]
-crate-type = ["staticlib"]
-
[dependencies]
# No required dependencies to simplify build process. TODO: Link to yet to be
# written rationale. Optional For development and testing purposes
@@ -27,27 +24,3 @@ disasm = ["capstone"]
# from cfg!(debug_assertions) so that we can see disasm of the code
# that would run in the release mode.
runtime_checks = []
-
-[profile.dev]
-opt-level = 0
-debug = true
-debug-assertions = true
-overflow-checks = true
-
-[profile.dev_nodebug]
-inherits = "dev"
-
-[profile.stats]
-inherits = "release"
-
-[profile.release]
-# NOTE: --enable-yjit builds use `rustc` without going through Cargo. You
-# might want to update the `rustc` invocation if you change this profile.
-opt-level = 3
-# The extra robustness that comes from checking for arithmetic overflow is
-# worth the performance cost for the compiler.
-overflow-checks = true
-# Generate debug info
-debug = true
-# Use ThinLTO. Much smaller output for a small amount of build time increase.
-lto = "thin"
diff --git a/yjit/bindgen/Cargo.toml b/yjit/bindgen/Cargo.toml
index 8c1b533006..ba695e0ce6 100644
--- a/yjit/bindgen/Cargo.toml
+++ b/yjit/bindgen/Cargo.toml
@@ -8,3 +8,5 @@ edition = "2021"
[dependencies]
bindgen = "0.70.1"
env_logger = "0.11.5"
+
+[workspace]
diff --git a/yjit/yjit.mk b/yjit/yjit.mk
index 90f14568da..98e4ed3196 100644
--- a/yjit/yjit.mk
+++ b/yjit/yjit.mk
@@ -19,60 +19,21 @@ YJIT_SRC_FILES = $(wildcard \
# rebuild at the next build.
YJIT_LIB_TOUCH = touch $@
+# Absolute path to match RUST_LIB rules to avoid picking
+# the "target" dir in the source directory through VPATH.
+BUILD_YJIT_LIBS = $(TOP_BUILD_DIR)/$(YJIT_LIBS)
+
# YJIT_SUPPORT=yes when `configure` gets `--enable-yjit`
ifeq ($(YJIT_SUPPORT),yes)
-$(YJIT_LIBS): $(YJIT_SRC_FILES)
+yjit-libs: $(BUILD_YJIT_LIBS)
+$(BUILD_YJIT_LIBS): $(YJIT_SRC_FILES)
$(ECHO) 'building Rust YJIT (release mode)'
+$(Q) $(RUSTC) $(YJIT_RUSTC_ARGS)
$(YJIT_LIB_TOUCH)
-else ifeq ($(YJIT_SUPPORT),no)
-$(YJIT_LIBS):
- $(ECHO) 'Error: Tried to build YJIT without configuring it first. Check `make showconfig`?'
- @false
-else ifeq ($(YJIT_SUPPORT),$(filter dev dev_nodebug stats,$(YJIT_SUPPORT)))
-# NOTE: MACOSX_DEPLOYMENT_TARGET to match `rustc --print deployment-target` to avoid the warning below.
-# ld: warning: object file (yjit/target/debug/libyjit.a(<libcapstone object>)) was built for
-# newer macOS version (15.2) than being linked (15.0)
-# We don't use newer macOS feature as of yet.
-$(YJIT_LIBS): $(YJIT_SRC_FILES)
- $(ECHO) 'building Rust YJIT ($(YJIT_SUPPORT) mode)'
- +$(Q)$(CHDIR) $(top_srcdir)/yjit && \
- CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
- CARGO_TERM_PROGRESS_WHEN='never' \
- MACOSX_DEPLOYMENT_TARGET=11.0 \
- $(CARGO) $(CARGO_VERBOSE) build $(CARGO_BUILD_ARGS)
- $(YJIT_LIB_TOUCH)
-else
-endif
-
-yjit-libobj: $(YJIT_LIBOBJ)
-
-YJIT_LIB_SYMBOLS = $(YJIT_LIBS:.a=).symbols
-$(YJIT_LIBOBJ): $(YJIT_LIBS)
- $(ECHO) 'partial linking $(YJIT_LIBS) into $@'
-ifneq ($(findstring darwin,$(target_os)),)
- $(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(YJIT_LIB_SYMBOLS) $(YJIT_LIBS)
-else
- $(Q) $(LD) -r -o $@ --whole-archive $(YJIT_LIBS)
- -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
endif
-# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
-# Unfortunately, using wildcard like '_rb_*' with -exported-symbol does not work, at least
-# not on version 820.1. Assume llvm-nm, so XCode 8.0 (from 2016) or newer.
-#
-# The -exported_symbols_list pulls out the right archive members. Symbols not listed
-# in the list are made private extern, which are in turn made local as we're using `ld -r`.
-# Note, section about -keep_private_externs in ld's man page hints at this behavior on which
-# we rely.
-ifneq ($(findstring darwin,$(target_os)),)
-$(YJIT_LIB_SYMBOLS): $(YJIT_LIBS)
- $(Q) $(tooldir)/darwin-ar $(NM) --defined-only --extern-only $(YJIT_LIBS) | \
- sed -n -e 's/.* //' -e '/^$(SYMBOL_PREFIX)rb_/p' \
- -e '/^$(SYMBOL_PREFIX)rust_eh_personality/p' \
- > $@
-
-$(YJIT_LIBOBJ): $(YJIT_LIB_SYMBOLS)
+ifneq ($(YJIT_SUPPORT),no)
+$(RUST_LIB): $(YJIT_SRC_FILES)
endif
# By using YJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install`
@@ -94,7 +55,7 @@ RUST_VERSION = +1.58.0
.PHONY: yjit-smoke-test
yjit-smoke-test:
ifneq ($(strip $(CARGO)),)
- $(CARGO) $(RUST_VERSION) test --all-features -q --manifest-path='$(top_srcdir)/yjit/Cargo.toml'
+ $(CARGO) test --all-features -q --manifest-path='$(top_srcdir)/yjit/Cargo.toml'
endif
$(MAKE) btest RUN_OPTS='--yjit-call-threshold=1' BTESTS=-j
$(MAKE) test-all TESTS='$(top_srcdir)/test/ruby/test_yjit.rb'
diff --git a/zjit.c b/zjit.c
index 620b9d6af3..b993006e58 100644
--- a/zjit.c
+++ b/zjit.c
@@ -9,6 +9,7 @@
#include "internal/numeric.h"
#include "internal/gc.h"
#include "internal/vm.h"
+#include "yjit.h"
#include "vm_core.h"
#include "vm_callinfo.h"
#include "builtin.h"
diff --git a/zjit/Cargo.toml b/zjit/Cargo.toml
index ed8e66be02..a86117d6e2 100644
--- a/zjit/Cargo.toml
+++ b/zjit/Cargo.toml
@@ -1,25 +1,10 @@
[package]
name = "zjit"
version = "0.0.1"
-edition = "2024" # Rust 2021 edition to compile with
+edition = "2024"
rust-version = "1.85.0" # Minimally supported rust version
publish = false # Don't publish to crates.io
-[lib]
-crate-type = ["staticlib"]
-
-[profile.release]
-# NOTE: --enable-zjit builds use `rustc` without going through Cargo. You
-# might want to update the `rustc` invocation if you change this profile.
-opt-level = 3
-# The extra robustness that comes from checking for arithmetic overflow is
-# worth the performance cost for the compiler.
-overflow-checks = true
-# Generate debug info
-debug = true
-# Use ThinLTO. Much smaller output for a small amount of build time increase.
-lto = "thin"
-
[dependencies]
# No required dependencies to simplify build process. TODO: Link to yet to be
# written rationale. Optional For development and testing purposes
diff --git a/zjit/bindgen/Cargo.toml b/zjit/bindgen/Cargo.toml
index 2824f31213..2f20f18016 100644
--- a/zjit/bindgen/Cargo.toml
+++ b/zjit/bindgen/Cargo.toml
@@ -8,3 +8,5 @@ edition = "2024"
[dependencies]
bindgen = "0.71.1"
env_logger = "0.11.5"
+
+[workspace]
diff --git a/zjit/build.rs b/zjit/build.rs
index cf402fbc1d..fc6ad1decf 100644
--- a/zjit/build.rs
+++ b/zjit/build.rs
@@ -1,7 +1,8 @@
fn main() {
use std::env;
- if let Ok(ruby_build_dir) = env::var("RUBY_BUILD_DIR") {
+ // option_env! automatically registers a rerun-if-env-changed
+ if let Some(ruby_build_dir) = option_env!("RUBY_BUILD_DIR") {
// Link against libminiruby
println!("cargo:rustc-link-search=native={ruby_build_dir}");
println!("cargo:rustc-link-lib=static:-bundle=miniruby");
diff --git a/zjit/zjit.mk b/zjit/zjit.mk
index 91cf861a39..9107c15809 100644
--- a/zjit/zjit.mk
+++ b/zjit/zjit.mk
@@ -19,60 +19,20 @@ ZJIT_SRC_FILES = $(wildcard \
# rebuild at the next build.
ZJIT_LIB_TOUCH = touch $@
+# Absolute path to match RUST_LIB rules to avoid picking
+# the "target" dir in the source directory through VPATH.
+BUILD_ZJIT_LIBS = $(TOP_BUILD_DIR)/$(ZJIT_LIBS)
+
# ZJIT_SUPPORT=yes when `configure` gets `--enable-zjit`
ifeq ($(ZJIT_SUPPORT),yes)
-$(ZJIT_LIBS): $(ZJIT_SRC_FILES)
+$(BUILD_ZJIT_LIBS): $(ZJIT_SRC_FILES)
$(ECHO) 'building Rust ZJIT (release mode)'
+$(Q) $(RUSTC) $(ZJIT_RUSTC_ARGS)
$(ZJIT_LIB_TOUCH)
-else ifeq ($(ZJIT_SUPPORT),no)
-$(ZJIT_LIBS):
- $(ECHO) 'Error: Tried to build ZJIT without configuring it first. Check `make showconfig`?'
- @false
-else ifeq ($(ZJIT_SUPPORT),$(filter dev dev_nodebug stats,$(ZJIT_SUPPORT)))
-# NOTE: MACOSX_DEPLOYMENT_TARGET to match `rustc --print deployment-target` to avoid the warning below.
-# ld: warning: object file (zjit/target/debug/libzjit.a(<libcapstone object>)) was built for
-# newer macOS version (15.2) than being linked (15.0)
-# We don't use newer macOS feature as of yet.
-$(ZJIT_LIBS): $(ZJIT_SRC_FILES)
- $(ECHO) 'building Rust ZJIT ($(ZJIT_SUPPORT) mode)'
- +$(Q)$(CHDIR) $(top_srcdir)/zjit && \
- CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \
- CARGO_TERM_PROGRESS_WHEN='never' \
- MACOSX_DEPLOYMENT_TARGET=11.0 \
- $(CARGO) $(CARGO_VERBOSE) build $(ZJIT_CARGO_BUILD_ARGS)
- $(ZJIT_LIB_TOUCH)
-else
-endif
-
-zjit-libobj: $(ZJIT_LIBOBJ)
-
-ZJIT_LIB_SYMBOLS = $(ZJIT_LIBS:.a=).symbols
-$(ZJIT_LIBOBJ): $(ZJIT_LIBS)
- $(ECHO) 'partial linking $(ZJIT_LIBS) into $@'
-ifneq ($(findstring darwin,$(target_os)),)
- $(Q) $(CC) -nodefaultlibs -r -o $@ -exported_symbols_list $(ZJIT_LIB_SYMBOLS) $(ZJIT_LIBS)
-else
- $(Q) $(LD) -r -o $@ --whole-archive $(ZJIT_LIBS)
- -$(Q) $(OBJCOPY) --wildcard --keep-global-symbol='$(SYMBOL_PREFIX)rb_*' $(@)
endif
-# For Darwin only: a list of symbols that we want the glommed Rust static lib to export.
-# Unfortunately, using wildcard like '_rb_*' with -exported-symbol does not work, at least
-# not on version 820.1. Assume llvm-nm, so XCode 8.0 (from 2016) or newer.
-#
-# The -exported_symbols_list pulls out the right archive members. Symbols not listed
-# in the list are made private extern, which are in turn made local as we're using `ld -r`.
-# Note, section about -keep_private_externs in ld's man page hints at this behavior on which
-# we rely.
-ifneq ($(findstring darwin,$(target_os)),)
-$(ZJIT_LIB_SYMBOLS): $(ZJIT_LIBS)
- $(Q) $(tooldir)/darwin-ar $(NM) --defined-only --extern-only $(ZJIT_LIBS) | \
- sed -n -e 's/.* //' -e '/^$(SYMBOL_PREFIX)rb_/p' \
- -e '/^$(SYMBOL_PREFIX)rust_eh_personality/p' \
- > $@
-
-$(ZJIT_LIBOBJ): $(ZJIT_LIB_SYMBOLS)
+ifneq ($(ZJIT_SUPPORT),no)
+$(RUST_LIB): $(ZJIT_SRC_FILES)
endif
# By using ZJIT_BENCH_OPTS instead of RUN_OPTS, you can skip passing the options to `make install`
@@ -113,7 +73,7 @@ zjit-bindgen: zjit.$(OBJEXT)
zjit-test: libminiruby.a
RUBY_BUILD_DIR='$(TOP_BUILD_DIR)' \
RUBY_LD_FLAGS='$(LDFLAGS) $(XLDFLAGS) $(MAINLIBS)' \
- CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \
+ CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
$(CARGO) nextest run --manifest-path '$(top_srcdir)/zjit/Cargo.toml' $(ZJIT_TESTS)
# Run a ZJIT test written with Rust #[test] under LLDB
@@ -126,7 +86,7 @@ zjit-test-lldb: libminiruby.a
fi; \
exe_path=`RUBY_BUILD_DIR='$(TOP_BUILD_DIR)' \
RUBY_LD_FLAGS='$(LDFLAGS) $(XLDFLAGS) $(MAINLIBS)' \
- CARGO_TARGET_DIR='$(ZJIT_CARGO_TARGET_DIR)' \
+ CARGO_TARGET_DIR='$(CARGO_TARGET_DIR)' \
$(CARGO) nextest list --manifest-path '$(top_srcdir)/zjit/Cargo.toml' --message-format json --list-type=binaries-only | \
$(BASERUBY) -rjson -e 'puts JSON.load(STDIN.read).dig("rust-binaries", "zjit", "binary-path")'`; \
exec lldb $$exe_path -- --test-threads=1 $(ZJIT_TESTS)