diff --git a/.github/workflows/parse_y.yml b/.github/workflows/parse_y.yml index 9345a1f7a465fc..c77b4516d1e737 100644 --- a/.github/workflows/parse_y.yml +++ b/.github/workflows/parse_y.yml @@ -78,7 +78,9 @@ jobs: - run: make - - run: make TESTRUN_SCRIPT='-e "exit !RUBY_DESCRIPTION.include?(%[+PRISM])"' run + - run: make TESTRUN_SCRIPT='-renvutil -v -e "exit EnvUtil.current_parser == %[parse.y]"' run + env: + RUNOPT0: -I$(tooldir)/lib - name: make ${{ matrix.test_task }} run: make -s ${{ matrix.test_task }} RUN_OPTS="$RUN_OPTS" SPECOPTS="$SPECOPTS" diff --git a/NEWS.md b/NEWS.md index bc026cdce8c5f1..288f7d97dedf93 100644 --- a/NEWS.md +++ b/NEWS.md @@ -160,7 +160,7 @@ The following default gems are updated. * io-console 0.8.1 * io-nonblock 0.3.2 * io-wait 0.3.2 -* json 2.13.1 +* json 2.13.2 * optparse 0.7.0.dev.2 * prism 1.4.0 * psych 5.2.6 diff --git a/bignum.c b/bignum.c index ac86951372f4d4..2e135caf20fb09 100644 --- a/bignum.c +++ b/bignum.c @@ -6757,6 +6757,73 @@ rb_big_aref(VALUE x, VALUE y) return (xds[s1] & bit) ? INT2FIX(1) : INT2FIX(0); } +VALUE +rb_big_aref2(VALUE x, VALUE beg, VALUE len) +{ + BDIGIT *xds, *vds; + VALUE v; + size_t copy_begin, xn, shift; + ssize_t begin, length, end; + bool negative_add_one; + + beg = rb_to_int(beg); + len = rb_to_int(len); + length = NUM2SSIZET(len); + begin = NUM2SSIZET(beg); + end = NUM2SSIZET(rb_int_plus(beg, len)); + shift = begin < 0 ? -begin : 0; + xn = BIGNUM_LEN(x); + xds = BDIGITS(x); + + if (length < 0) return rb_big_rshift(x, beg); + if (length == 0 || end <= 0) return INT2FIX(0); + if (begin < 0) begin = 0; + + if ((size_t)(end - 1) / BITSPERDIG >= xn) { + /* end > xn * BITSPERDIG */ + end = xn * BITSPERDIG; + } + + if ((size_t)begin / BITSPERDIG < xn) { + /* begin < xn * BITSPERDIG */ + size_t shift_bits, copy_end; + copy_begin = begin / BITSPERDIG; + shift_bits = begin % BITSPERDIG; + copy_end = (end - 1) / BITSPERDIG + 1; + v = bignew(copy_end - copy_begin, 1); + vds = BDIGITS(v); + MEMCPY(vds, xds + copy_begin, BDIGIT, copy_end - copy_begin); + negative_add_one = (vds[0] & ((1 << shift_bits) - 1)) == 0; + v = bignorm(v); + if (shift_bits) v = rb_int_rshift(v, SIZET2NUM(shift_bits)); + } + else { + /* Out of range */ + v = INT2FIX(0); + negative_add_one = false; + copy_begin = begin = end = 0; + } + + if (BIGNUM_NEGATIVE_P(x)) { + size_t mask_size = length - shift; + VALUE mask = rb_int_minus(rb_int_lshift(INT2FIX(1), SIZET2NUM(mask_size)), INT2FIX(1)); + v = rb_int_xor(v, mask); + for (size_t i = 0; negative_add_one && i < copy_begin; i++) { + if (xds[i]) negative_add_one = false; + } + if (negative_add_one) v = rb_int_plus(v, INT2FIX(1)); + v = rb_int_and(v, mask); + } + else { + size_t mask_size = (size_t)end - begin; + VALUE mask = rb_int_minus(rb_int_lshift(INT2FIX(1), SIZET2NUM(mask_size)), INT2FIX(1)); + v = rb_int_and(v, mask); + } + RB_GC_GUARD(x); + if (shift) v = rb_int_lshift(v, SSIZET2NUM(shift)); + return v; +} + VALUE rb_big_hash(VALUE x) { diff --git a/compile.c b/compile.c index 8d5cb45904c6e7..bda18c1c424ab6 100644 --- a/compile.c +++ b/compile.c @@ -1440,6 +1440,30 @@ new_insn_body(rb_iseq_t *iseq, int line_no, int node_id, enum ruby_vminsn_type i return new_insn_core(iseq, line_no, node_id, insn_id, argc, operands); } +static INSN * +insn_replace_with_operands(rb_iseq_t *iseq, INSN *iobj, enum ruby_vminsn_type insn_id, int argc, ...) +{ + VALUE *operands = 0; + va_list argv; + if (argc > 0) { + int i; + va_start(argv, argc); + operands = compile_data_alloc2(iseq, sizeof(VALUE), argc); + for (i = 0; i < argc; i++) { + VALUE v = va_arg(argv, VALUE); + operands[i] = v; + } + va_end(argv); + } + + iobj->insn_id = insn_id; + iobj->operand_size = argc; + iobj->operands = operands; + iseq_insn_each_markable_object(iobj, iseq_insn_each_object_write_barrier, (VALUE)iseq); + + return iobj; +} + static const struct rb_callinfo * new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_callinfo_kwarg *kw_arg, int has_blockiseq) { @@ -3439,11 +3463,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal VALUE ary = iobj->operands[0]; rb_obj_reveal(ary, rb_cArray); - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = ary; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, ary, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3465,11 +3485,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal VALUE hash = iobj->operands[0]; rb_obj_reveal(hash, rb_cHash); - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = hash; - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, hash, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3488,11 +3504,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { - iobj->insn_id = BIN(opt_ary_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cArray_empty_frozen); - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_ary_freeze), 2, rb_cArray_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -3511,11 +3523,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(next, 1); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0 && blockiseq == NULL && vm_ci_mid(ci) == idFreeze) { - iobj->insn_id = BIN(opt_hash_freeze); - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - RB_OBJ_WRITE(iseq, &iobj->operands[0], rb_cHash_empty_frozen); - iobj->operands[1] = (VALUE)ci; + insn_replace_with_operands(iseq, iobj, BIN(opt_hash_freeze), 2, rb_cHash_empty_frozen, (VALUE)ci); ELEM_REMOVE(next); } } @@ -4109,17 +4117,16 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal static int insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) { - iobj->insn_id = insn_id; - iobj->operand_size = insn_len(insn_id) - 1; - iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; - if (insn_id == BIN(opt_neq)) { VALUE original_ci = iobj->operands[0]; - iobj->operand_size = 2; - iobj->operands = compile_data_calloc2(iseq, iobj->operand_size, sizeof(VALUE)); - iobj->operands[0] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); - iobj->operands[1] = original_ci; + VALUE new_ci = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE); + insn_replace_with_operands(iseq, iobj, insn_id, 2, new_ci, original_ci); } + else { + iobj->insn_id = insn_id; + iobj->operand_size = insn_len(insn_id) - 1; + } + iobj->insn_info.events |= RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN; return COMPILE_OK; } @@ -4151,12 +4158,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (method != INT2FIX(0)) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = num; - iobj->operands[1] = method; - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, num, method); ELEM_REMOVE(&niobj->link); return COMPILE_OK; } @@ -4168,12 +4170,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0); if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 1); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK); - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK)); ELEM_REMOVE(&iobj->link); ELEM_REMOVE(niobj->link.next); ELEM_INSERT_NEXT(&niobj->link, &iobj->link); @@ -4191,12 +4188,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (vm_ci_mid(ci) == idPack && vm_ci_argc(ci) == 2 && (kwarg && kwarg->keyword_len == 1 && kwarg->keywords[0] == rb_id2sym(idBuffer))) { VALUE num = iobj->operands[0]; - int operand_len = insn_len(BIN(opt_newarray_send)) - 1; - iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); - iobj->operands[0] = FIXNUM_INC(num, 2); - iobj->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER); - iobj->operand_size = operand_len; + insn_replace_with_operands(iseq, iobj, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 2), INT2FIX(VM_OPT_NEWARRAY_SEND_PACK_BUFFER)); // Remove the "send" insn. ELEM_REMOVE((niobj->link.next)->next); // Remove the modified insn from its original "newarray" position... @@ -4230,11 +4222,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idIncludeP) { VALUE num = iobj->operands[0]; INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_newarray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = FIXNUM_INC(num, 1); - sendins->operands[1] = INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P); + insn_replace_with_operands(iseq, sendins, BIN(opt_newarray_send), 2, FIXNUM_INC(num, 1), INT2FIX(VM_OPT_NEWARRAY_SEND_INCLUDE_P)); // Remove the original "newarray" insn. ELEM_REMOVE(&iobj->link); return COMPILE_OK; @@ -4272,12 +4260,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) rb_obj_reveal(ary, rb_cArray); INSN *sendins = (INSN *)sendobj; - sendins->insn_id = BIN(opt_duparray_send); - sendins->operand_size = insn_len(sendins->insn_id) - 1;; - sendins->operands = compile_data_calloc2(iseq, sendins->operand_size, sizeof(VALUE)); - sendins->operands[0] = ary; - sendins->operands[1] = rb_id2sym(idIncludeP); - sendins->operands[2] = INT2FIX(1); + insn_replace_with_operands(iseq, sendins, BIN(opt_duparray_send), 3, ary, rb_id2sym(idIncludeP), INT2FIX(1)); // Remove the duparray insn. ELEM_REMOVE(&iobj->link); diff --git a/configure.ac b/configure.ac index 6c455b3d67f309..c8018cdabb490a 100644 --- a/configure.ac +++ b/configure.ac @@ -3976,7 +3976,7 @@ AS_CASE(["${YJIT_SUPPORT}"], ZJIT_LIBS= AS_CASE(["${ZJIT_SUPPORT}"], -[yes|dev|dev_nodebug], [ +[yes|dev|dev_nodebug|stats], [ AS_IF([test x"$RUSTC" = "xno"], AC_MSG_ERROR([rustc is required. Installation instructions available at https://www.rust-lang.org/tools/install]) ) @@ -3987,11 +3987,16 @@ AS_CASE(["${ZJIT_SUPPORT}"], [dev], [ rb_cargo_features="$rb_cargo_features,disasm" JIT_CARGO_SUPPORT=dev - AC_DEFINE(RUBY_DEBUG, 1) + AC_DEFINE(RUBY_DEBUG, 1) ], [dev_nodebug], [ rb_cargo_features="$rb_cargo_features,disasm" JIT_CARGO_SUPPORT=dev_nodebug + AC_DEFINE(ZJIT_STATS, 1) + ], + [stats], [ + JIT_CARGO_SUPPORT=stats + AC_DEFINE(ZJIT_STATS, 1) ]) ZJIT_LIBS="target/release/libzjit.a" diff --git a/debug_counter.h b/debug_counter.h index 9f0649e0ac8e33..8ffce66f0f7b16 100644 --- a/debug_counter.h +++ b/debug_counter.h @@ -305,7 +305,6 @@ RB_DEBUG_COUNTER(obj_imemo_ment) RB_DEBUG_COUNTER(obj_imemo_iseq) RB_DEBUG_COUNTER(obj_imemo_env) RB_DEBUG_COUNTER(obj_imemo_tmpbuf) -RB_DEBUG_COUNTER(obj_imemo_ast) RB_DEBUG_COUNTER(obj_imemo_cref) RB_DEBUG_COUNTER(obj_imemo_svar) RB_DEBUG_COUNTER(obj_imemo_throw_data) diff --git a/doc/contributing/building_ruby.md b/doc/contributing/building_ruby.md index a0486cb931de68..eac83fc00ef978 100644 --- a/doc/contributing/building_ruby.md +++ b/doc/contributing/building_ruby.md @@ -307,9 +307,6 @@ RUBY_TEST_TIMEOUT_SCALE=5 SYNTAX_SUGGEST_TIMEOUT=600 make check Please note, however, the following caveats! -* ASAN will not work properly on any currently released version of Ruby; the - necessary support is currently only present on Ruby's master branch (and the - whole test suite passes only as of commit [Revision 9d0a5148]). * Due to [Bug #20243], Clang generates code for threadlocal variables which doesn't work with M:N threading. Thus, it's necessary to disable M:N threading support at build time for now (with the `-DUSE_MN_THREADS=0` diff --git a/ext/-test-/namespace/yay1/yay1.def b/ext/-test-/namespace/yay1/yay1.def index edbae873126604..510fbe7017bd9f 100644 --- a/ext/-test-/namespace/yay1/yay1.def +++ b/ext/-test-/namespace/yay1/yay1.def @@ -1,4 +1,3 @@ -LIBRARY yay1 EXPORTS Init_yay1 yay_value diff --git a/ext/-test-/namespace/yay1/yay1.h b/ext/-test-/namespace/yay1/yay1.h index d68f8b552324b9..c4dade928ad34b 100644 --- a/ext/-test-/namespace/yay1/yay1.h +++ b/ext/-test-/namespace/yay1/yay1.h @@ -1,4 +1,4 @@ #include #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE yay_value(void); +RUBY_FUNC_EXPORTED VALUE yay_value(void); diff --git a/ext/-test-/namespace/yay2/yay2.def b/ext/-test-/namespace/yay2/yay2.def index 6bb6011f4b841d..163fc44c04649c 100644 --- a/ext/-test-/namespace/yay2/yay2.def +++ b/ext/-test-/namespace/yay2/yay2.def @@ -1,4 +1,3 @@ -LIBRARY yay2 EXPORTS Init_yay2 yay_value diff --git a/ext/-test-/namespace/yay2/yay2.h b/ext/-test-/namespace/yay2/yay2.h index d68f8b552324b9..c4dade928ad34b 100644 --- a/ext/-test-/namespace/yay2/yay2.h +++ b/ext/-test-/namespace/yay2/yay2.h @@ -1,4 +1,4 @@ #include #include "ruby/internal/dllexport.h" -RUBY_EXTERN VALUE yay_value(void); +RUBY_FUNC_EXPORTED VALUE yay_value(void); diff --git a/ext/json/lib/json/version.rb b/ext/json/lib/json/version.rb index 2aef3d7f613888..f9ac3e17a947a5 100644 --- a/ext/json/lib/json/version.rb +++ b/ext/json/lib/json/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module JSON - VERSION = '2.13.1' + VERSION = '2.13.2' end diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 8476c9b74752d1..8b3045fda322c4 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -500,7 +500,6 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) INIT_IMEMO_TYPE_ID(imemo_ment); INIT_IMEMO_TYPE_ID(imemo_iseq); INIT_IMEMO_TYPE_ID(imemo_tmpbuf); - INIT_IMEMO_TYPE_ID(imemo_ast); INIT_IMEMO_TYPE_ID(imemo_callinfo); INIT_IMEMO_TYPE_ID(imemo_callcache); INIT_IMEMO_TYPE_ID(imemo_constcache); diff --git a/gc/default/default.c b/gc/default/default.c index de3bee15223b19..47cfe3fb3baff3 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -4454,10 +4454,10 @@ rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj) asan_unpoisoning_object(obj) { /* Garbage can live on the stack, so do not mark or pin */ switch (BUILTIN_TYPE(obj)) { - case T_ZOMBIE: - case T_NONE: + case T_ZOMBIE: + case T_NONE: break; - default: + default: gc_mark_and_pin(objspace, obj); break; } diff --git a/gc/mmtk/src/api.rs b/gc/mmtk/src/api.rs index a1b94d520d57f4..5217eb4a758da1 100644 --- a/gc/mmtk/src/api.rs +++ b/gc/mmtk/src/api.rs @@ -107,10 +107,7 @@ pub extern "C" fn mmtk_builder_default() -> *mut MMTKBuilder { if let Some(threads) = mmtk_builder_default_parse_threads() { if !builder.options.threads.set(threads) { // MMTk will validate it and reject 0. - eprintln!( - "[FATAL] Failed to set the number of MMTk threads to {}", - threads - ); + eprintln!("[FATAL] Failed to set the number of MMTk threads to {threads}"); std::process::exit(1); } } @@ -120,10 +117,7 @@ pub extern "C" fn mmtk_builder_default() -> *mut MMTKBuilder { let heap_max = mmtk_builder_default_parse_heap_max(); if heap_min >= heap_max { - eprintln!( - "[FATAL] MMTK_HEAP_MIN({}) >= MMTK_HEAP_MAX({})", - heap_min, heap_max - ); + eprintln!("[FATAL] MMTK_HEAP_MIN({heap_min}) >= MMTK_HEAP_MAX({heap_max})"); std::process::exit(1); } diff --git a/gc/mmtk/src/binding.rs b/gc/mmtk/src/binding.rs index 619b7f246ce704..811cbf8abf8b32 100644 --- a/gc/mmtk/src/binding.rs +++ b/gc/mmtk/src/binding.rs @@ -119,7 +119,7 @@ impl RubyBinding { } pub fn register_wb_unprotected_object(&self, object: ObjectReference) { - debug!("Registering WB-unprotected object: {}", object); + debug!("Registering WB-unprotected object: {object}"); let mut objects = self.wb_unprotected_objects.lock().unwrap(); objects.insert(object); } diff --git a/gc/mmtk/src/collection.rs b/gc/mmtk/src/collection.rs index 0570b64e3a6012..41c508afd9115d 100644 --- a/gc/mmtk/src/collection.rs +++ b/gc/mmtk/src/collection.rs @@ -41,10 +41,7 @@ impl Collection for VMCollection { .name("MMTk Worker Thread".to_string()) .spawn(move || { let ordinal = worker.ordinal; - debug!( - "Hello! This is MMTk Worker Thread running! ordinal: {}", - ordinal - ); + debug!("Hello! This is MMTk Worker Thread running! ordinal: {ordinal}"); crate::register_gc_thread(thread::current().id()); let ptr_worker = &mut *worker as *mut GCWorker; let gc_thread_tls = @@ -55,10 +52,7 @@ impl Collection for VMCollection { GCThreadTLS::to_vwt(gc_thread_tls), worker, ); - debug!( - "An MMTk Worker Thread is quitting. Good bye! ordinal: {}", - ordinal - ); + debug!("An MMTk Worker Thread is quitting. Good bye! ordinal: {ordinal}"); crate::unregister_gc_thread(thread::current().id()); }) .unwrap(), diff --git a/gc/mmtk/src/scanning.rs b/gc/mmtk/src/scanning.rs index 36c0f998f4a3fe..af435813770a8a 100644 --- a/gc/mmtk/src/scanning.rs +++ b/gc/mmtk/src/scanning.rs @@ -47,11 +47,7 @@ impl Scanning for VMScanning { ); let forwarded_target = object_tracer.trace_object(target_object); if forwarded_target != target_object { - trace!( - " Forwarded target {} -> {}", - target_object, - forwarded_target - ); + trace!(" Forwarded target {target_object} -> {forwarded_target}"); } forwarded_target }; @@ -251,15 +247,11 @@ impl> GCWork for ScanWbUnprotectedRoots { VMScanning::collect_object_roots_in("wb_unprot_roots", gc_tls, &mut self.factory, || { for object in self.objects.iter().copied() { if object.is_reachable() { - debug!( - "[wb_unprot_roots] Visiting WB-unprotected object (parent): {}", - object - ); + debug!("[wb_unprot_roots] Visiting WB-unprotected object (parent): {object}"); (upcalls().scan_object_ruby_style)(object); } else { debug!( - "[wb_unprot_roots] Skipping young WB-unprotected object (parent): {}", - object + "[wb_unprot_roots] Skipping young WB-unprotected object (parent): {object}" ); } } diff --git a/gc/mmtk/src/weak_proc.rs b/gc/mmtk/src/weak_proc.rs index 204dd203aa333f..0217673e36b842 100644 --- a/gc/mmtk/src/weak_proc.rs +++ b/gc/mmtk/src/weak_proc.rs @@ -103,7 +103,7 @@ impl GCWork for ProcessObjFreeCandidates { let n_cands = obj_free_candidates.len(); - debug!("Total: {} candidates", n_cands); + debug!("Total: {n_cands} candidates"); // Process obj_free let mut new_candidates = Vec::new(); @@ -112,11 +112,7 @@ impl GCWork for ProcessObjFreeCandidates { if object.is_reachable() { // Forward and add back to the candidate list. let new_object = object.forward(); - trace!( - "Forwarding obj_free candidate: {} -> {}", - object, - new_object - ); + trace!("Forwarding obj_free candidate: {object} -> {new_object}"); new_candidates.push(new_object); } else { (upcalls().call_obj_free)(object); @@ -158,11 +154,10 @@ trait GlobalTableProcessingWork { let forward_object = |_worker, object: ObjectReference, _pin| { debug_assert!( mmtk::memory_manager::is_mmtk_object(object.to_raw_address()).is_some(), - "{} is not an MMTk object", - object + "{object} is not an MMTk object" ); let result = object.forward(); - trace!("Forwarding reference: {} -> {}", object, result); + trace!("Forwarding reference: {object} -> {result}"); result }; @@ -216,14 +211,10 @@ impl GCWork for UpdateWbUnprotectedObjectsList { if object.is_reachable() { // Forward and add back to the candidate list. let new_object = object.forward(); - trace!( - "Forwarding WB-unprotected object: {} -> {}", - object, - new_object - ); + trace!("Forwarding WB-unprotected object: {object} -> {new_object}"); objects.insert(new_object); } else { - trace!("Removing WB-unprotected object from list: {}", object); + trace!("Removing WB-unprotected object from list: {object}"); } } diff --git a/hash.c b/hash.c index 6f3ffb78ba2055..7256063d72e079 100644 --- a/hash.c +++ b/hash.c @@ -71,10 +71,6 @@ #define HASH_DEBUG 0 #endif -#if HASH_DEBUG -#include "internal/gc.h" -#endif - #define SET_DEFAULT(hash, ifnone) ( \ FL_UNSET_RAW(hash, RHASH_PROC_DEFAULT), \ RHASH_SET_IFNONE(hash, ifnone)) diff --git a/imemo.c b/imemo.c index 5f24e0230129db..7153689030a7b6 100644 --- a/imemo.c +++ b/imemo.c @@ -16,7 +16,6 @@ rb_imemo_name(enum imemo_type type) // put no default case to get a warning if an imemo type is missing switch (type) { #define IMEMO_NAME(x) case imemo_##x: return #x; - IMEMO_NAME(ast); IMEMO_NAME(callcache); IMEMO_NAME(callinfo); IMEMO_NAME(constcache); @@ -220,10 +219,6 @@ rb_imemo_memsize(VALUE obj) { size_t size = 0; switch (imemo_type(obj)) { - case imemo_ast: - rb_bug("imemo_ast is obsolete"); - - break; case imemo_callcache: break; case imemo_callinfo: @@ -336,10 +331,6 @@ void rb_imemo_mark_and_move(VALUE obj, bool reference_updating) { switch (imemo_type(obj)) { - case imemo_ast: - rb_bug("imemo_ast is obsolete"); - - break; case imemo_callcache: { /* cc is callcache. * @@ -600,10 +591,6 @@ void rb_imemo_free(VALUE obj) { switch (imemo_type(obj)) { - case imemo_ast: - rb_bug("imemo_ast is obsolete"); - - break; case imemo_callcache: RB_DEBUG_COUNTER_INC(obj_imemo_callcache); diff --git a/inits.c b/inits.c index 29087a6306481b..b4e58ea25a1cec 100644 --- a/inits.c +++ b/inits.c @@ -90,7 +90,6 @@ rb_call_builtin_inits(void) #define BUILTIN(n) CALL(builtin_##n) BUILTIN(kernel); BUILTIN(yjit); - // BUILTIN(yjit_hook) is called after rb_yjit_init() BUILTIN(gc); BUILTIN(ractor); BUILTIN(numeric); @@ -109,6 +108,7 @@ rb_call_builtin_inits(void) BUILTIN(nilclass); BUILTIN(marshal); BUILTIN(zjit); + BUILTIN(yjit_hook); Init_builtin_prelude(); } #undef CALL diff --git a/internal/bignum.h b/internal/bignum.h index db8d3aee83852e..0ba21a492334f1 100644 --- a/internal/bignum.h +++ b/internal/bignum.h @@ -121,6 +121,7 @@ VALUE rb_integer_float_eq(VALUE x, VALUE y); VALUE rb_str_convert_to_inum(VALUE str, int base, int badcheck, int raise_exception); VALUE rb_big_comp(VALUE x); VALUE rb_big_aref(VALUE x, VALUE y); +VALUE rb_big_aref2(VALUE num, VALUE beg, VALUE len); VALUE rb_big_abs(VALUE x); VALUE rb_big_size_m(VALUE big); VALUE rb_big_bit_length(VALUE big); diff --git a/internal/imemo.h b/internal/imemo.h index dcea997ae803a7..0ad00fe6b79d99 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -37,7 +37,6 @@ enum imemo_type { imemo_ment = 6, imemo_iseq = 7, imemo_tmpbuf = 8, - imemo_ast = 9, // Obsolete due to the universal parser imemo_callinfo = 10, imemo_callcache = 11, imemo_constcache = 12, diff --git a/internal/numeric.h b/internal/numeric.h index 6406cfc2fa7480..58f42f41ac4bea 100644 --- a/internal/numeric.h +++ b/internal/numeric.h @@ -85,6 +85,7 @@ VALUE rb_int_cmp(VALUE x, VALUE y); VALUE rb_int_equal(VALUE x, VALUE y); VALUE rb_int_divmod(VALUE x, VALUE y); VALUE rb_int_and(VALUE x, VALUE y); +VALUE rb_int_xor(VALUE x, VALUE y); VALUE rb_int_lshift(VALUE x, VALUE y); VALUE rb_int_rshift(VALUE x, VALUE y); VALUE rb_int_div(VALUE x, VALUE y); diff --git a/internal/vm.h b/internal/vm.h index 3ee958a020f474..3a99011c44b2d8 100644 --- a/internal/vm.h +++ b/internal/vm.h @@ -79,7 +79,7 @@ VALUE rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_ca struct vm_ifunc *rb_current_ifunc(void); VALUE rb_gccct_clear_table(VALUE); -#if USE_YJIT +#if USE_YJIT || USE_ZJIT /* vm_exec.c */ extern uint64_t rb_vm_insns_count; #endif diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb index 2e63a16ec3178b..92d7a1c519c219 100644 --- a/lib/bundler/cli/cache.rb +++ b/lib/bundler/cli/cache.rb @@ -16,7 +16,6 @@ def run setup_cache_all install - # TODO: move cache contents here now that all bundles are locked custom_path = Bundler.settings[:path] if options[:path] Bundler.settings.temporary(cache_all_platforms: options["all-platforms"]) do diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 32006af109ee09..6c7a3e9c38a613 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -645,20 +645,12 @@ def filter_specs(specs, deps, skips: []) end def materialize(dependencies) - # Tracks potential endless loops trying to re-resolve. - # TODO: Remove as dead code if not reports are received in a while - incorrect_spec = nil - specs = begin resolve.materialize(dependencies) rescue IncorrectLockfileDependencies => e raise if Bundler.frozen_bundle? - spec = e.spec - raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == spec - - incorrect_spec = spec - reresolve_without([spec]) + reresolve_without([e.spec]) retry end @@ -740,7 +732,6 @@ def reresolve_without(incomplete_specs) def start_resolution local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && !@platforms.include?(Bundler.local_platform) @platforms << Bundler.local_platform if local_platform_needed_for_resolvability - add_platform(Gem::Platform::RUBY) if RUBY_ENGINE == "truffleruby" result = SpecSet.new(resolver.start) diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb index ff75e7b6bc2c94..3906be3f57718e 100644 --- a/lib/bundler/resolver/package.rb +++ b/lib/bundler/resolver/package.rb @@ -21,6 +21,7 @@ def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefe @locked_version = locked_specs.version_for(name) @unlock = unlock @dependency = dependency || Dependency.new(name, @locked_version) + @platforms |= [Gem::Platform::RUBY] if @dependency.default_force_ruby_platform @top_level = !dependency.nil? @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore @prefer_local = prefer_local diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index ac76ae1fa0e44c..7511def2306192 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -124,11 +124,7 @@ def expanded_path end def expand(somepath) - if Bundler.current_ruby.jruby? # TODO: Unify when https://github.com/rubygems/bundler/issues/7598 fixed upstream and all supported jrubies include the fix - somepath.expand_path(root_path).expand_path - else - somepath.expand_path(root_path) - end + somepath.expand_path(root_path) rescue ArgumentError => e Bundler.ui.debug(e) raise PathError, "There was an error while trying to use the path " \ diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb index bccfbb6b85e887..d8c9863054296b 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb @@ -242,6 +242,35 @@ def inject_into_module(path, module_name, *args, &block) insert_into_file(path, *(args << config), &block) end + # Run a regular expression replacement on a file, raising an error if the + # contents of the file are not changed. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string to be replaced + # replacement:: the replacement, can be also given as a block + # config:: give :verbose => false to not log the status, and + # :force => true, to force the replacement regardless of runner behavior. + # + # ==== Example + # + # gsub_file! 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + # gsub_file! 'README', /rake/, :green do |match| + # match << " no more. Use thor!" + # end + # + def gsub_file!(path, flag, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + + return unless behavior == :invoke || config.fetch(:force, false) + + path = File.expand_path(path, destination_root) + say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + actually_gsub_file(path, flag, args, true, &block) unless options[:pretend] + end + # Run a regular expression replacement on a file. # # ==== Parameters @@ -267,11 +296,7 @@ def gsub_file(path, flag, *args, &block) path = File.expand_path(path, destination_root) say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) - unless options[:pretend] - content = File.binread(path) - content.gsub!(flag, *args, &block) - File.open(path, "wb") { |file| file.write(content) } - end + actually_gsub_file(path, flag, args, false, &block) unless options[:pretend] end # Uncomment all lines matching a given regex. Preserves indentation before @@ -348,7 +373,7 @@ def capture(*args) end def with_output_buffer(buf = "".dup) #:nodoc: - raise ArgumentError, "Buffer can not be a frozen object" if buf.frozen? + raise ArgumentError, "Buffer cannot be a frozen object" if buf.frozen? old_buffer = output_buffer self.output_buffer = buf yield @@ -357,6 +382,17 @@ def with_output_buffer(buf = "".dup) #:nodoc: self.output_buffer = old_buffer end + def actually_gsub_file(path, flag, args, error_on_no_change, &block) + content = File.binread(path) + success = content.gsub!(flag, *args, &block) + + if success.nil? && error_on_no_change + raise Bundler::Thor::Error, "The content of #{path} did not change" + end + + File.open(path, "wb") { |file| file.write(content) } + end + # Bundler::Thor::Actions#capture depends on what kind of buffer is used in ERB. # Thus CapturableERB fixes ERB to use String buffer. class CapturableERB < ERB diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb index 734f5fe7e34af0..fe22d989e54095 100644 --- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb @@ -144,7 +144,7 @@ def parse(args) # rubocop:disable Metrics/MethodLength def check_exclusive! opts = @assigns.keys # When option A and B are exclusive, if A and B are given at the same time, - # the diffrence of argument array size will decrease. + # the difference of argument array size will decrease. found = @exclusives.find{ |ex| (ex - opts).size < ex.size - 1 } if found names = names_to_switch_names(found & opts).map{|n| "'#{n}'"} diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb index c7cc873131f3e8..95f8b16e31893a 100644 --- a/lib/bundler/vendor/thor/lib/thor/runner.rb +++ b/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -1,7 +1,6 @@ require_relative "../thor" require_relative "group" -require "yaml" require "digest/sha2" require "pathname" @@ -195,6 +194,7 @@ def thor_root def thor_yaml @thor_yaml ||= begin yaml_file = File.join(thor_root, "thor.yml") + require "yaml" yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file) yaml || {} end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index b3e85733cba788..da02b9422758e5 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -314,7 +314,7 @@ def show_diff(destination, content) #:nodoc: diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" require "tempfile" - Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| + Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp| temp.write content temp.rewind system %(#{diff_cmd} "#{destination}" "#{temp.path}") @@ -372,16 +372,12 @@ def merge(destination, content) #:nodoc: Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| temp.write content temp.rewind - system %(#{merge_tool} "#{temp.path}" "#{destination}") + system(merge_tool, temp.path, destination) end end def merge_tool #:nodoc: - @merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool - end - - def git_merge_tool #:nodoc: - `git config merge.tool`.rstrip rescue "" + @merge_tool ||= ENV["THOR_MERGE"] || "git difftool --no-index" end end end diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index cd7b4f060eac5b..5474a2f71badc8 100644 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "1.3.2" + VERSION = "1.4.0" end diff --git a/lib/prism/ffi.rb b/lib/prism/ffi.rb index 5a4ba09a4fd9a0..1e1bf8b1c877d4 100644 --- a/lib/prism/ffi.rb +++ b/lib/prism/ffi.rb @@ -422,13 +422,13 @@ def dump_options_command_line(options) def dump_options_version(version) case version when nil, "latest" - 0 + 0 # Handled in pm_parser_init when /\A3\.3(\.\d+)?\z/ 1 when /\A3\.4(\.\d+)?\z/ 2 when /\A3\.5(\.\d+)?\z/ - 0 + 3 else raise ArgumentError, "invalid version: #{version}" end diff --git a/lib/tsort.gemspec b/lib/tsort.gemspec index 8970cbe8261770..4e0ef0507df008 100644 --- a/lib/tsort.gemspec +++ b/lib/tsort.gemspec @@ -19,6 +19,7 @@ Gem::Specification.new do |spec| spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage + spec.metadata["changelog_uri"] = "#{spec.homepage}/releases" dir, gemspec = File.split(__FILE__) excludes = %W[ diff --git a/numeric.c b/numeric.c index d3affed8049b34..89cff8a730fc9c 100644 --- a/numeric.c +++ b/numeric.c @@ -5115,8 +5115,8 @@ fix_xor(VALUE x, VALUE y) * */ -static VALUE -int_xor(VALUE x, VALUE y) +VALUE +rb_int_xor(VALUE x, VALUE y) { if (FIXNUM_P(x)) { return fix_xor(x, y); @@ -5288,10 +5288,23 @@ generate_mask(VALUE len) return rb_int_minus(rb_int_lshift(INT2FIX(1), len), INT2FIX(1)); } +static VALUE +int_aref2(VALUE num, VALUE beg, VALUE len) +{ + if (RB_TYPE_P(num, T_BIGNUM)) { + return rb_big_aref2(num, beg, len); + } + else { + num = rb_int_rshift(num, beg); + VALUE mask = generate_mask(len); + return rb_int_and(num, mask); + } +} + static VALUE int_aref1(VALUE num, VALUE arg) { - VALUE orig_num = num, beg, end; + VALUE beg, end; int excl; if (rb_range_values(arg, &beg, &end, &excl)) { @@ -5311,22 +5324,19 @@ int_aref1(VALUE num, VALUE arg) return INT2FIX(0); } } - num = rb_int_rshift(num, beg); int cmp = compare_indexes(beg, end); if (!NIL_P(end) && cmp < 0) { VALUE len = rb_int_minus(end, beg); if (!excl) len = rb_int_plus(len, INT2FIX(1)); - VALUE mask = generate_mask(len); - num = rb_int_and(num, mask); + return int_aref2(num, beg, len); } else if (cmp == 0) { if (excl) return INT2FIX(0); - num = orig_num; arg = beg; goto one_bit; } - return num; + return rb_int_rshift(num, beg); } one_bit: @@ -5339,15 +5349,6 @@ int_aref1(VALUE num, VALUE arg) return Qnil; } -static VALUE -int_aref2(VALUE num, VALUE beg, VALUE len) -{ - num = rb_int_rshift(num, beg); - VALUE mask = generate_mask(len); - num = rb_int_and(num, mask); - return num; -} - /* * call-seq: * self[offset] -> 0 or 1 @@ -6366,7 +6367,7 @@ Init_Numeric(void) rb_define_method(rb_cInteger, "&", rb_int_and, 1); rb_define_method(rb_cInteger, "|", int_or, 1); - rb_define_method(rb_cInteger, "^", int_xor, 1); + rb_define_method(rb_cInteger, "^", rb_int_xor, 1); rb_define_method(rb_cInteger, "[]", int_aref, -1); rb_define_method(rb_cInteger, "<<", rb_int_lshift, 1); diff --git a/prism/options.c b/prism/options.c index a457178ce85757..1b5c022cf54f9f 100644 --- a/prism/options.c +++ b/prism/options.c @@ -89,7 +89,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5", 3) == 0) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } @@ -108,7 +108,7 @@ pm_options_version_set(pm_options_t *options, const char *version, size_t length } if (strncmp(version, "3.5.", 4) == 0 && is_number(version + 4, length - 4)) { - options->version = PM_OPTIONS_VERSION_LATEST; + options->version = PM_OPTIONS_VERSION_CRUBY_3_5; return true; } } diff --git a/prism/options.h b/prism/options.h index 2f64701b0c20ea..092fda4f07878a 100644 --- a/prism/options.h +++ b/prism/options.h @@ -82,14 +82,20 @@ typedef void (*pm_options_shebang_callback_t)(struct pm_options *options, const * parse in the same way as a specific version of CRuby would have. */ typedef enum { - /** The current version of prism. */ - PM_OPTIONS_VERSION_LATEST = 0, + /** If an explicit version is not provided, the current version of prism will be used. */ + PM_OPTIONS_VERSION_UNSET = 0, /** The vendored version of prism in CRuby 3.3.x. */ PM_OPTIONS_VERSION_CRUBY_3_3 = 1, /** The vendored version of prism in CRuby 3.4.x. */ - PM_OPTIONS_VERSION_CRUBY_3_4 = 2 + PM_OPTIONS_VERSION_CRUBY_3_4 = 2, + + /** The vendored version of prism in CRuby 3.5.x. */ + PM_OPTIONS_VERSION_CRUBY_3_5 = 3, + + /** The current version of prism. */ + PM_OPTIONS_VERSION_LATEST = PM_OPTIONS_VERSION_CRUBY_3_5 } pm_options_version_t; /** diff --git a/prism/prism.c b/prism/prism.c index a40e0ebeb0c821..ec8f84fb6bc8da 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -1409,7 +1409,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) { static inline void pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) { if (pm_conditional_predicate_warn_write_literal_p(node)) { - pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); + pm_parser_warn_node(parser, node, parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL); } } @@ -2976,7 +2976,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const */ static void pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) { - if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) { if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) { pm_node_t *node; PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) { @@ -9113,7 +9113,7 @@ lex_global_variable(pm_parser_t *parser) { } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0); // $0 isn't allowed to be followed by anything. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id); } @@ -9150,7 +9150,7 @@ lex_global_variable(pm_parser_t *parser) { } else { // If we get here, then we have a $ followed by something that // isn't recognized as a global variable. - pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; + pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL; const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end); PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start); } @@ -10177,7 +10177,7 @@ lex_at_variable(pm_parser_t *parser) { } } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) { pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE; - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) { diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3; } @@ -14667,7 +14667,7 @@ parse_parameters( parser_lex(parser); pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1)); @@ -14683,7 +14683,7 @@ parse_parameters( // If the value of the parameter increased the number of // reads of that parameter, then we need to warn that we // have a circular definition. - if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR); } @@ -14768,13 +14768,13 @@ parse_parameters( if (token_begins_expression_p(parser->current.type)) { pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local); - uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; + uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0; if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true); pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1)); if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser); - if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { + if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR); } @@ -16478,7 +16478,7 @@ parse_variable(pm_parser_t *parser) { pm_node_list_append(¤t_scope->implicit_parameters, node); return node; - } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) { + } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) { pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous); pm_node_list_append(¤t_scope->implicit_parameters, node); @@ -19524,7 +19524,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_do_loop_stack_push(parser, false); statements = (pm_node_t *) pm_statements_node_create(parser); - pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, binding_power < PM_BINDING_POWER_COMPOSITION, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); + // In endless method bodies, we need to handle command calls carefully. + // We want to allow command calls in assignment context but maintain + // the same binding power to avoid changing how operators are parsed. + // Note that we're intentionally NOT allowing code like `private def foo = puts "Hello"` + // because the original parser, parse.y, can't handle it and we want to maintain the same behavior + bool allow_command_call = (binding_power == PM_BINDING_POWER_ASSIGNMENT) || + (binding_power < PM_BINDING_POWER_COMPOSITION); + + pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1)); if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) { context_push(parser, PM_CONTEXT_RESCUE_MODIFIER); @@ -22641,6 +22649,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm } } + // Now that we have established the user-provided options, check if + // a version was given and parse as the latest version otherwise. + if (parser->version == PM_OPTIONS_VERSION_UNSET) { + parser->version = PM_OPTIONS_VERSION_LATEST; + } + pm_accepts_block_stack_push(parser, true); // Skip past the UTF-8 BOM if it exists. diff --git a/ruby.c b/ruby.c index 0d09e7ce61e1ab..cc67b0b25db8f8 100644 --- a/ruby.c +++ b/ruby.c @@ -1833,12 +1833,6 @@ ruby_opt_init(ruby_cmdline_options_t *opt) } #endif -#if USE_YJIT - // Call yjit_hook.rb after rb_yjit_init() to use `RubyVM::YJIT.enabled?` - void Init_builtin_yjit_hook(); - Init_builtin_yjit_hook(); -#endif - rb_namespace_init_done(); ruby_init_prelude(); ruby_set_script_name(opt->script_name); diff --git a/set.c b/set.c index 55d0e626338af5..61f1fd8bc43d1b 100644 --- a/set.c +++ b/set.c @@ -505,6 +505,14 @@ set_i_initialize(int argc, VALUE *argv, VALUE set) } } else { + ID id_size = rb_intern("size"); + if (rb_obj_is_kind_of(other, rb_mEnumerable) && rb_respond_to(other, id_size)) { + VALUE size = rb_funcall(other, id_size, 0); + if (RB_TYPE_P(size, T_FLOAT) && RFLOAT_VALUE(size) == INFINITY) { + rb_raise(rb_eArgError, "cannot initialize Set from an object with infinite size"); + } + } + rb_block_call(other, enum_method_id(other), 0, 0, rb_block_given_p() ? set_initialize_with_block : set_initialize_without_block, set); diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index c47cc9727191ea..19c1a5ca8bf0ab 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -2237,7 +2237,6 @@ nokogiri (1.14.2-x86_64-linux) PLATFORMS - ruby x86_64-linux DEPENDENCIES diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 228ae7b0d06cc1..71065c36f33507 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -392,7 +392,23 @@ end end - it "installs sorbet-static, which does not provide a pure ruby variant, just fine", :truffleruby do + it "installs sorbet-static, which does not provide a pure ruby variant, in absence of a lockfile, just fine", :truffleruby do + skip "does not apply to Windows" if Gem.win_platform? + + build_repo2 do + build_gem("sorbet-static", "0.5.6403") {|s| s.platform = Bundler.local_platform } + end + + gemfile <<~G + source "https://gem.repo2" + + gem "sorbet-static", "0.5.6403" + G + + bundle "install --verbose" + end + + it "installs sorbet-static, which does not provide a pure ruby variant, in presence of a lockfile, just fine", :truffleruby do skip "does not apply to Windows" if Gem.win_platform? build_repo2 do diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock index c9ded0fd787030..ccb51b6fd31eb3 100644 --- a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock @@ -31,7 +31,7 @@ GEM spoom (>= 1.2.0) thor (>= 1.2.0) yard-sorbet - thor (1.3.2) + thor (1.4.0) yard (0.9.37) yard-sorbet (0.9.0) sorbet-runtime @@ -46,4 +46,4 @@ DEPENDENCIES tapioca BUNDLED WITH - 2.7.0.dev + 2.8.0.dev diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock index f6d50aad359018..15f9a224e55bbb 100644 --- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock +++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock @@ -36,4 +36,4 @@ DEPENDENCIES warbler! BUNDLED WITH - 2.7.0.dev + 2.8.0.dev diff --git a/spec/ruby/command_line/dash_v_spec.rb b/spec/ruby/command_line/dash_v_spec.rb index d30efa37d68c23..b13350404c659c 100644 --- a/spec/ruby/command_line/dash_v_spec.rb +++ b/spec/ruby/command_line/dash_v_spec.rb @@ -6,7 +6,7 @@ describe "when used alone" do it "prints version and ends" do - ruby_exe(nil, args: '-v').sub("+PRISM ", "").should include(RUBY_DESCRIPTION.sub("+PRISM ", "")) + ruby_exe(nil, args: '-v').gsub("+PRISM ", "").should include(RUBY_DESCRIPTION.gsub("+PRISM ", "")) end unless (defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?) || (defined?(RubyVM::RJIT) && RubyVM::RJIT.enabled?) || (ENV['RUBY_GC_LIBRARY'] && ENV['RUBY_GC_LIBRARY'].length > 0) || diff --git a/spec/ruby/command_line/rubyopt_spec.rb b/spec/ruby/command_line/rubyopt_spec.rb index e940f912af8f7b..e1163ffcfcc9d7 100644 --- a/spec/ruby/command_line/rubyopt_spec.rb +++ b/spec/ruby/command_line/rubyopt_spec.rb @@ -25,12 +25,12 @@ guard -> { RbConfig::CONFIG["CROSS_COMPILING"] != "yes" } do it "prints the version number for '-v'" do ENV["RUBYOPT"] = '-v' - ruby_exe("").sub("+PRISM ", "").sub(/\+GC(\[\w+\]\s|\s)?/, "")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "").sub(/\+GC(\[\w+\]\s|\s)?/, "") + ruby_exe("")[/\A.*/].gsub(/\s\+(PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(PRISM|GC(\[\w+\])?)(?=\s)/, "") end it "ignores whitespace around the option" do ENV["RUBYOPT"] = ' -v ' - ruby_exe("").sub("+PRISM ", "").sub(/\+GC(\[\w+\]\s|\s)?/, "")[/\A.*/].should == RUBY_DESCRIPTION.sub("+PRISM ", "").sub(/\+GC(\[\w+\]\s|\s)?/, "") + ruby_exe("")[/\A.*/].gsub(/\s\+(PRISM|GC(\[\w+\])?)(?=\s)/, "").should == RUBY_DESCRIPTION.gsub(/\s\+(PRISM|GC(\[\w+\])?)(?=\s)/, "") end end diff --git a/test/prism/errors/private_endless_method.txt b/test/prism/errors/private_endless_method.txt new file mode 100644 index 00000000000000..8aae5e0cd39035 --- /dev/null +++ b/test/prism/errors/private_endless_method.txt @@ -0,0 +1,3 @@ +private def foo = puts "Hello" + ^ unexpected string literal, expecting end-of-input + diff --git a/test/prism/fixtures/endless_methods.txt b/test/prism/fixtures/endless_methods.txt index 8c2f2a30cc58c0..7eb3bf431897b1 100644 --- a/test/prism/fixtures/endless_methods.txt +++ b/test/prism/fixtures/endless_methods.txt @@ -3,3 +3,5 @@ def foo = 1 def bar = A "" def method = 1 + 2 + 3 + +x = def f = p 1 diff --git a/test/ruby/test_bignum.rb b/test/ruby/test_bignum.rb index beef33e2a60ff4..dd6f4baa4c292b 100644 --- a/test/ruby/test_bignum.rb +++ b/test/ruby/test_bignum.rb @@ -605,6 +605,49 @@ def test_aref assert_equal(1, (-2**(BIGNUM_MIN_BITS*4))[BIGNUM_MIN_BITS*4]) end + def test_aref2 + x = (0x123456789abcdef << (BIGNUM_MIN_BITS + 32)) | 0x12345678 + assert_equal(x, x[0, x.bit_length]) + assert_equal(x >> 10, x[10, x.bit_length]) + assert_equal(0x45678, x[0, 20]) + assert_equal(0x6780, x[-4, 16]) + assert_equal(0x123456, x[x.bit_length - 21, 40]) + assert_equal(0x6789ab, x[x.bit_length - 41, 24]) + assert_equal(0, x[-20, 10]) + assert_equal(0, x[x.bit_length + 10, 10]) + + assert_equal(0, x[5, 0]) + assert_equal(0, (-x)[5, 0]) + + assert_equal(x >> 5, x[5, -1]) + assert_equal(x << 5, x[-5, -1]) + assert_equal((-x) >> 5, (-x)[5, -1]) + assert_equal((-x) << 5, (-x)[-5, -1]) + + assert_equal(x << 5, x[-5, FIXNUM_MAX]) + assert_equal(x >> 5, x[5, FIXNUM_MAX]) + assert_equal(0, x[FIXNUM_MIN, 100]) + assert_equal(0, (-x)[FIXNUM_MIN, 100]) + + y = (x << 160) | 0x1234_0000_0000_0000_1234_0000_0000_0000 + assert_equal(0xffffedcc00, (-y)[40, 40]) + assert_equal(0xfffffffedc, (-y)[52, 40]) + assert_equal(0xffffedcbff, (-y)[104, 40]) + assert_equal(0xfffff6e5d4, (-y)[y.bit_length - 20, 40]) + assert_equal(0, (-y)[-20, 10]) + assert_equal(0xfff, (-y)[y.bit_length + 10, 12]) + + z = (1 << (BIGNUM_MIN_BITS * 2)) - 1 + assert_equal(0x400, (-z)[-10, 20]) + assert_equal(1, (-z)[0, 20]) + assert_equal(0, (-z)[10, 20]) + assert_equal(1, (-z)[0, z.bit_length]) + assert_equal(0, (-z)[z.bit_length - 10, 10]) + assert_equal(0x400, (-z)[z.bit_length - 10, 11]) + assert_equal(0xfff, (-z)[z.bit_length, 12]) + assert_equal(0xfff00, (-z)[z.bit_length - 8, 20]) + end + def test_hash assert_nothing_raised { T31P.hash } end diff --git a/test/ruby/test_set.rb b/test/ruby/test_set.rb index 87e1fd8d26e908..934a470c1e626f 100644 --- a/test/ruby/test_set.rb +++ b/test/ruby/test_set.rb @@ -81,6 +81,20 @@ def test_s_new s = Set.new(ary) { |o| o * 2 } assert_equal([2,4,6], s.sort) + + assert_raise(ArgumentError) { + Set.new((1..)) + } + assert_raise(ArgumentError) { + Set.new((1..), &:succ) + } + assert_raise(ArgumentError) { + Set.new(1.upto(Float::INFINITY)) + } + + assert_raise(ArgumentError) { + Set.new(Object.new) + } end def test_clone diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 0dcdb8e4cb8275..b78d53e682233c 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -819,6 +819,13 @@ def a(n1,n2,n3,n4,n5,n6,n7,n8) = self } end + def test_spilled_param_new_arary + assert_compiles '[:ok]', %q{ + def a(n1,n2,n3,n4,n5,n6,n7,n8) = [n8] + a(0,0,0,0,0,0,0, :ok) + } + end + def test_opt_aref_with assert_compiles ':ok', %q{ def aref_with(hash) = hash["key"] @@ -882,6 +889,48 @@ def test = X end end + def test_constant_invalidation + assert_compiles '123', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path] + class C; end + def test = C + test + test + + C = 123 + test + RUBY + end + + def test_constant_path_invalidation + assert_compiles '["Foo::C", "Foo::C", "Bar::C"]', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path] + module A + module B; end + end + + module Foo + C = "Foo::C" + end + + module Bar + C = "Bar::C" + end + + A::B = Foo + + def test = A::B::C + + result = [] + + result << test + result << test + + A::B = Bar + + result << test + result + RUBY + end + def test_dupn assert_compiles '[[1], [1, 1], :rhs, [nil, :rhs]]', <<~RUBY, insns: [:dupn] def test(array) = (array[1, 2] ||= :rhs) diff --git a/tool/bundler/vendor_gems.rb b/tool/bundler/vendor_gems.rb index 71a7fbf4b0f7cc..b3e06d3f096047 100644 --- a/tool/bundler/vendor_gems.rb +++ b/tool/bundler/vendor_gems.rb @@ -12,6 +12,6 @@ gem "resolv", "0.6.2" gem "securerandom", "0.4.1" gem "timeout", "0.4.3" -gem "thor", "1.3.2" +gem "thor", "1.4.0" gem "tsort", "0.2.0" gem "uri", "1.0.3" diff --git a/tool/bundler/vendor_gems.rb.lock b/tool/bundler/vendor_gems.rb.lock index 82dbe8963c46bf..825107514a6f1a 100644 --- a/tool/bundler/vendor_gems.rb.lock +++ b/tool/bundler/vendor_gems.rb.lock @@ -37,7 +37,7 @@ GEM optparse (0.6.0) resolv (0.6.2) securerandom (0.4.1) - thor (1.3.2) + thor (1.4.0) timeout (0.4.3) tsort (0.2.0) uri (1.0.3) @@ -60,7 +60,7 @@ DEPENDENCIES pub_grub! resolv (= 0.6.2) securerandom (= 0.4.1) - thor (= 1.3.2) + thor (= 1.4.0) timeout (= 0.4.3) tsort (= 0.2.0) uri (= 1.0.3) @@ -76,7 +76,7 @@ CHECKSUMS pub_grub (0.5.0) resolv (0.6.2) sha256=61efe545cedddeb1b14f77e51f85c85ca66af5098fdbf567fadf32c34590fb14 securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda + thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d timeout (0.4.3) sha256=9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f uri (1.0.3) sha256=e9f2244608eea2f7bc357d954c65c910ce0399ca5e18a7a29207ac22d8767011 diff --git a/tool/lib/envutil.rb b/tool/lib/envutil.rb index d02329d4f13053..fe166d85145ea7 100644 --- a/tool/lib/envutil.rb +++ b/tool/lib/envutil.rb @@ -226,7 +226,6 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = args = [args] if args.kind_of?(String) # use the same parser as current ruby if args.none? { |arg| arg.start_with?("--parser=") } - current_parser = RUBY_DESCRIPTION =~ /prism/i ? "prism" : "parse.y" args = ["--parser=#{current_parser}"] + args end pid = spawn(child_env, *precommand, rubybin, *args, opt) @@ -276,6 +275,12 @@ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = end module_function :invoke_ruby + def current_parser + features = RUBY_DESCRIPTION[%r{\)\K [-+*/%._0-9a-zA-Z ]*(?=\[[-+*/%._0-9a-zA-Z]+\]\z)}] + features&.split&.include?("+PRISM") ? "prism" : "parse.y" + end + module_function :current_parser + def verbose_warning class << (stderr = "".dup) alias write concat diff --git a/tool/zjit_bisect.rb b/tool/zjit_bisect.rb new file mode 100755 index 00000000000000..472a60e66c5df9 --- /dev/null +++ b/tool/zjit_bisect.rb @@ -0,0 +1,97 @@ +#!/usr/bin/env ruby +require 'logger' +require 'open3' +require 'tempfile' +require 'timeout' + +RUBY = ARGV[0] || raise("Usage: ruby jit_bisect.rb ") +OPTIONS = ARGV[1] || raise("Usage: ruby jit_bisect.rb ") +TIMEOUT_SEC = 5 +LOGGER = Logger.new($stdout) + +# From https://github.com/tekknolagi/omegastar +# MIT License +# Copyright (c) 2024 Maxwell Bernstein and Meta Platforms +# Attempt to reduce the `items` argument as much as possible, returning the +# shorter version. `fixed` will always be used as part of the items when +# running `command`. +# `command` should return True if the command succeeded (the failure did not +# reproduce) and False if the command failed (the failure reproduced). +def bisect_impl(command, fixed, items, indent="") + LOGGER.info("#{indent}step fixed[#{fixed.length}] and items[#{items.length}]") + while items.length > 1 + LOGGER.info("#{indent}#{fixed.length + items.length} candidates") + # Return two halves of the given list. For odd-length lists, the second + # half will be larger. + half = items.length / 2 + left = items[0...half] + right = items[half..] + if !command.call(fixed + left) + items = left + next + end + if !command.call(fixed + right) + items = right + next + end + # We need something from both halves to trigger the failure. Try + # holding each half fixed and bisecting the other half to reduce the + # candidates. + new_right = bisect_impl(command, fixed + left, right, indent + "< ") + new_left = bisect_impl(command, fixed + new_right, left, indent + "> ") + return new_left + new_right + end + items +end + +# From https://github.com/tekknolagi/omegastar +# MIT License +# Copyright (c) 2024 Maxwell Bernstein and Meta Platforms +def run_bisect(command, items) + LOGGER.info("Verifying items") + if command.call(items) + raise StandardError.new("Command succeeded with full items") + end + if !command.call([]) + raise StandardError.new("Command failed with empty items") + end + bisect_impl(command, [], items) +end + +def run_with_jit_list(ruby, options, jit_list) + # Make a new temporary file containing the JIT list + Tempfile.create("jit_list") do |temp_file| + temp_file.write(jit_list.join("\n")) + temp_file.flush + temp_file.close + # Run the JIT with the temporary file + Open3.capture3("#{ruby} --zjit-allowed-iseqs=#{temp_file.path} #{options}") + end +end + +# Try running with no JIT list to get a stable baseline +_, stderr, status = run_with_jit_list(RUBY, OPTIONS, []) +if !status.success? + raise "Command failed with empty JIT list: #{stderr}" +end +# Collect the JIT list from the failing Ruby process +jit_list = nil +Tempfile.create "jit_list" do |temp_file| + Open3.capture3("#{RUBY} --zjit-log-compiled-iseqs=#{temp_file.path} #{OPTIONS}") + jit_list = File.readlines(temp_file.path).map(&:strip).reject(&:empty?) +end +LOGGER.info("Starting with JIT list of #{jit_list.length} items.") +# Now narrow it down +command = lambda do |items| + status = Timeout.timeout(TIMEOUT_SEC) do + _, _, status = run_with_jit_list(RUBY, OPTIONS, items) + status + end + status.success? +end +result = run_bisect(command, jit_list) +File.open("jitlist.txt", "w") do |file| + file.puts(result) +end +puts "Reduced JIT list (available in jitlist.txt):" +puts result diff --git a/vm.c b/vm.c index bfc9ff733c5cdb..da5a51d25b73ba 100644 --- a/vm.c +++ b/vm.c @@ -35,6 +35,8 @@ #include "iseq.h" #include "symbol.h" // This includes a macro for a more performant rb_id2sym. #include "yjit.h" +#include "insns.inc" +#include "zjit.h" #include "ruby/st.h" #include "ruby/vm.h" #include "vm_core.h" @@ -45,8 +47,6 @@ #include "ractor_core.h" #include "vm_sync.h" #include "shape.h" -#include "insns.inc" -#include "zjit.h" #include "builtin.h" diff --git a/vm_exec.c b/vm_exec.c index 947d4dc42169a9..3d7c241a321bfa 100644 --- a/vm_exec.c +++ b/vm_exec.c @@ -11,8 +11,8 @@ #include -#if USE_YJIT -// The number of instructions executed on vm_exec_core. --yjit-stats uses this. +#if USE_YJIT || USE_ZJIT +// The number of instructions executed on vm_exec_core. --yjit-stats and --zjit-stats use this. uint64_t rb_vm_insns_count = 0; #endif diff --git a/vm_insnhelper.h b/vm_insnhelper.h index ba957406978e11..24bfbb8210b7d0 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -16,7 +16,7 @@ RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_invalidations; RUBY_EXTERN rb_serial_t ruby_vm_constant_cache_misses; RUBY_EXTERN rb_serial_t ruby_vm_global_cvar_state; -#if USE_YJIT && YJIT_STATS // We want vm_insns_count only on stats builds. +#if YJIT_STATS || ZJIT_STATS // We want vm_insns_count only on stats builds. // Increment vm_insns_count for --yjit-stats. We increment this even when // --yjit or --yjit-stats is not used because branching to skip it is slower. // We also don't use ATOMIC_INC for performance, allowing inaccuracy on Ractors. diff --git a/vm_method.c b/vm_method.c index 84d0ed2f9e4c07..fa81d56c74119d 100644 --- a/vm_method.c +++ b/vm_method.c @@ -148,6 +148,7 @@ rb_clear_constant_cache_for_id(ID id) } rb_yjit_constant_state_changed(id); + rb_zjit_constant_state_changed(id); } static void diff --git a/yjit.c b/yjit.c index b3364ff6065515..46f89e2020c3f1 100644 --- a/yjit.c +++ b/yjit.c @@ -23,13 +23,13 @@ #include "insns_info.inc" #include "vm_sync.h" #include "yjit.h" +#include "zjit.h" #include "vm_insnhelper.h" #include "probes.h" #include "probes_helper.h" #include "iseq.h" #include "ruby/debug.h" #include "internal/cont.h" -#include "zjit.h" // For mmapp(), sysconf() #ifndef _WIN32 diff --git a/yjit.h b/yjit.h index cb96ee78382c63..645f0af49d8fd3 100644 --- a/yjit.h +++ b/yjit.h @@ -9,10 +9,9 @@ #include "vm_core.h" #include "method.h" -// YJIT_STATS controls whether to support runtime counters in generated code -// and in the interpreter. +// YJIT_STATS controls whether to support runtime counters in the interpreter #ifndef YJIT_STATS -# define YJIT_STATS RUBY_DEBUG +# define YJIT_STATS (USE_YJIT && RUBY_DEBUG) #endif #if USE_YJIT diff --git a/yjit.rb b/yjit.rb index e8ba3cdd28752a..e4fafa729eea75 100644 --- a/yjit.rb +++ b/yjit.rb @@ -64,7 +64,6 @@ def self.enable(stats: false, log: false, mem_size: nil, call_threshold: nil) end at_exit { print_and_dump_stats } if stats - call_yjit_hooks Primitive.rb_yjit_enable(stats, stats != :quiet, log, log != :quiet, mem_size, call_threshold) end diff --git a/yjit/bindgen/src/main.rs b/yjit/bindgen/src/main.rs index 7c8ce7bce5c69d..61b6f233269269 100644 --- a/yjit/bindgen/src/main.rs +++ b/yjit/bindgen/src/main.rs @@ -105,6 +105,9 @@ fn main() { .allowlist_var("SHAPE_ID_NUM_BITS") .allowlist_var("SHAPE_ID_HAS_IVAR_MASK") + // From ruby/internal/eval.h + .allowlist_function("rb_funcall") + // From ruby/internal/intern/object.h .allowlist_function("rb_obj_is_kind_of") .allowlist_function("rb_obj_frozen_p") @@ -269,6 +272,7 @@ fn main() { .allowlist_function("rb_float_new") // From vm_core.h + .allowlist_var("rb_cRubyVM") .allowlist_var("rb_mRubyVMFrozenCore") .allowlist_var("VM_BLOCK_HANDLER_NONE") .allowlist_type("vm_frame_env_flags") @@ -383,6 +387,7 @@ fn main() { .allowlist_function("rb_ivar_defined") .allowlist_function("rb_ivar_get") .allowlist_function("rb_mod_name") + .allowlist_function("rb_const_get") // From internal/vm.h .allowlist_var("rb_vm_insns_count") diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 725a29fa70cf23..f7a08b3b18b537 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -601,9 +601,15 @@ pub fn rust_str_to_ruby(str: &str) -> VALUE { /// Produce a Ruby symbol from a Rust string slice pub fn rust_str_to_sym(str: &str) -> VALUE { + let id = rust_str_to_id(str); + unsafe { rb_id2sym(id) } +} + +/// Produce an ID from a Rust string slice +pub fn rust_str_to_id(str: &str) -> ID { let c_str = CString::new(str).unwrap(); let c_ptr: *const c_char = c_str.as_ptr(); - unsafe { rb_id2sym(rb_intern(c_ptr)) } + unsafe { rb_intern(c_ptr) } } /// Produce an owned Rust String from a C char pointer diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 666f5c4f28d614..eeabbf594df9bb 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -405,7 +405,6 @@ pub const imemo_memo: imemo_type = 5; pub const imemo_ment: imemo_type = 6; pub const imemo_iseq: imemo_type = 7; pub const imemo_tmpbuf: imemo_type = 8; -pub const imemo_ast: imemo_type = 9; pub const imemo_callinfo: imemo_type = 10; pub const imemo_callcache: imemo_type = 11; pub const imemo_constcache: imemo_type = 12; @@ -1019,6 +1018,7 @@ extern "C" { pub fn rb_gc_location(obj: VALUE) -> VALUE; pub fn rb_gc_writebarrier(old: VALUE, young: VALUE); pub fn rb_class_get_superclass(klass: VALUE) -> VALUE; + pub fn rb_funcall(recv: VALUE, mid: ID, n: ::std::os::raw::c_int, ...) -> VALUE; pub static mut rb_mKernel: VALUE; pub static mut rb_cBasicObject: VALUE; pub static mut rb_cArray: VALUE; @@ -1080,6 +1080,7 @@ extern "C" { pub fn rb_ivar_get(obj: VALUE, name: ID) -> VALUE; pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE; pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE; + pub fn rb_const_get(space: VALUE, name: ID) -> VALUE; pub fn rb_obj_info_dump(obj: VALUE); pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE; pub fn rb_obj_equal(obj1: VALUE, obj2: VALUE) -> VALUE; @@ -1102,6 +1103,7 @@ extern "C" { klass: VALUE, id: ID, ) -> *const rb_callable_method_entry_t; + pub static mut rb_cRubyVM: VALUE; pub static mut rb_mRubyVMFrozenCore: VALUE; pub static mut rb_block_param_proxy: VALUE; pub fn rb_vm_ep_local_ep(ep: *const VALUE) -> *const VALUE; diff --git a/yjit/src/yjit.rs b/yjit/src/yjit.rs index 4b2b5214f4da13..8df1163d64b725 100644 --- a/yjit/src/yjit.rs +++ b/yjit/src/yjit.rs @@ -54,6 +54,12 @@ fn yjit_init() { // TODO: need to make sure that command-line options have been // initialized by CRuby + // Call YJIT hooks before enabling YJIT to avoid compiling the hooks themselves + unsafe { + let yjit = rb_const_get(rb_cRubyVM, rust_str_to_id("YJIT")); + rb_funcall(yjit, rust_str_to_id("call_yjit_hooks"), 0); + } + // Catch panics to avoid UB for unwinding into C frames. // See https://doc.rust-lang.org/nomicon/exception-safety.html let result = std::panic::catch_unwind(|| { diff --git a/yjit_hook.rb b/yjit_hook.rb index 8f0f38aaf1f535..610a7be3303e2c 100644 --- a/yjit_hook.rb +++ b/yjit_hook.rb @@ -1,8 +1,3 @@ -# If YJIT is enabled, load the YJIT-only version of builtin methods -if defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled? - RubyVM::YJIT.send(:call_yjit_hooks) -end - # Remove the helper defined in kernel.rb class Module undef :with_yjit diff --git a/zjit.c b/zjit.c index 61c17d32c30751..abe74225404c98 100644 --- a/zjit.c +++ b/zjit.c @@ -15,6 +15,7 @@ #include "builtin.h" #include "insns.inc" #include "insns_info.inc" +#include "zjit.h" #include "vm_sync.h" #include "vm_insnhelper.h" #include "probes.h" @@ -22,7 +23,6 @@ #include "iseq.h" #include "ruby/debug.h" #include "internal/cont.h" -#include "zjit.h" // For mmapp(), sysconf() #ifndef _WIN32 @@ -331,9 +331,6 @@ rb_iseq_set_zjit_payload(const rb_iseq_t *iseq, void *payload) iseq->body->zjit_payload = payload; } -// Primitives used by zjit.rb -VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self); - void rb_zjit_print_exception(void) { @@ -349,5 +346,10 @@ rb_zjit_shape_obj_too_complex_p(VALUE obj) return rb_shape_obj_too_complex_p(obj); } +// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them. +VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self); +VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self); +VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self); + // Preprocessed zjit.rb generated during build #include "zjit.rbinc" diff --git a/zjit.h b/zjit.h index 724ae4abd05b15..5ce2826d067a2b 100644 --- a/zjit.h +++ b/zjit.h @@ -4,6 +4,11 @@ // This file contains definitions ZJIT exposes to the CRuby codebase // +// ZJIT_STATS controls whether to support runtime counters in the interpreter +#ifndef ZJIT_STATS +# define ZJIT_STATS (USE_ZJIT && RUBY_DEBUG) +#endif + #if USE_ZJIT extern bool rb_zjit_enabled_p; extern uint64_t rb_zjit_call_threshold; @@ -14,6 +19,7 @@ void rb_zjit_profile_enable(const rb_iseq_t *iseq); void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop); void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme); void rb_zjit_invalidate_ep_is_bp(const rb_iseq_t *iseq); +void rb_zjit_constant_state_changed(ID id); void rb_zjit_iseq_mark(void *payload); void rb_zjit_iseq_update_references(void *payload); #else @@ -24,6 +30,7 @@ static inline void rb_zjit_profile_enable(const rb_iseq_t *iseq) {} static inline void rb_zjit_bop_redefined(int redefined_flag, enum ruby_basic_operators bop) {} static inline void rb_zjit_cme_invalidate(const rb_callable_method_entry_t *cme) {} static inline void rb_zjit_invalidate_ep_is_bp(const rb_iseq_t *iseq) {} +static inline void rb_zjit_constant_state_changed(ID id) {} #endif // #if USE_YJIT #endif // #ifndef ZJIT_H diff --git a/zjit.rb b/zjit.rb index fd58c1c94a097a..2bc779ef2895ec 100644 --- a/zjit.rb +++ b/zjit.rb @@ -1,6 +1,61 @@ +# frozen_string_literal: true + +# 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 +# for which CRuby is built. module RubyVM::ZJIT + # Avoid calling a Ruby method here to avoid interfering with compilation tests + if Primitive.rb_zjit_stats_enabled_p + at_exit { print_stats } + end +end + +class << RubyVM::ZJIT + # Return ZJIT statistics as a Hash + def stats + stats = Primitive.rb_zjit_stats + + if stats.key?(:vm_insns_count) && stats.key?(:zjit_insns_count) + stats[:total_insns_count] = stats[:vm_insns_count] + stats[:zjit_insns_count] + stats[:ratio_in_zjit] = 100.0 * stats[:zjit_insns_count] / stats[:total_insns_count] + end + + stats + end + + # Get the summary of ZJIT statistics as a String + def stats_string + buf = +'' + stats = self.stats + + [ + :total_insns_count, + :vm_insns_count, + :zjit_insns_count, + :ratio_in_zjit, + ].each do |key| + value = stats[key] + if key == :ratio_in_zjit + value = '%0.1f%%' % value + end + buf << "#{'%-18s' % "#{key}:"} #{value}\n" + end + buf + end + # Assert that any future ZJIT compilation will return a function pointer - def self.assert_compiles + def assert_compiles # :nodoc: Primitive.rb_zjit_assert_compiles end + + # :stopdoc: + private + + # Print ZJIT stats + def print_stats + $stderr.write stats_string + end end diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index cb15c3657e37e1..f67d8e91d31a62 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -392,6 +392,9 @@ fn main() { .allowlist_function("rb_ivar_set") .allowlist_function("rb_mod_name") + // From internal/vm.h + .allowlist_var("rb_vm_insns_count") + // From include/ruby/internal/intern/vm.h .allowlist_function("rb_get_alloc_func") diff --git a/zjit/src/asm/arm64/opnd.rs b/zjit/src/asm/arm64/opnd.rs index 28422b747652d8..a77958f7e6eeec 100644 --- a/zjit/src/asm/arm64/opnd.rs +++ b/zjit/src/asm/arm64/opnd.rs @@ -119,6 +119,9 @@ pub const X20_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 20 }; pub const X21_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 21 }; pub const X22_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 22 }; +// frame pointer (base pointer) +pub const X29_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 29 }; + // link register pub const X30_REG: A64Reg = A64Reg { num_bits: 64, reg_no: 30 }; diff --git a/zjit/src/backend/arm64/mod.rs b/zjit/src/backend/arm64/mod.rs index 88ccad8e091ed1..42dc31c90fd5cc 100644 --- a/zjit/src/backend/arm64/mod.rs +++ b/zjit/src/backend/arm64/mod.rs @@ -29,6 +29,7 @@ pub const C_ARG_OPNDS: [Opnd; 6] = [ pub const C_RET_REG: Reg = X0_REG; pub const C_RET_OPND: Opnd = Opnd::Reg(X0_REG); pub const NATIVE_STACK_PTR: Opnd = Opnd::Reg(XZR_REG); +pub const NATIVE_BASE_PTR: Opnd = Opnd::Reg(X29_REG); // These constants define the way we work with Arm64's stack pointer. The stack // pointer always needs to be aligned to a 16-byte boundary. @@ -911,18 +912,54 @@ impl Assembler cb.write_byte(0); } }, - Insn::FrameSetup => { + &Insn::FrameSetup { preserved, mut slot_count } => { + const { assert!(SIZEOF_VALUE == 8, "alignment logic relies on SIZEOF_VALUE == 8"); } + // Preserve X29 and set up frame record stp_pre(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, -16)); - - // X29 (frame_pointer) = SP mov(cb, X29, C_SP_REG); - }, - Insn::FrameTeardown => { + + for regs in preserved.chunks(2) { + // For the body, store pairs and move SP + if let [reg0, reg1] = regs { + stp_pre(cb, reg1.into(), reg0.into(), A64Opnd::new_mem(128, C_SP_REG, -16)); + } else if let [reg] = regs { + // For overhang, store but don't move SP. Combine movement with + // movement for slots below. + stur(cb, reg.into(), A64Opnd::new_mem(64, C_SP_REG, -8)); + slot_count += 1; + } else { + unreachable!("chunks(2)"); + } + } + // Align slot_count + if slot_count % 2 == 1 { + slot_count += 1 + } + if slot_count > 0 { + let slot_offset = (slot_count * SIZEOF_VALUE) as u64; + // Bail when asked to reserve too many slots in one instruction. + ShiftedImmediate::try_from(slot_offset).ok()?; + sub(cb, C_SP_REG, C_SP_REG, A64Opnd::new_uimm(slot_offset)); + } + } + Insn::FrameTeardown { preserved } => { + // Restore preserved registers below frame pointer. + let mut base_offset = 0; + for regs in preserved.chunks(2) { + if let [reg0, reg1] = regs { + base_offset -= 16; + ldp(cb, reg1.into(), reg0.into(), A64Opnd::new_mem(128, X29, base_offset)); + } else if let [reg] = regs { + ldur(cb, reg.into(), A64Opnd::new_mem(64, X29, base_offset - 8)); + } else { + unreachable!("chunks(2)"); + } + } + // SP = X29 (frame pointer) mov(cb, C_SP_REG, X29); - ldp_post(cb, X29, X30, A64Opnd::new_mem(128, C_SP_REG, 16)); - }, + } Insn::Add { left, right, out } => { // Usually, we issue ADDS, so you could branch on overflow, but ADDS with // out=31 refers to out=XZR, which discards the sum. So, instead of ADDS @@ -1482,11 +1519,73 @@ mod tests { fn test_emit_frame() { let (mut asm, mut cb) = setup_asm(); - asm.frame_setup(); - asm.frame_teardown(); + asm.frame_setup(&[], 0); + asm.frame_teardown(&[]); asm.compile_with_num_regs(&mut cb, 0); } + #[test] + fn frame_setup_and_teardown() { + const THREE_REGS: &'static [Opnd] = &[Opnd::Reg(X19_REG), Opnd::Reg(X20_REG), Opnd::Reg(X21_REG)]; + // Test 3 preserved regs (odd), odd slot_count + { + let (mut asm, mut cb) = setup_asm(); + asm.frame_setup(THREE_REGS, 3); + asm.frame_teardown(THREE_REGS); + asm.compile_with_num_regs(&mut cb, 0); + assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f5831ff8ff8300d1b44f7fa9b5835ef8bf030091fd7bc1a8", " + 0x0: stp x29, x30, [sp, #-0x10]! + 0x4: mov x29, sp + 0x8: stp x20, x19, [sp, #-0x10]! + 0xc: stur x21, [sp, #-8] + 0x10: sub sp, sp, #0x20 + 0x14: ldp x20, x19, [x29, #-0x10] + 0x18: ldur x21, [x29, #-0x18] + 0x1c: mov sp, x29 + 0x20: ldp x29, x30, [sp], #0x10 + "); + } + + // Test 3 preserved regs (odd), even slot_count + { + let (mut asm, mut cb) = setup_asm(); + asm.frame_setup(THREE_REGS, 4); + asm.frame_teardown(THREE_REGS); + asm.compile_with_num_regs(&mut cb, 0); + assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f5831ff8ffc300d1b44f7fa9b5835ef8bf030091fd7bc1a8", " + 0x0: stp x29, x30, [sp, #-0x10]! + 0x4: mov x29, sp + 0x8: stp x20, x19, [sp, #-0x10]! + 0xc: stur x21, [sp, #-8] + 0x10: sub sp, sp, #0x30 + 0x14: ldp x20, x19, [x29, #-0x10] + 0x18: ldur x21, [x29, #-0x18] + 0x1c: mov sp, x29 + 0x20: ldp x29, x30, [sp], #0x10 + "); + } + + // Test 4 preserved regs (even), odd slot_count + { + static FOUR_REGS: &'static [Opnd] = &[Opnd::Reg(X19_REG), Opnd::Reg(X20_REG), Opnd::Reg(X21_REG), Opnd::Reg(X22_REG)]; + let (mut asm, mut cb) = setup_asm(); + asm.frame_setup(FOUR_REGS, 3); + asm.frame_teardown(FOUR_REGS); + asm.compile_with_num_regs(&mut cb, 0); + assert_disasm!(cb, "fd7bbfa9fd030091f44fbfa9f657bfa9ff8300d1b44f7fa9b6577ea9bf030091fd7bc1a8", " + 0x0: stp x29, x30, [sp, #-0x10]! + 0x4: mov x29, sp + 0x8: stp x20, x19, [sp, #-0x10]! + 0xc: stp x22, x21, [sp, #-0x10]! + 0x10: sub sp, sp, #0x20 + 0x14: ldp x20, x19, [x29, #-0x10] + 0x18: ldp x22, x21, [x29, #-0x20] + 0x1c: mov sp, x29 + 0x20: ldp x29, x30, [sp], #0x10 + "); + } + } + #[test] fn test_emit_je_fits_into_bcond() { let (mut asm, mut cb) = setup_asm(); diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index 7bac210bee6689..36e783bd4e658a 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -12,10 +12,12 @@ use crate::asm::{CodeBlock, Label}; pub use crate::backend::current::{ Reg, EC, CFP, SP, - NATIVE_STACK_PTR, + NATIVE_STACK_PTR, NATIVE_BASE_PTR, C_ARG_OPNDS, C_RET_REG, C_RET_OPND, }; +pub static JIT_PRESERVED_REGS: &'static [Opnd] = &[CFP, SP, EC]; + // Memory operand base #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum MemBase @@ -291,8 +293,6 @@ pub enum Target context: Option, /// We use this to enrich asm comments. reason: SideExitReason, - /// The number of bytes we need to adjust the C stack pointer by. - c_stack_bytes: usize, /// Some if the side exit should write this label. We use it for patch points. label: Option