diff options
-rw-r--r-- | .github/auto_request_review.yml | 1 | ||||
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Cargo.lock | 89 | ||||
-rw-r--r-- | Cargo.toml | 51 | ||||
-rw-r--r-- | common.mk | 13 | ||||
-rw-r--r-- | configure.ac | 97 | ||||
-rw-r--r-- | defs/gmake.mk | 1 | ||||
-rw-r--r-- | defs/jit.mk | 53 | ||||
-rw-r--r-- | gc/mmtk/Cargo.toml | 2 | ||||
-rw-r--r-- | jit.rs | 4 | ||||
-rw-r--r-- | template/Makefile.in | 9 | ||||
-rw-r--r-- | vm.c | 4 | ||||
-rw-r--r-- | yjit/Cargo.toml | 27 | ||||
-rw-r--r-- | yjit/bindgen/Cargo.toml | 2 | ||||
-rw-r--r-- | yjit/yjit.mk | 57 | ||||
-rw-r--r-- | zjit.c | 1 | ||||
-rw-r--r-- | zjit/Cargo.toml | 17 | ||||
-rw-r--r-- | zjit/bindgen/Cargo.toml | 2 | ||||
-rw-r--r-- | zjit/build.rs | 3 | ||||
-rw-r--r-- | zjit/zjit.mk | 58 |
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" @@ -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@ @@ -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' @@ -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) |