From 354d47ae5bc4edcc94db4a5391ed71a8b9844e57 Mon Sep 17 00:00:00 2001 From: Aleksey Maximov Date: Sun, 14 Sep 2025 12:30:09 +0000 Subject: [PATCH 01/13] IBF: Avoid unaligned load on 32 bit platforms [Bug #21569] --- compile.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compile.c b/compile.c index d3b9704cc94d69..bb7f974b6b23d0 100644 --- a/compile.c +++ b/compile.c @@ -14097,8 +14097,10 @@ ibf_dump_object_float(struct ibf_dump *dump, VALUE obj) static VALUE ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset) { - const double *dblp = IBF_OBJBODY(double, offset); - return DBL2NUM(*dblp); + double d; + /* Avoid unaligned VFP load on ARMv7; IBF payload may be unaligned (C99 6.3.2.3 p7). */ + memcpy(&d, IBF_OBJBODY(double, offset), sizeof(d)); + return DBL2NUM(d); } static void From 50393d1ada756fa03febb6566b6820bb1a37036c Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 15:20:51 -0400 Subject: [PATCH 02/13] IBF: Remove unnecessary and potentially UB pointer cast [Bug #21569] --- compile.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compile.c b/compile.c index bb7f974b6b23d0..cb8554f0a17fd3 100644 --- a/compile.c +++ b/compile.c @@ -14002,7 +14002,11 @@ struct ibf_object_symbol { #define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \ ((((offset) - 1) / (align) + 1) * (align)) -#define IBF_OBJBODY(type, offset) (const type *)\ +/* No cast, since it's UB to create an unaligned pointer. + * Leave as void* for use with memcpy in those cases. + * We align the offset, but the buffer pointer is only VALUE aligned, + * so the returned pointer may be unaligned for `type` .*/ +#define IBF_OBJBODY(type, offset) \ ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset)) static const void * From 62430c19c9f1ab49429cebe65f30588472648c95 Mon Sep 17 00:00:00 2001 From: Luke Gruber Date: Mon, 18 Aug 2025 10:47:40 -0400 Subject: [PATCH 03/13] Properly unlock locked mutexes on thread cleanup. Mutexes were being improperly unlocked on thread cleanup. This bug was introduced in 050a8954395. We must keep a reference from the mutex to the thread, because if the fiber is collected before the mutex is, then we cannot unlink it from the thread in `mutex_free`. If it's not unlinked from the the thread when it's freed, it causes bugs in `rb_thread_unlock_all_locking_mutexes`. We now mark the fiber when a mutex is locked, and the thread is marked as well. However, a fiber can still be freed in the same GC cycle as the mutex, so the reference to the thread is still needed. The reason we need to mark the fiber is that `mutex_owned_p()` has an ABA issue where if the fiber is collected while it's locked, a new fiber could be allocated at the same memory address and we could get false positives. Fixes [Bug #21342] Co-authored-by: John Hawthorn --- test/ruby/test_thread.rb | 61 +++++++++++++++++++++++++++++++++ thread.c | 2 +- thread_sync.c | 73 +++++++++++++++++++++++++++++++--------- 3 files changed, 120 insertions(+), 16 deletions(-) diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 12ba1165ed8817..01a1e51025181c 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -1589,4 +1589,65 @@ def frame_for_deadlock_test_2 frame_for_deadlock_test_2 { t.join } INPUT end + + # [Bug #21342] + def test_unlock_locked_mutex_with_collected_fiber + bug21127 = '[ruby-core:120930] [Bug #21127]' + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + 5.times do + m = Mutex.new + Thread.new do + m.synchronize do + end + end.join + Fiber.new do + GC.start + m.lock + end.resume + end + end; + end + + def test_unlock_locked_mutex_with_collected_fiber2 + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + MUTEXES = [] + 5.times do + m = Mutex.new + Fiber.new do + GC.start + m.lock + end.resume + MUTEXES << m + end + 10.times do + MUTEXES.clear + GC.start + end + end; + end + + def test_mutexes_locked_in_fiber_dont_have_aba_issue_with_new_fibers + assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}") + begin; + mutexes = 1000.times.map do + Mutex.new + end + + mutexes.map do |m| + Fiber.new do + m.lock + end.resume + end + + GC.start + + 1000.times.map do + Fiber.new do + raise "FAILED!" if mutexes.any?(&:owned?) + end.resume + end + end; + end end diff --git a/thread.c b/thread.c index bab615b9edb27b..55561001023dfe 100644 --- a/thread.c +++ b/thread.c @@ -442,7 +442,7 @@ rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th) th->keeping_mutexes = mutex->next_mutex; // rb_warn("mutex #<%p> was not unlocked by thread #<%p>", (void *)mutex, (void*)th); - + VM_ASSERT(mutex->fiber); const char *error_message = rb_mutex_unlock_th(mutex, th, mutex->fiber); if (error_message) rb_bug("invalid keeping_mutexes: %s", error_message); } diff --git a/thread_sync.c b/thread_sync.c index dd54e8267195cd..0fc70224ff90ed 100644 --- a/thread_sync.c +++ b/thread_sync.c @@ -8,6 +8,7 @@ static VALUE rb_eClosedQueueError; /* Mutex */ typedef struct rb_mutex_struct { rb_fiber_t *fiber; + VALUE thread; // even if the fiber is collected, we might need access to the thread in mutex_free struct rb_mutex_struct *next_mutex; struct ccan_list_head waitq; /* protected by GVL */ } rb_mutex_t; @@ -106,8 +107,6 @@ static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_fib * */ -#define mutex_mark ((void(*)(void*))0) - static size_t rb_mutex_num_waiting(rb_mutex_t *mutex) { @@ -123,13 +122,39 @@ rb_mutex_num_waiting(rb_mutex_t *mutex) rb_thread_t* rb_fiber_threadptr(const rb_fiber_t *fiber); +static bool +locked_p(rb_mutex_t *mutex) +{ + return mutex->fiber != 0; +} + +static void +mutex_mark(void *ptr) +{ + rb_mutex_t *mutex = ptr; + VALUE fiber; + if (locked_p(mutex)) { + fiber = rb_fiberptr_self(mutex->fiber); // rb_fiber_t* doesn't move along with fiber object + if (fiber) rb_gc_mark_movable(fiber); + rb_gc_mark_movable(mutex->thread); + } +} + +static void +mutex_compact(void *ptr) +{ + rb_mutex_t *mutex = ptr; + if (locked_p(mutex)) { + mutex->thread = rb_gc_location(mutex->thread); + } +} + static void mutex_free(void *ptr) { rb_mutex_t *mutex = ptr; - if (mutex->fiber) { - /* rb_warn("free locked mutex"); */ - const char *err = rb_mutex_unlock_th(mutex, rb_fiber_threadptr(mutex->fiber), mutex->fiber); + if (locked_p(mutex)) { + const char *err = rb_mutex_unlock_th(mutex, rb_thread_ptr(mutex->thread), mutex->fiber); if (err) rb_bug("%s", err); } ruby_xfree(ptr); @@ -143,7 +168,7 @@ mutex_memsize(const void *ptr) static const rb_data_type_t mutex_data_type = { "mutex", - {mutex_mark, mutex_free, mutex_memsize,}, + {mutex_mark, mutex_free, mutex_memsize, mutex_compact,}, 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY }; @@ -204,12 +229,13 @@ rb_mutex_locked_p(VALUE self) { rb_mutex_t *mutex = mutex_ptr(self); - return RBOOL(mutex->fiber); + return RBOOL(locked_p(mutex)); } static void thread_mutex_insert(rb_thread_t *thread, rb_mutex_t *mutex) { + RUBY_ASSERT(!mutex->next_mutex); if (thread->keeping_mutexes) { mutex->next_mutex = thread->keeping_mutexes; } @@ -234,10 +260,24 @@ thread_mutex_remove(rb_thread_t *thread, rb_mutex_t *mutex) } static void -mutex_locked(rb_thread_t *th, VALUE self) +mutex_set_owner(VALUE self, rb_thread_t *th, rb_fiber_t *fiber) +{ + rb_mutex_t *mutex = mutex_ptr(self); + + mutex->thread = th->self; + mutex->fiber = fiber; + RB_OBJ_WRITTEN(self, Qundef, th->self); + if (fiber) { + RB_OBJ_WRITTEN(self, Qundef, rb_fiberptr_self(fiber)); + } +} + +static void +mutex_locked(rb_thread_t *th, rb_fiber_t *fiber, VALUE self) { rb_mutex_t *mutex = mutex_ptr(self); + mutex_set_owner(self, th, fiber); thread_mutex_insert(th, mutex); } @@ -258,9 +298,8 @@ rb_mutex_trylock(VALUE self) rb_fiber_t *fiber = GET_EC()->fiber_ptr; rb_thread_t *th = GET_THREAD(); - mutex->fiber = fiber; - mutex_locked(th, self); + mutex_locked(th, fiber, self); return Qtrue; } else { @@ -328,7 +367,7 @@ do_mutex_lock(VALUE self, int interruptible_p) rb_ensure(call_rb_fiber_scheduler_block, self, delete_from_waitq, (VALUE)&sync_waiter); if (!mutex->fiber) { - mutex->fiber = fiber; + mutex_set_owner(self, th, fiber); } } else { @@ -358,6 +397,7 @@ do_mutex_lock(VALUE self, int interruptible_p) rb_ractor_sleeper_threads_inc(th->ractor); rb_check_deadlock(th->ractor); + RUBY_ASSERT(!th->locking_mutex); th->locking_mutex = self; ccan_list_add_tail(&mutex->waitq, &sync_waiter.node); @@ -368,7 +408,7 @@ do_mutex_lock(VALUE self, int interruptible_p) // unlocked by another thread while sleeping if (!mutex->fiber) { - mutex->fiber = fiber; + mutex_set_owner(self, th, fiber); } rb_ractor_sleeper_threads_dec(th->ractor); @@ -381,10 +421,13 @@ do_mutex_lock(VALUE self, int interruptible_p) if (interruptible_p) { /* release mutex before checking for interrupts...as interrupt checking * code might call rb_raise() */ - if (mutex->fiber == fiber) mutex->fiber = 0; + if (mutex->fiber == fiber) { + mutex->thread = Qfalse; + mutex->fiber = NULL; + } RUBY_VM_CHECK_INTS_BLOCKING(th->ec); /* may release mutex */ if (!mutex->fiber) { - mutex->fiber = fiber; + mutex_set_owner(self, th, fiber); } } else { @@ -403,7 +446,7 @@ do_mutex_lock(VALUE self, int interruptible_p) } if (saved_ints) th->ec->interrupt_flag = saved_ints; - if (mutex->fiber == fiber) mutex_locked(th, self); + if (mutex->fiber == fiber) mutex_locked(th, fiber, self); } RUBY_DEBUG_LOG("%p locked", mutex); From 1a52c42e61878a1fe1d411a74108607766183b10 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 18:30:12 -0400 Subject: [PATCH 04/13] Always use assert-free APIs when profiling and crashing rb_profile_frames() is used by profilers in a way such that it can run on any instruction in the binary, and it crashed previously in the following situation in `RUBY_DEBUG` builds: ``` * thread #1, queue = 'com.apple.main-thread', stop reason = step over frame #0: 0x00000001002827f0 miniruby`vm_make_env_each(ec=0x0000000101866b00, cfp=0x000000080c91bee8) at vm.c:992:74 989 } 990 991 vm_make_env_each(ec, prev_cfp); -> 992 VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep)); 993 } 994 } 995 else { (lldb) call rb_profile_frames(0, 100, $2, $3) /Users/alan/ruby/vm_core.h:1448: Assertion Failed: VM_ENV_FLAGS:FIXNUM_P(flags) ruby 3.5.0dev (2025-09-23T20:20:04Z master 06b7a70837) +PRISM [arm64-darwin25] -- Crash Report log information -------------------------------------------- See Crash Report log file in one of the following locations: * ~/Library/Logs/DiagnosticReports * /Library/Logs/DiagnosticReports for more details. Don't forget to include the above Crash Report log file in bug reports. -- Control frame information ----------------------------------------------- c:0008 p:---- s:0029 e:000028 CFUNC :lambda /Users/alan/ruby/vm_core.h:1448: Assertion Failed: VM_ENV_FLAGS:FIXNUM_P(flags) ruby 3.5.0dev (2025-09-23T20:20:04Z master 06b7a70837) +PRISM [arm64-darwin25] -- Crash Report log information -------------------------------------------- ``` There is a small window where the control frame is invalid and fails the assert. The double crash also shows that in `RUBY_DEBUG` builds, the crash reporter was previously not resilient to corrupt frame state. In release builds, it prints more info. Add unchecked APIs for the crash reporter and profilers so they work as well in `RUBY_DEBUG` builds as non-debug builds. --- vm_backtrace.c | 8 +++++--- vm_core.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- vm_dump.c | 12 +++++++----- vm_insnhelper.c | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 9 deletions(-) diff --git a/vm_backtrace.c b/vm_backtrace.c index e81c568dda2e6b..07d2e33e321787 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -1745,14 +1745,14 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp); for (i=0; ipc != 0) { + if (VM_FRAME_RUBYFRAME_P_UNCHECKED(cfp) && cfp->pc != 0) { if (start > 0) { start--; continue; } /* record frame info */ - cme = rb_vm_frame_method_entry(cfp); + cme = rb_vm_frame_method_entry_unchecked(cfp); if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) { buff[i] = (VALUE)cme; } @@ -1770,6 +1770,8 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b // before entering a non-leaf method (so that `caller` will work), // so only the topmost frame could possibly have an out-of-date PC. // ZJIT doesn't set `cfp->jit_return`, so it's not a reliable signal. + // TODO(zjit): lightweight frames potentially makes more than + // the top most frame invalid. // // Avoid passing invalid PC to calc_lineno() to avoid crashing. if (cfp == top && (pc < iseq_encoded || pc > pc_end)) { @@ -1783,7 +1785,7 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b i++; } else { - cme = rb_vm_frame_method_entry(cfp); + cme = rb_vm_frame_method_entry_unchecked(cfp); if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) { if (start > 0) { start--; diff --git a/vm_core.h b/vm_core.h index 2e77e1073eb959..487a4020eec5cd 100644 --- a/vm_core.h +++ b/vm_core.h @@ -1449,12 +1449,25 @@ VM_ENV_FLAGS(const VALUE *ep, long flag) return flags & flag; } +static inline unsigned long +VM_ENV_FLAGS_UNCHECKED(const VALUE *ep, long flag) +{ + VALUE flags = ep[VM_ENV_DATA_INDEX_FLAGS]; + return flags & flag; +} + static inline unsigned long VM_FRAME_TYPE(const rb_control_frame_t *cfp) { return VM_ENV_FLAGS(cfp->ep, VM_FRAME_MAGIC_MASK); } +static inline unsigned long +VM_FRAME_TYPE_UNCHECKED(const rb_control_frame_t *cfp) +{ + return VM_ENV_FLAGS_UNCHECKED(cfp->ep, VM_FRAME_MAGIC_MASK); +} + static inline int VM_FRAME_LAMBDA_P(const rb_control_frame_t *cfp) { @@ -1473,6 +1486,12 @@ VM_FRAME_FINISHED_P(const rb_control_frame_t *cfp) return VM_ENV_FLAGS(cfp->ep, VM_FRAME_FLAG_FINISH) != 0; } +static inline int +VM_FRAME_FINISHED_P_UNCHECKED(const rb_control_frame_t *cfp) +{ + return VM_ENV_FLAGS_UNCHECKED(cfp->ep, VM_FRAME_FLAG_FINISH) != 0; +} + static inline int VM_FRAME_BMETHOD_P(const rb_control_frame_t *cfp) { @@ -1498,12 +1517,24 @@ VM_FRAME_CFRAME_P(const rb_control_frame_t *cfp) return cframe_p; } +static inline int +VM_FRAME_CFRAME_P_UNCHECKED(const rb_control_frame_t *cfp) +{ + return VM_ENV_FLAGS_UNCHECKED(cfp->ep, VM_FRAME_FLAG_CFRAME) != 0; +} + static inline int VM_FRAME_RUBYFRAME_P(const rb_control_frame_t *cfp) { return !VM_FRAME_CFRAME_P(cfp); } +static inline int +VM_FRAME_RUBYFRAME_P_UNCHECKED(const rb_control_frame_t *cfp) +{ + return !VM_FRAME_CFRAME_P_UNCHECKED(cfp); +} + static inline int VM_FRAME_NS_SWITCH_P(const rb_control_frame_t *cfp) { @@ -1522,11 +1553,23 @@ VM_ENV_LOCAL_P(const VALUE *ep) return VM_ENV_FLAGS(ep, VM_ENV_FLAG_LOCAL) ? 1 : 0; } +static inline int +VM_ENV_LOCAL_P_UNCHECKED(const VALUE *ep) +{ + return VM_ENV_FLAGS_UNCHECKED(ep, VM_ENV_FLAG_LOCAL) ? 1 : 0; +} + +static inline const VALUE * +VM_ENV_PREV_EP_UNCHECKED(const VALUE *ep) +{ + return GC_GUARDED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL]); +} + static inline const VALUE * VM_ENV_PREV_EP(const VALUE *ep) { VM_ASSERT(VM_ENV_LOCAL_P(ep) == 0); - return GC_GUARDED_PTR_REF(ep[VM_ENV_DATA_INDEX_SPECVAL]); + return VM_ENV_PREV_EP_UNCHECKED(ep); } static inline VALUE @@ -1934,6 +1977,7 @@ void rb_gc_mark_machine_context(const rb_execution_context_t *ec); rb_cref_t *rb_vm_rewrite_cref(rb_cref_t *node, VALUE old_klass, VALUE new_klass); const rb_callable_method_entry_t *rb_vm_frame_method_entry(const rb_control_frame_t *cfp); +const rb_callable_method_entry_t *rb_vm_frame_method_entry_unchecked(const rb_control_frame_t *cfp); #define sysstack_error GET_VM()->special_exceptions[ruby_error_sysstack] diff --git a/vm_dump.c b/vm_dump.c index 2a863ddef955a0..131844b4cc1766 100644 --- a/vm_dump.c +++ b/vm_dump.c @@ -61,14 +61,14 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c const char *magic, *iseq_name = "-", *selfstr = "-", *biseq_name = "-"; VALUE tmp; const rb_iseq_t *iseq = NULL; - const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); + const rb_callable_method_entry_t *me = rb_vm_frame_method_entry_unchecked(cfp); if (ep < 0 || (size_t)ep > ec->vm_stack_size) { ep = (ptrdiff_t)cfp->ep; ep_in_heap = 'p'; } - switch (VM_FRAME_TYPE(cfp)) { + switch (VM_FRAME_TYPE_UNCHECKED(cfp)) { case VM_FRAME_MAGIC_TOP: magic = "TOP"; break; @@ -128,7 +128,9 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c iseq = cfp->iseq; pc = cfp->pc - ISEQ_BODY(iseq)->iseq_encoded; iseq_name = RSTRING_PTR(ISEQ_BODY(iseq)->location.label); - line = rb_vm_get_sourceline(cfp); + if (pc >= 0 && pc <= ISEQ_BODY(iseq)->iseq_size) { + line = rb_vm_get_sourceline(cfp); + } if (line) { snprintf(posbuf, MAX_POSBUF, "%s:%d", RSTRING_PTR(rb_iseq_path(iseq)), line); } @@ -138,7 +140,7 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c } } } - else if (me != NULL) { + else if (me != NULL && IMEMO_TYPE_P(me, imemo_ment)) { iseq_name = rb_id2name(me->def->original_id); snprintf(posbuf, MAX_POSBUF, ":%s", iseq_name); line = -1; @@ -158,7 +160,7 @@ control_frame_dump(const rb_execution_context_t *ec, const rb_control_frame_t *c if (line) { kprintf(" %s", posbuf); } - if (VM_FRAME_FINISHED_P(cfp)) { + if (VM_FRAME_FINISHED_P_UNCHECKED(cfp)) { kprintf(" [FINISH]"); } if (0) { diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 8022a29a6e77e2..75339aaa5c5abd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -761,6 +761,25 @@ check_method_entry(VALUE obj, int can_be_svar) } } +static rb_callable_method_entry_t * +env_method_entry_unchecked(VALUE obj, int can_be_svar) +{ + if (obj == Qfalse) return NULL; + + switch (imemo_type(obj)) { + case imemo_ment: + return (rb_callable_method_entry_t *)obj; + case imemo_cref: + return NULL; + case imemo_svar: + if (can_be_svar) { + return env_method_entry_unchecked(((struct vm_svar *)obj)->cref_or_me, FALSE); + } + default: + return NULL; + } +} + const rb_callable_method_entry_t * rb_vm_frame_method_entry(const rb_control_frame_t *cfp) { @@ -775,6 +794,20 @@ rb_vm_frame_method_entry(const rb_control_frame_t *cfp) return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE); } +const rb_callable_method_entry_t * +rb_vm_frame_method_entry_unchecked(const rb_control_frame_t *cfp) +{ + const VALUE *ep = cfp->ep; + rb_callable_method_entry_t *me; + + while (!VM_ENV_LOCAL_P_UNCHECKED(ep)) { + if ((me = env_method_entry_unchecked(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me; + ep = VM_ENV_PREV_EP_UNCHECKED(ep); + } + + return env_method_entry_unchecked(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE); +} + static const rb_iseq_t * method_entry_iseqptr(const rb_callable_method_entry_t *me) { From 00e6c10168596d4810f56430f18f778b66e30769 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 16:24:58 -0400 Subject: [PATCH 05/13] ZJIT: Standardize to `Iterator::map` in `Invariants::update_references` The old code was doing a manual HashSet/HashMap rebuild, and there isn't a clear performance advantage over `Iterator::map`. So let's use `map` since it looks clearer and it's easier to see that everything was indeed updated. This also adds assertions the old code did not have by way of as_iseq() and as_cme(). --- zjit/src/invariants.rs | 43 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 80948c696eb704..2e67c33a6df58c 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, mem}; -use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID, VALUE}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr}; +use crate::{backend::lir::{asm_comment, Assembler}, cruby::{iseq_name, rb_callable_method_entry_t, rb_gc_location, ruby_basic_operators, src_loc, with_vm_lock, IseqPtr, RedefinitionFlag, ID}, gc::IseqPayload, hir::Invariant, options::debug, state::{zjit_enabled_p, ZJITState}, virtualmem::CodePtr}; use crate::stats::with_time_stat; use crate::stats::Counter::invalidation_time_ns; use crate::gc::remove_gc_offsets; @@ -70,38 +70,23 @@ impl Invariants { /// Update ISEQ references in Invariants::ep_escape_iseqs fn update_ep_escape_iseqs(&mut self) { - let mut moved: Vec = Vec::with_capacity(self.ep_escape_iseqs.len()); - - self.ep_escape_iseqs.retain(|&old_iseq| { - let new_iseq = unsafe { rb_gc_location(VALUE(old_iseq as usize)) }.0 as IseqPtr; - if old_iseq != new_iseq { - moved.push(new_iseq); - } - old_iseq == new_iseq - }); - - for new_iseq in moved { - self.ep_escape_iseqs.insert(new_iseq); - } + let updated = std::mem::take(&mut self.ep_escape_iseqs) + .into_iter() + .map(|iseq| unsafe { rb_gc_location(iseq.into()) }.as_iseq()) + .collect(); + self.ep_escape_iseqs = updated; } /// Update ISEQ references in Invariants::no_ep_escape_iseq_patch_points fn update_no_ep_escape_iseq_patch_points(&mut self) { - let mut moved: Vec<(IseqPtr, HashSet)> = Vec::with_capacity(self.no_ep_escape_iseq_patch_points.len()); - let iseqs: Vec = self.no_ep_escape_iseq_patch_points.keys().cloned().collect(); - - for old_iseq in iseqs { - let new_iseq = unsafe { rb_gc_location(VALUE(old_iseq as usize)) }.0 as IseqPtr; - if old_iseq != new_iseq { - let patch_points = self.no_ep_escape_iseq_patch_points.remove(&old_iseq).unwrap(); - // Do not insert patch points to no_ep_escape_iseq_patch_points yet to avoid corrupting keys that had a different ISEQ - moved.push((new_iseq, patch_points)); - } - } - - for (new_iseq, patch_points) in moved { - self.no_ep_escape_iseq_patch_points.insert(new_iseq, patch_points); - } + let updated = std::mem::take(&mut self.no_ep_escape_iseq_patch_points) + .into_iter() + .map(|(iseq, patch_points)| { + let new_iseq = unsafe { rb_gc_location(iseq.into()) }; + (new_iseq.as_iseq(), patch_points) + }) + .collect(); + self.no_ep_escape_iseq_patch_points = updated; } } From adfa784eaa03a215f2d57ce7e219cc7504ecc68b Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 17:12:31 -0400 Subject: [PATCH 06/13] ZJIT: Forget about dead ISEQs in `Invariants` Without this, we crash during reference update. --- iseq.c | 3 +++ zjit.h | 1 + zjit/src/gc.rs | 11 +++++++++++ zjit/src/invariants.rs | 11 +++++++++++ 4 files changed, 26 insertions(+) diff --git a/iseq.c b/iseq.c index d8891353f1a365..082ea28c4ad199 100644 --- a/iseq.c +++ b/iseq.c @@ -174,6 +174,9 @@ rb_iseq_free(const rb_iseq_t *iseq) RUBY_ASSERT(rb_yjit_live_iseq_count > 0); rb_yjit_live_iseq_count--; } +#endif +#if USE_ZJIT + rb_zjit_iseq_free(iseq); #endif ruby_xfree((void *)body->iseq_encoded); ruby_xfree((void *)body->insns_info.body); diff --git a/zjit.h b/zjit.h index 9735cab6d459d0..24da476fd9eff3 100644 --- a/zjit.h +++ b/zjit.h @@ -22,6 +22,7 @@ void rb_zjit_invalidate_no_ep_escape(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); +void rb_zjit_iseq_free(const rb_iseq_t *iseq); void rb_zjit_before_ractor_spawn(void); void rb_zjit_tracing_invalidate_all(void); #else diff --git a/zjit/src/gc.rs b/zjit/src/gc.rs index bf07f1f340f7d6..f53536e2617d13 100644 --- a/zjit/src/gc.rs +++ b/zjit/src/gc.rs @@ -128,6 +128,17 @@ pub extern "C" fn rb_zjit_iseq_update_references(payload: *mut c_void) { with_time_stat(gc_time_ns, || iseq_update_references(payload)); } +/// GC callback for finalizing an ISEQ +#[unsafe(no_mangle)] +pub extern "C" fn rb_zjit_iseq_free(iseq: IseqPtr) { + if !ZJITState::has_instance() { + return; + } + // TODO(Shopify/ruby#682): Free `IseqPayload` + let invariants = ZJITState::get_invariants(); + invariants.forget_iseq(iseq); +} + /// GC callback for updating object references after all object moves #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_root_update_references() { diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 2e67c33a6df58c..cea97bc8ee7c9a 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -68,6 +68,17 @@ impl Invariants { self.update_no_ep_escape_iseq_patch_points(); } + /// Forget an ISEQ when freeing it. We need to because a) if the address is reused, we'd be + /// tracking the wrong object b) dead VALUEs in the table can means we risk passing invalid + /// VALUEs to `rb_gc_location()`. + pub fn forget_iseq(&mut self, iseq: IseqPtr) { + // Why not patch the patch points? If the ISEQ is dead then the GC also proved that all + // generated code referencing the ISEQ are unreachable. We mark the ISEQs baked into + // generated code. + self.ep_escape_iseqs.remove(&iseq); + self.no_ep_escape_iseq_patch_points.remove(&iseq); + } + /// Update ISEQ references in Invariants::ep_escape_iseqs fn update_ep_escape_iseqs(&mut self) { let updated = std::mem::take(&mut self.ep_escape_iseqs) From 6ea7557353982bb4b5de2dcd836233f328dc00f6 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 17:45:35 -0400 Subject: [PATCH 07/13] ZJIT: Reference update `Invariant::cme_patch_points` --- zjit/src/invariants.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index cea97bc8ee7c9a..473eec9e95df75 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -66,6 +66,7 @@ impl Invariants { pub fn update_references(&mut self) { self.update_ep_escape_iseqs(); self.update_no_ep_escape_iseq_patch_points(); + self.update_cme_patch_points(); } /// Forget an ISEQ when freeing it. We need to because a) if the address is reused, we'd be @@ -99,6 +100,17 @@ impl Invariants { .collect(); self.no_ep_escape_iseq_patch_points = updated; } + + fn update_cme_patch_points(&mut self) { + let updated_cme_patch_points = std::mem::take(&mut self.cme_patch_points) + .into_iter() + .map(|(cme, patch_points)| { + let new_cme = unsafe { rb_gc_location(cme.into()) }; + (new_cme.as_cme(), patch_points) + }) + .collect(); + self.cme_patch_points = updated_cme_patch_points; + } } /// Called when a basic operator is redefined. Note that all the blocks assuming From 63483e75b8189aa078d92456b8c942244d97b533 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 16:18:15 -0400 Subject: [PATCH 08/13] ZJIT: Actually call rb_zjit_root_update_references() Previously unused. --- depend | 1 + gc.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/depend b/depend index 5c3a03c96219d1..fa17e2ea6a43f6 100644 --- a/depend +++ b/depend @@ -5797,6 +5797,7 @@ gc.$(OBJEXT): {$(VPATH)}vm_debug.h gc.$(OBJEXT): {$(VPATH)}vm_opts.h gc.$(OBJEXT): {$(VPATH)}vm_sync.h gc.$(OBJEXT): {$(VPATH)}yjit.h +gc.$(OBJEXT): {$(VPATH)}zjit.h goruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h goruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h goruby.$(OBJEXT): $(CCAN_DIR)/list/list.h diff --git a/gc.c b/gc.c index da74c24c3f5e54..8c8887c46b7d78 100644 --- a/gc.c +++ b/gc.c @@ -127,6 +127,7 @@ #include "vm_callinfo.h" #include "ractor_core.h" #include "yjit.h" +#include "zjit.h" #include "builtin.h" #include "shape.h" @@ -4106,6 +4107,14 @@ rb_gc_update_vm_references(void *objspace) rb_yjit_root_update_references(); } #endif + +#if USE_ZJIT + void rb_zjit_root_update_references(void); // in Rust + + if (rb_zjit_enabled_p) { + rb_zjit_root_update_references(); + } +#endif } void From 328d2037a6bf24e26a7d144e80ab8c1a9c9fd868 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Thu, 25 Sep 2025 17:34:44 -0400 Subject: [PATCH 09/13] ZJIT: Remove dead CMEs from `Invariants` --- vm_method.c | 11 +++++++++++ zjit.h | 1 + zjit/src/gc.rs | 10 ++++++++++ zjit/src/invariants.rs | 5 +++++ 4 files changed, 27 insertions(+) diff --git a/vm_method.c b/vm_method.c index 238ad62f4b134b..16f402e893c8ed 100644 --- a/vm_method.c +++ b/vm_method.c @@ -859,6 +859,17 @@ rb_free_method_entry_vm_weak_references(const rb_method_entry_t *me) void rb_free_method_entry(const rb_method_entry_t *me) { +#if USE_ZJIT + if (METHOD_ENTRY_CACHED(me)) { + rb_zjit_cme_free((const rb_callable_method_entry_t *)me); + } +#endif + +#if USE_YJIT + // YJIT rb_yjit_root_mark() roots CMEs in `Invariants`, + // to remove from `Invariants` here. +#endif + rb_method_definition_release(me->def); } diff --git a/zjit.h b/zjit.h index 24da476fd9eff3..85f6e86d0350d4 100644 --- a/zjit.h +++ b/zjit.h @@ -18,6 +18,7 @@ void rb_zjit_profile_insn(uint32_t insn, rb_execution_context_t *ec); 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_cme_free(const rb_callable_method_entry_t *cme); void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq); void rb_zjit_constant_state_changed(ID id); void rb_zjit_iseq_mark(void *payload); diff --git a/zjit/src/gc.rs b/zjit/src/gc.rs index f53536e2617d13..aa87ff98423ab9 100644 --- a/zjit/src/gc.rs +++ b/zjit/src/gc.rs @@ -139,6 +139,16 @@ pub extern "C" fn rb_zjit_iseq_free(iseq: IseqPtr) { invariants.forget_iseq(iseq); } +/// GC callback for finalizing a CME +#[unsafe(no_mangle)] +pub extern "C" fn rb_zjit_cme_free(cme: *const rb_callable_method_entry_struct) { + if !ZJITState::has_instance() { + return; + } + let invariants = ZJITState::get_invariants(); + invariants.forget_cme(cme); +} + /// GC callback for updating object references after all object moves #[unsafe(no_mangle)] pub extern "C" fn rb_zjit_root_update_references() { diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 473eec9e95df75..75f900c38dffd8 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -80,6 +80,11 @@ impl Invariants { self.no_ep_escape_iseq_patch_points.remove(&iseq); } + /// Forget a CME when freeing it. See [Self::forget_iseq] for reasoning. + pub fn forget_cme(&mut self, cme: *const rb_callable_method_entry_t) { + self.cme_patch_points.remove(&cme); + } + /// Update ISEQ references in Invariants::ep_escape_iseqs fn update_ep_escape_iseqs(&mut self) { let updated = std::mem::take(&mut self.ep_escape_iseqs) From b600c95dc1b9aff1b49743be7d007649bc38c643 Mon Sep 17 00:00:00 2001 From: sodacris Date: Sat, 4 Jan 2025 10:48:46 +0800 Subject: [PATCH 10/13] [rubygems/rubygems] skip checking title on Windows because it's not supported https://github.com/rubygems/rubygems/commit/fd2c54f371 --- spec/bundler/commands/exec_spec.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index cad1ac4ba3ce83..49465fa07bfe50 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -730,7 +730,11 @@ def bin_path(a,b,c) puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}" puts "ARGS: \#{$0} \#{ARGV.join(' ')}" puts "MYRACK: \#{MYRACK}" - process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip + if Gem.win_platform? + process_title = "ruby" + else + process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip + end puts "PROCESS: \#{process_title}" RUBY @@ -748,9 +752,11 @@ def bin_path(a,b,c) let(:args) { "ARGS: #{path} arg1 arg2" } let(:myrack) { "MYRACK: 1.0.0" } let(:process) do - title = "PROCESS: #{path}" - title += " arg1 arg2" - title + if Gem.win_platform? + "PROCESS: ruby" + else + "PROCESS: #{path} arg1 arg2" + end end let(:exit_code) { 0 } let(:expected) { [exec, args, myrack, process].join("\n") } @@ -952,7 +958,13 @@ def bin_path(a,b,c) context "when disable_exec_load is set" do let(:exec) { "EXEC: exec" } - let(:process) { "PROCESS: ruby #{path} arg1 arg2" } + let(:process) do + if Gem.win_platform? + "PROCESS: ruby" + else + "PROCESS: ruby #{path} arg1 arg2" + end + end before do bundle "config set disable_exec_load true" From a1a1c9080ff5a948762da54962412c407304f41c Mon Sep 17 00:00:00 2001 From: sodacris Date: Mon, 18 Nov 2024 13:04:31 +0800 Subject: [PATCH 11/13] [rubygems/rubygems] add loading support on Windows https://github.com/rubygems/rubygems/commit/04574ba59a --- lib/bundler/cli/exec.rb | 33 ++++++++++++++++++++--- spec/bundler/commands/exec_spec.rb | 42 ++++++------------------------ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb index 9428e9db3bf8d7..2fdc4162868abe 100644 --- a/lib/bundler/cli/exec.rb +++ b/lib/bundler/cli/exec.rb @@ -19,11 +19,13 @@ def run validate_cmd! SharedHelpers.set_bundle_environment if bin_path = Bundler.which(cmd) - if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path) - return kernel_load(bin_path, *args) + if !Bundler.settings[:disable_exec_load] && directly_loadable?(bin_path) + bin_path.delete_suffix!(".bat") if Gem.win_platform? + kernel_load(bin_path, *args) + else + bin_path = "./" + bin_path unless File.absolute_path?(bin_path) + kernel_exec(bin_path, *args) end - bin_path = "./" + bin_path unless File.absolute_path?(bin_path) - kernel_exec(bin_path, *args) else # exec using the given command kernel_exec(cmd, *args) @@ -69,6 +71,29 @@ def process_title(file, args) "#{file} #{args.join(" ")}".strip end + def directly_loadable?(file) + if Gem.win_platform? + script_wrapper?(file) + else + ruby_shebang?(file) + end + end + + def script_wrapper?(file) + script_file = file.delete_suffix(".bat") + return false unless File.exist?(script_file) + + if File.zero?(script_file) + Bundler.ui.warn "#{script_file} is empty" + return false + end + + header = File.open(file, "r") {|f| f.read(32) } + ruby_exe = "#{RbConfig::CONFIG["RUBY_INSTALL_NAME"]}#{RbConfig::CONFIG["EXEEXT"]}" + ruby_exe = "ruby.exe" if ruby_exe.empty? + header.include?(ruby_exe) + end + def ruby_shebang?(file) possibilities = [ "#!/usr/bin/env ruby\n", diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 49465fa07bfe50..af8bfc71f19419 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -661,8 +661,6 @@ describe "with gems bundled for deployment" do it "works when calling bundler from another script" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - gemfile <<-G source "https://gem.repo1" @@ -739,8 +737,7 @@ def bin_path(a,b,c) RUBY before do - bundled_app(path).open("w") {|f| f << executable } - bundled_app(path).chmod(0o755) + create_file(bundled_app(path), executable) install_gemfile <<-G source "https://gem.repo1" @@ -765,8 +762,6 @@ def bin_path(a,b,c) subject { bundle "exec #{path} arg1 arg2", raise_on_error: false } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -778,8 +773,6 @@ def bin_path(a,b,c) context "with exit 0" do it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -791,8 +784,6 @@ def bin_path(a,b,c) let(:exit_code) { 99 } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -831,7 +822,12 @@ def bin_path(a,b,c) let(:expected) { "" } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + # it's empty, so `create_file` won't add executable permission and bat scripts on Windows + bundled_app(path).chmod(0o755) + path.sub_ext(".bat").write <<~SCRIPT if Gem.win_platform? + @ECHO OFF + @"ruby.exe" "%~dpn0" %* + SCRIPT subject expect(exitstatus).to eq(exit_code) @@ -844,12 +840,10 @@ def bin_path(a,b,c) let(:executable) { super() << "\nraise 'ERROR'" } let(:exit_code) { 1 } let(:expected_err) do - /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:10:in [`']': ERROR \(RuntimeError\)/ + /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:[0-9]+:in [`']': ERROR \(RuntimeError\)/ end it "runs like a normally executed executable" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to match(expected_err) @@ -864,8 +858,6 @@ def bin_path(a,b,c) let(:expected) { super() } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -877,8 +869,6 @@ def bin_path(a,b,c) let(:shebang) { "#!#{Gem.ruby}" } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -909,8 +899,6 @@ def bin_path(a,b,c) EOS it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -933,8 +921,6 @@ def bin_path(a,b,c) let(:expected) { "" } it "prints proper suggestion" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.") @@ -947,8 +933,6 @@ def bin_path(a,b,c) let(:exit_code) { 1 } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -971,8 +955,6 @@ def bin_path(a,b,c) end it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -995,8 +977,6 @@ def bin_path(a,b,c) EOS it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -1013,8 +993,6 @@ def bin_path(a,b,c) EOS it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -1031,8 +1009,6 @@ def bin_path(a,b,c) EOS it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -1173,8 +1149,6 @@ def require(path) context "when gemfile and path are configured", :ruby_repo do before do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - build_repo2 do build_gem "rails", "6.1.0" do |s| s.executables = "rails" From 61a0de1b652f8e1c79ef762ecf41bdea3f799d25 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 25 Sep 2025 16:57:23 -0700 Subject: [PATCH 12/13] ZJIT: Compile ISEQ with optional arguments (#14653) --- test/ruby/test_zjit.rb | 33 + zjit/src/codegen.rs | 61 +- zjit/src/cruby.rs | 9 +- zjit/src/gc.rs | 4 +- zjit/src/hir.rs | 1691 ++++++++++++++++++++++------------------ zjit/src/stats.rs | 27 +- 6 files changed, 1031 insertions(+), 794 deletions(-) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 564e62c070698f..683d53f339f8c2 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -308,6 +308,39 @@ def test_setlocal_on_eval } end + def test_optional_arguments + assert_compiles '[[1, 2, 3], [10, 20, 3], [100, 200, 300]]', %q{ + def test(a, b = 2, c = 3) + [a, b, c] + end + [test(1), test(10, 20), test(100, 200, 300)] + } + end + + def test_optional_arguments_setlocal + assert_compiles '[[2, 2], [1, nil]]', %q{ + def test(a = (b = 2)) + [a, b] + end + [test, test(1)] + } + end + + def test_optional_arguments_cyclic + assert_compiles '[nil, 1]', %q{ + test = proc { |a=a| a } + [test.call, test.call(1)] + } + end + + def test_optional_arguments_side_exit + # This leads to FailedOptionalArguments, so not using assert_compiles + assert_runs '[:foo, nil, 1]', %q{ + def test(a = (def foo = nil)) = a + [test, (undef :foo), test(1)] + } + end + def test_getblockparamproxy assert_compiles '1', %q{ def test(&block) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 682b7ea61d21bc..8230d39522504f 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -16,7 +16,7 @@ use crate::stats::{exit_counter_for_compile_error, incr_counter, incr_counter_by use crate::stats::{counter_ptr, with_time_stat, Counter, send_fallback_counter, Counter::{compile_time_ns, exit_compile_error}}; use crate::{asm::CodeBlock, cruby::*, options::debug, virtualmem::CodePtr}; use crate::backend::lir::{self, asm_comment, asm_ccall, Assembler, Opnd, Target, CFP, C_ARG_OPNDS, C_RET_OPND, EC, NATIVE_STACK_PTR, NATIVE_BASE_PTR, SCRATCH_OPND, SP}; -use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, Invariant, RangeType, SideExitReason, SideExitReason::*, MethodType, SpecialObjectType, SpecialBackrefSymbol, SELF_PARAM_IDX}; +use crate::hir::{iseq_to_hir, Block, BlockId, BranchEdge, Invariant, MethodType, RangeType, SideExitReason::{self, *}, SpecialBackrefSymbol, SpecialObjectType, SELF_PARAM_IDX}; use crate::hir::{Const, FrameState, Function, Insn, InsnId}; use crate::hir_type::{types, Type}; use crate::options::get_option; @@ -34,7 +34,7 @@ struct JITState { labels: Vec>, /// JIT entry point for the `iseq` - jit_entry: Option>>, + jit_entries: Vec>>, /// ISEQ calls that need to be compiled later iseq_calls: Vec>>, @@ -50,7 +50,7 @@ impl JITState { iseq, opnds: vec![None; num_insns], labels: vec![None; num_blocks], - jit_entry: None, + jit_entries: Vec::default(), iseq_calls: Vec::default(), c_stack_slots, } @@ -228,7 +228,7 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>, }; // Compile the High-level IR - let (start_ptr, jit_entry_ptr, gc_offsets, iseq_calls) = gen_function(cb, iseq, function)?; + let (iseq_code_ptrs, gc_offsets, iseq_calls) = gen_function(cb, iseq, function)?; // Stub callee ISEQs for JIT-to-JIT calls for iseq_call in iseq_calls.iter() { @@ -238,11 +238,11 @@ fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>, // Prepare for GC payload.iseq_calls.extend(iseq_calls.clone()); append_gc_offsets(iseq, &gc_offsets); - Ok(IseqCodePtrs { start_ptr, jit_entry_ptr }) + Ok(iseq_code_ptrs) } /// Compile a function -fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Result<(CodePtr, CodePtr, Vec, Vec), CompileError> { +fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Result<(IseqCodePtrs, Vec, Vec), CompileError> { let c_stack_slots = max_num_params(function).saturating_sub(ALLOC_REGS.len()); let mut jit = JITState::new(iseq, function.num_insns(), function.num_blocks(), c_stack_slots); let mut asm = Assembler::new(); @@ -307,8 +307,13 @@ fn gen_function(cb: &mut CodeBlock, iseq: IseqPtr, function: &Function) -> Resul } } result.map(|(start_ptr, gc_offsets)| { - let jit_entry_ptr = jit.jit_entry.unwrap().borrow().start_addr.get().unwrap(); - (start_ptr, jit_entry_ptr, gc_offsets, jit.iseq_calls) + // Make sure jit_entry_ptrs can be used as a parallel vector to jit_entry_insns() + jit.jit_entries.sort_by_key(|jit_entry| jit_entry.borrow().jit_entry_idx); + + let jit_entry_ptrs = jit.jit_entries.iter().map(|jit_entry| + jit_entry.borrow().start_addr.get().expect("start_addr should have been set by pos_marker in gen_entry_point") + ).collect(); + (IseqCodePtrs { start_ptr, jit_entry_ptrs }, gc_offsets, jit.iseq_calls) }) } @@ -375,7 +380,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio // TODO remove this check when we have stack args (we can use Time.new to test it) Insn::InvokeBuiltin { bf, state, .. } if bf.argc + 2 > (C_ARG_OPNDS.len() as i32) => return Err(*state), Insn::InvokeBuiltin { bf, args, state, .. } => gen_invokebuiltin(jit, asm, &function.frame_state(*state), bf, opnds!(args)), - &Insn::EntryPoint { jit_entry } => no_output!(gen_entry_point(jit, asm, jit_entry)), + &Insn::EntryPoint { jit_entry_idx } => no_output!(gen_entry_point(jit, asm, jit_entry_idx)), Insn::Return { val } => no_output!(gen_return(asm, opnd!(val))), Insn::FixnumAdd { left, right, state } => gen_fixnum_add(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), Insn::FixnumSub { left, right, state } => gen_fixnum_sub(jit, asm, opnd!(left), opnd!(right), &function.frame_state(*state)), @@ -424,7 +429,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::DefinedIvar { self_val, id, pushval, .. } => { gen_defined_ivar(asm, opnd!(self_val), id, pushval) }, &Insn::ArrayExtend { left, right, state } => { no_output!(gen_array_extend(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state))) }, &Insn::GuardShape { val, shape, state } => gen_guard_shape(jit, asm, opnd!(val), shape, &function.frame_state(state)), - Insn::LoadPC => gen_load_pc(), + Insn::LoadPC => gen_load_pc(asm), &Insn::LoadIvarEmbedded { self_val, id, index } => gen_load_ivar_embedded(asm, opnd!(self_val), id, index), &Insn::LoadIvarExtended { self_val, id, index } => gen_load_ivar_extended(asm, opnd!(self_val), id, index), &Insn::ArrayMax { state, .. } @@ -825,8 +830,8 @@ fn gen_guard_shape(jit: &mut JITState, asm: &mut Assembler, val: Opnd, shape: Sh val } -fn gen_load_pc() -> Opnd { - Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC) +fn gen_load_pc(asm: &mut Assembler) -> Opnd { + asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC)) } fn gen_load_ivar_embedded(asm: &mut Assembler, self_val: Opnd, id: ID, index: u16) -> Opnd { @@ -1313,12 +1318,11 @@ fn gen_object_alloc_class(asm: &mut Assembler, class: VALUE, state: &FrameState) } } -/// Compile a frame setup. If is_jit_entry is true, remember the address of it as a JIT entry. -fn gen_entry_point(jit: &mut JITState, asm: &mut Assembler, is_jit_entry: bool) { - if is_jit_entry { - assert!(jit.jit_entry.is_none(), "only one jit_entry is expected"); - let jit_entry = JITEntry::new(); - jit.jit_entry = Some(jit_entry.clone()); +/// Compile a frame setup. If jit_entry_idx is Some, remember the address of it as a JIT entry. +fn gen_entry_point(jit: &mut JITState, asm: &mut Assembler, jit_entry_idx: Option) { + if let Some(jit_entry_idx) = jit_entry_idx { + let jit_entry = JITEntry::new(jit_entry_idx); + jit.jit_entries.push(jit_entry.clone()); asm.pos_marker(move |code_ptr, _| { jit_entry.borrow_mut().start_addr.set(Some(code_ptr)); }); @@ -1776,8 +1780,7 @@ fn compile_iseq(iseq: IseqPtr) -> Result { let mut function = match iseq_to_hir(iseq) { Ok(function) => function, Err(err) => { - let name = crate::cruby::iseq_get_location(iseq, 0); - debug!("ZJIT: iseq_to_hir: {err:?}: {name}"); + debug!("ZJIT: iseq_to_hir: {err:?}: {}", iseq_get_location(iseq, 0)); return Err(CompileError::ParseError(err)); } }; @@ -1785,12 +1788,6 @@ fn compile_iseq(iseq: IseqPtr) -> Result { function.optimize(); } function.dump_hir(); - #[cfg(debug_assertions)] - if let Err(err) = function.validate() { - debug!("ZJIT: compile_iseq: {err:?}"); - use crate::hir::ParseError; - return Err(CompileError::ParseError(ParseError::Validation(err))); - } Ok(function) } @@ -1928,10 +1925,15 @@ c_callable! { /// Compile an ISEQ for a function stub fn function_stub_hit_body(cb: &mut CodeBlock, iseq_call: &Rc>) -> Result { // Compile the stubbed ISEQ - let IseqCodePtrs { jit_entry_ptr, .. } = gen_iseq(cb, iseq_call.borrow().iseq, None).inspect_err(|err| { + let IseqCodePtrs { jit_entry_ptrs, .. } = gen_iseq(cb, iseq_call.borrow().iseq, None).inspect_err(|err| { debug!("{err:?}: gen_iseq failed: {}", iseq_get_location(iseq_call.borrow().iseq, 0)); })?; + // We currently don't support JIT-to-JIT calls for ISEQs with optional arguments. + // So we only need to use jit_entry_ptrs[0] for now. TODO: Support optional arguments. + assert_eq!(1, jit_entry_ptrs.len()); + let jit_entry_ptr = jit_entry_ptrs[0]; + // Update the stub to call the code pointer let code_addr = jit_entry_ptr.raw_ptr(cb); let iseq = iseq_call.borrow().iseq; @@ -2131,14 +2133,17 @@ impl Assembler { /// Store info about a JIT entry point pub struct JITEntry { + /// Index that corresponds to jit_entry_insns() + jit_entry_idx: usize, /// Position where the entry point starts start_addr: Cell>, } impl JITEntry { /// Allocate a new JITEntry - fn new() -> Rc> { + fn new(jit_entry_idx: usize) -> Rc> { let jit_entry = JITEntry { + jit_entry_idx, start_addr: Cell::new(None), }; Rc::new(RefCell::new(jit_entry)) diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 85aa3c0b05b435..9575c3d1993393 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1105,9 +1105,14 @@ pub mod test_utils { ruby_str_to_rust_string(eval(&inspect)) } - /// Get the ISeq of a specified method + /// Get IseqPtr for a specified method pub fn get_method_iseq(recv: &str, name: &str) -> *const rb_iseq_t { - let wrapped_iseq = eval(&format!("RubyVM::InstructionSequence.of({}.method(:{}))", recv, name)); + get_proc_iseq(&format!("{}.method(:{})", recv, name)) + } + + /// Get IseqPtr for a specified Proc object + pub fn get_proc_iseq(obj: &str) -> *const rb_iseq_t { + let wrapped_iseq = eval(&format!("RubyVM::InstructionSequence.of({obj})")); unsafe { rb_iseqw_to_iseq(wrapped_iseq) } } diff --git a/zjit/src/gc.rs b/zjit/src/gc.rs index aa87ff98423ab9..eed202ac77a378 100644 --- a/zjit/src/gc.rs +++ b/zjit/src/gc.rs @@ -40,8 +40,8 @@ impl IseqPayload { pub struct IseqCodePtrs { /// Entry for the interpreter pub start_ptr: CodePtr, - /// Entry for JIT-to-JIT calls - pub jit_entry_ptr: CodePtr, + /// Entries for JIT-to-JIT calls + pub jit_entry_ptrs: Vec, } #[derive(Debug, PartialEq)] diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index a23d117d7d27c5..63749f0991f467 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -6,10 +6,10 @@ #![allow(clippy::if_same_then_else)] #![allow(clippy::match_like_matches_macro)] use crate::{ - cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, gc::{get_or_create_iseq_payload, IseqPayload}, options::{get_option, DumpHIR}, state::ZJITState + cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, gc::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState }; use std::{ - cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_int, c_void, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter + cell::RefCell, collections::{HashMap, HashSet, VecDeque}, ffi::{c_void, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter }; use crate::hir_type::{Type, types}; use crate::bitset::BitSet; @@ -456,7 +456,6 @@ pub enum SideExitReason { BlockParamProxyModified, BlockParamProxyNotIseqOrIfunc, StackOverflow, - OptionalArgumentsSupplied, } #[derive(Debug, Clone, Copy)] @@ -661,8 +660,8 @@ pub enum Insn { return_type: Option, // None for unannotated builtins }, - /// Set up frame and remember the address as a JIT entry if jit_entry is true - EntryPoint { jit_entry: bool }, + /// Set up frame. Remember the address as the JIT entry for the insn_idx in `jit_entry_insns()[jit_entry_idx]`. + EntryPoint { jit_entry_idx: Option }, /// Control flow instructions Return { val: InsnId }, /// Non-local control flow. See the throw YARV instruction @@ -936,7 +935,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { } Ok(()) } - &Insn::EntryPoint { jit_entry } => write!(f, "EntryPoint {}", if jit_entry { "JIT" } else { "interpreter" }), + &Insn::EntryPoint { jit_entry_idx: Some(idx) } => write!(f, "EntryPoint JIT({idx})"), + &Insn::EntryPoint { jit_entry_idx: None } => write!(f, "EntryPoint interpreter"), Insn::Return { val } => { write!(f, "Return {val}") } Insn::FixnumAdd { left, right, .. } => { write!(f, "FixnumAdd {left}, {right}") }, Insn::FixnumSub { left, right, .. } => { write!(f, "FixnumSub {left}, {right}") }, @@ -1221,7 +1221,8 @@ fn can_direct_send(iseq: *const rb_iseq_t) -> bool { pub struct Function { // ISEQ this function refers to iseq: *const rb_iseq_t, - // The types for the parameters of this function + /// The types for the parameters of this function. They are copied to the type + /// of entry block params after infer_types() fills Empty to all insn_types. param_types: Vec, // TODO: get method name and source location from the ISEQ @@ -1233,7 +1234,7 @@ pub struct Function { /// Entry block for the interpreter entry_block: BlockId, /// Entry block for JIT-to-JIT calls - jit_entry_block: BlockId, + jit_entry_blocks: Vec, profiles: Option, } @@ -1244,9 +1245,9 @@ impl Function { insns: vec![], insn_types: vec![], union_find: UnionFind::new().into(), - blocks: vec![Block::default(), Block::default()], + blocks: vec![Block::default()], entry_block: BlockId(0), - jit_entry_block: BlockId(1), + jit_entry_blocks: vec![], param_types: vec![], profiles: None, } @@ -1607,40 +1608,60 @@ impl Function { } } + /// Set self.param_types. They are copied to the param types of entry blocks. + fn set_param_types(&mut self) { + let iseq = self.iseq; + let param_size = unsafe { get_iseq_body_param_size(iseq) }.as_usize(); + let rest_param_idx = if !iseq.is_null() && unsafe { get_iseq_flags_has_rest(iseq) } { + let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; + let lead_num = unsafe { get_iseq_body_param_lead_num(iseq) }; + Some(opt_num + lead_num) + } else { + None + }; + + self.param_types.push(types::BasicObject); // self + for local_idx in 0..param_size { + let param_type = if Some(local_idx as i32) == rest_param_idx { + types::ArrayExact // Rest parameters are always ArrayExact + } else { + types::BasicObject + }; + self.param_types.push(param_type); + } + } + + /// Copy self.param_types to the param types of entry blocks. + fn copy_param_types(&mut self) { + for entry_block in self.entry_blocks() { + let entry_params = self.blocks[entry_block.0].params.iter(); + let param_types = self.param_types.iter(); + assert_eq!( + entry_params.len(), + param_types.len(), + "param types should be initialized before type inference", + ); + for (param, param_type) in std::iter::zip(entry_params, param_types) { + // We know that function parameters are BasicObject or some subclass + self.insn_types[param.0] = *param_type; + } + } + } + fn infer_types(&mut self) { // Reset all types self.insn_types.fill(types::Empty); - // Fill parameter types - // TODO: Remove this when HIR incorporates parameter loads as well - let entry_params = self.blocks[self.entry_block.0].params.iter(); - let param_types = self.param_types.iter(); - assert_eq!( - entry_params.len(), - param_types.len(), - "param types should be initialized before type inference" - ); - for (param, param_type) in std::iter::zip(entry_params, param_types.clone()) { - // We know that function parameters are BasicObject or some subclass - self.insn_types[param.0] = *param_type; - } + // Fill entry parameter types + self.copy_param_types(); - // Fill JIT entry parameter types - let entry_params = self.blocks[self.jit_entry_block.0].params.iter(); - assert_eq!( - entry_params.len(), - param_types.len(), - "param types should be initialized before type inference" - ); - for (param, param_type) in std::iter::zip(entry_params, param_types) { - // We know that function parameters are BasicObject or some subclass - self.insn_types[param.0] = *param_type; + let mut reachable = BlockSet::with_capacity(self.blocks.len()); + for entry_block in self.entry_blocks() { + reachable.insert(entry_block); } - let rpo = self.rpo(); // Walk the graph, computing types until fixpoint - let mut reachable = BlockSet::with_capacity(self.blocks.len()); - reachable.insert(self.entry_block); + let rpo = self.rpo(); loop { let mut changed = false; for &block in &rpo { @@ -2623,15 +2644,21 @@ impl Function { } } + /// Return a list that has entry_block and then jit_entry_blocks + fn entry_blocks(&self) -> Vec { + let mut entry_blocks = self.jit_entry_blocks.clone(); + entry_blocks.insert(0, self.entry_block); + entry_blocks + } + /// Return a traversal of the `Function`'s `BlockId`s in reverse post-order. pub fn rpo(&self) -> Vec { - let mut result = self.po_from(self.entry_block); + let mut result = self.po_from(self.entry_blocks()); result.reverse(); - result.insert(1, self.jit_entry_block); // Put jit_entry_block after entry_block result } - fn po_from(&self, start: BlockId) -> Vec { + fn po_from(&self, starts: Vec) -> Vec { #[derive(PartialEq)] enum Action { VisitEdges, @@ -2639,7 +2666,7 @@ impl Function { } let mut result = vec![]; let mut seen = BlockSet::with_capacity(self.blocks.len()); - let mut stack = vec![(start, Action::VisitEdges)]; + let mut stack: Vec<_> = starts.iter().map(|&start| (start, Action::VisitEdges)).collect(); while let Some((block, action)) = stack.pop() { if action == Action::VisitSelf { result.push(block); @@ -3125,16 +3152,36 @@ fn insn_idx_at_offset(idx: u32, offset: i64) -> u32 { ((idx as isize) + (offset as isize)) as u32 } +/// List of insn_idx that starts a JIT entry block +fn jit_entry_insns(iseq: IseqPtr) -> Vec { + let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; + if opt_num > 0 { + let mut result = vec![]; + + let opt_table = unsafe { get_iseq_body_param_opt_table(iseq) }; // `opt_num + 1` entries + for opt_idx in 0..=opt_num as isize { + let insn_idx = unsafe { opt_table.offset(opt_idx).read().as_u32() }; + result.push(insn_idx); + } + + // Deduplicate entries with HashSet since opt_table may have duplicated entries, e.g. proc { |a=a| a } + result.sort(); + result.dedup(); + result + } else { + vec![0] + } +} + struct BytecodeInfo { jump_targets: Vec, has_blockiseq: bool, } -fn compute_bytecode_info(iseq: *const rb_iseq_t) -> BytecodeInfo { +fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &Vec) -> BytecodeInfo { let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; let mut insn_idx = 0; - let mut jump_targets = HashSet::new(); - jump_targets.insert(0); // entry blocks jump to the first instruction + let mut jump_targets: HashSet = opt_table.iter().copied().collect(); let mut has_blockiseq = false; while insn_idx < iseq_size { // Get the current pc and opcode @@ -3185,6 +3232,7 @@ pub enum ParseError { StackUnderflow(FrameState), MalformedIseq(u32), // insn_idx into iseq_encoded Validation(ValidationError), + FailedOptionalArguments, NotAllowed, } @@ -3258,9 +3306,16 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { let mut fun = Function::new(iseq); // Compute a map of PC->Block by finding jump targets - let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq); + let jit_entry_insns = jit_entry_insns(iseq); + let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq, &jit_entry_insns); let mut insn_idx_to_block = HashMap::new(); for insn_idx in jump_targets { + // Prepend a JIT entry block if it's a jit_entry_insn. + // compile_entry_block() assumes that a JIT entry block jumps to the next block. + if jit_entry_insns.contains(&insn_idx) { + let jit_entry_block = fun.new_block(insn_idx); + fun.jit_entry_blocks.push(jit_entry_block); + } insn_idx_to_block.insert(insn_idx, fun.new_block(insn_idx)); } @@ -3268,60 +3323,35 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // optimizing locals in that case because they're shared with other frames. let ep_escaped = iseq_escapes_ep(iseq); - // Compile an interpreter entry to the first block - let first_block = insn_idx_to_block[&0]; - compile_entry_block(fun.entry_block, first_block, &mut fun, false); - // Compile a JIT entry to the first block - compile_entry_block(fun.jit_entry_block, first_block, &mut fun, true); + // Compile an entry_block for the interpreter + compile_entry_block(&mut fun, &jit_entry_insns); // Iteratively fill out basic blocks using a queue // TODO(max): Basic block arguments at edges - let mut queue = std::collections::VecDeque::new(); - // Index of the rest parameter for comparison below - let rest_param_idx = if !iseq.is_null() && unsafe { get_iseq_flags_has_rest(iseq) } { - let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; - let lead_num = unsafe { get_iseq_body_param_lead_num(iseq) }; - opt_num + lead_num - } else { - -1 - }; - // The HIR function will have the same number of parameter as the iseq so - // we properly handle calls from the interpreter. Roughly speaking, each - // item between commas in the source increase the parameter count by one, - // regardless of parameter kind. - let mut entry_state = FrameState::new(iseq); - fun.push_insn(first_block, Insn::Param { idx: SELF_PARAM_IDX }); - fun.param_types.push(types::BasicObject); // self - for local_idx in 0..num_locals(iseq) { - if local_idx < unsafe { get_iseq_body_param_size(iseq) }.as_usize() { - entry_state.locals.push(fun.push_insn(first_block, Insn::Param { idx: local_idx + 1 })); // +1 for self - - let mut param_type = types::BasicObject; - // Rest parameters are always ArrayExact - if let Ok(true) = c_int::try_from(local_idx).map(|idx| idx == rest_param_idx) { - param_type = types::ArrayExact; - } - fun.param_types.push(param_type); - } else { - entry_state.locals.push(fun.push_insn(first_block, Insn::Const { val: Const::Value(Qnil) })); - } - } - queue.push_back((entry_state.clone(), first_block, /*insn_idx=*/0_u32, /*local_inval=*/false)); + let mut queue = VecDeque::new(); + queue.push_back((FrameState::new(iseq), insn_idx_to_block[&0], /*insn_idx=*/0, /*local_inval=*/false)); + // Keep compiling blocks until the queue becomes empty let mut visited = HashSet::new(); - let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; while let Some((incoming_state, block, mut insn_idx, mut local_inval)) = queue.pop_front() { + // Compile each block only once if visited.contains(&block) { continue; } visited.insert(block); - let (self_param, mut state) = if insn_idx == 0 { - (fun.blocks[block.0].params[SELF_PARAM_IDX], incoming_state.clone()) - } else { - let self_param = fun.push_insn(block, Insn::Param { idx: SELF_PARAM_IDX }); + + // Compile a JIT entry to the block if it's a jit_entry_insn + if let Some(block_idx) = jit_entry_insns.iter().position(|&idx| idx == insn_idx) { + compile_jit_entry_block(&mut fun, block_idx, block); + } + + // Load basic block params first + let self_param = fun.push_insn(block, Insn::Param { idx: SELF_PARAM_IDX }); + let mut state = { let mut result = FrameState::new(iseq); let mut idx = 1; - for _ in 0..incoming_state.locals.len() { + let local_size = if insn_idx == 0 { num_locals(iseq) } else { incoming_state.locals.len() }; + for _ in 0..local_size { result.locals.push(fun.push_insn(block, Insn::Param { idx })); idx += 1; } @@ -3329,8 +3359,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { result.stack.push(fun.push_insn(block, Insn::Param { idx })); idx += 1; } - (self_param, result) + result }; + // Start the block off with a Snapshot so that if we need to insert a new Guard later on // and we don't have a Snapshot handy, we can just iterate backward (at the earliest, to // the beginning of the block). @@ -4106,6 +4137,15 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } } + // Bail out if there's a JIT entry block whose target block was not compiled + // due to an unsupported instruction in the middle of the ISEQ. + for jit_entry_block in fun.jit_entry_blocks.iter() { + if fun.blocks[jit_entry_block.0].insns.is_empty() { + return Err(ParseError::FailedOptionalArguments); + } + } + + fun.set_param_types(); fun.infer_types(); match get_option!(dump_hir_init) { @@ -4116,44 +4156,77 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } fun.profiles = Some(profiles); - if let Err(e) = fun.validate() { - return Err(ParseError::Validation(e)); + if let Err(err) = fun.validate() { + debug!("ZJIT: {err:?}: Initial HIR:\n{}", FunctionPrinter::without_snapshot(&fun)); + return Err(ParseError::Validation(err)); } Ok(fun) } -/// Compile entry_block that jumps to first_block. Add prologue for the interpreter if jit_entry is false. -fn compile_entry_block(entry_block: BlockId, first_block: BlockId, fun: &mut Function, jit_entry: bool) { - fun.push_insn(entry_block, Insn::EntryPoint { jit_entry }); +/// Compile an entry_block for the interpreter +fn compile_entry_block(fun: &mut Function, jit_entry_insns: &Vec) { + let entry_block = fun.entry_block; + fun.push_insn(entry_block, Insn::EntryPoint { jit_entry_idx: None }); // Prepare entry_state with basic block params - let iseq = fun.iseq; - let mut entry_state = FrameState::new(iseq); - let self_param = fun.push_insn(entry_block, Insn::Param { idx: SELF_PARAM_IDX }); - for local_idx in 0..unsafe { get_iseq_body_param_size(iseq) }.as_usize() { - entry_state.locals.push(fun.push_insn(entry_block, Insn::Param { idx: local_idx + 1 })); // +1 for self + let (self_param, entry_state) = compile_entry_state(fun, entry_block); + + // Jump to target blocks + let mut pc: Option = None; + let &last_entry_insn = jit_entry_insns.last().unwrap(); + for (jit_entry_block_idx, &jit_entry_insn) in jit_entry_insns.iter().enumerate() { + let jit_entry_block = fun.jit_entry_blocks[jit_entry_block_idx]; + let target_block = BlockId(jit_entry_block.0 + 1); // jit_entry_block precedes the jump target block + + if jit_entry_insn == last_entry_insn { + // If it's the last possible entry, jump to the target_block without checking PC + fun.push_insn(entry_block, Insn::Jump(BranchEdge { target: target_block, args: entry_state.as_args(self_param) })); + } else { + // Otherwise, jump to the target_block only if PC matches. + let pc = pc.unwrap_or_else(|| { + let insn_id = fun.push_insn(entry_block, Insn::LoadPC); + pc = Some(insn_id); + insn_id + }); + let expected_pc = fun.push_insn(entry_block, Insn::Const { + val: Const::CPtr(unsafe { rb_iseq_pc_at_idx(fun.iseq, jit_entry_insn) } as *const u8), + }); + let test_id = fun.push_insn(entry_block, Insn::IsBitEqual { left: pc, right: expected_pc }); + fun.push_insn(entry_block, Insn::IfTrue { + val: test_id, + target: BranchEdge { target: target_block, args: entry_state.as_args(self_param) }, + }); + } } +} - // Jump to the first_block. Conditionally side-exit for interpreter entry with optional arguments. - // TODO: Compile gen_entry_params() for !jit_entry here - let opt_num = unsafe { get_iseq_body_param_opt_num(iseq) }; - if !jit_entry && opt_num > 0 { - let pc = fun.push_insn(entry_block, Insn::LoadPC); - let no_opts_pc = fun.push_insn(entry_block, Insn::Const { val: Const::CPtr(unsafe { rb_iseq_pc_at_idx(iseq, 0) } as *const u8) }); - let test_id = fun.push_insn(entry_block, Insn::IsBitEqual { left: pc, right: no_opts_pc }); - - // Jump to the first_block if no optional arguments are supplied - fun.push_insn(entry_block, Insn::IfTrue { - val: test_id, - target: BranchEdge { target: first_block, args: entry_state.as_args(self_param) }, - }); +/// Compile a jit_entry_block +fn compile_jit_entry_block(fun: &mut Function, jit_entry_idx: usize, target_block: BlockId) { + let jit_entry_block = fun.jit_entry_blocks[jit_entry_idx]; + fun.push_insn(jit_entry_block, Insn::EntryPoint { jit_entry_idx: Some(jit_entry_idx) }); - // Side-exit if any optional argument is supplied - let state = fun.push_insn(entry_block, Insn::Snapshot { state: FrameState::new(iseq) }); // no need to spill params - fun.push_insn(entry_block, Insn::SideExit { state, reason: SideExitReason::OptionalArgumentsSupplied }); - } else { - fun.push_insn(entry_block, Insn::Jump(BranchEdge { target: first_block, args: entry_state.as_args(self_param) })); + // Prepare entry_state with basic block params + let (self_param, entry_state) = compile_entry_state(fun, jit_entry_block); + + // Jump to target_block + fun.push_insn(jit_entry_block, Insn::Jump(BranchEdge { target: target_block, args: entry_state.as_args(self_param) })); +} + +/// Compile params and initial locals for an entry block +fn compile_entry_state(fun: &mut Function, entry_block: BlockId) -> (InsnId, FrameState) { + let iseq = fun.iseq; + let param_size = unsafe { get_iseq_body_param_size(iseq) }.as_usize(); + + let self_param = fun.push_insn(entry_block, Insn::Param { idx: SELF_PARAM_IDX }); + let mut entry_state = FrameState::new(iseq); + for local_idx in 0..num_locals(iseq) { + if local_idx < param_size { + entry_state.locals.push(fun.push_insn(entry_block, Insn::Param { idx: local_idx + 1 })); // +1 for self + } else { + entry_state.locals.push(fun.push_insn(entry_block, Insn::Const { val: Const::Value(Qnil) })); + } } + (self_param, entry_state) } #[cfg(test)] @@ -4201,29 +4274,26 @@ mod rpo_tests { fn one_block() { let mut function = Function::new(std::ptr::null()); let entry = function.entry_block; - let jit_entry = function.jit_entry_block; let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); function.push_insn(entry, Insn::Return { val }); - assert_eq!(function.rpo(), vec![entry, jit_entry]); + assert_eq!(function.rpo(), vec![entry]); } #[test] fn jump() { let mut function = Function::new(std::ptr::null()); let entry = function.entry_block; - let jit_entry = function.jit_entry_block; let exit = function.new_block(0); function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] })); let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); function.push_insn(entry, Insn::Return { val }); - assert_eq!(function.rpo(), vec![entry, jit_entry, exit]); + assert_eq!(function.rpo(), vec![entry, exit]); } #[test] fn diamond_iftrue() { let mut function = Function::new(std::ptr::null()); let entry = function.entry_block; - let jit_entry = function.jit_entry_block; let side = function.new_block(0); let exit = function.new_block(0); function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] })); @@ -4232,14 +4302,13 @@ mod rpo_tests { function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] })); let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); function.push_insn(entry, Insn::Return { val }); - assert_eq!(function.rpo(), vec![entry, jit_entry, side, exit]); + assert_eq!(function.rpo(), vec![entry, side, exit]); } #[test] fn diamond_iffalse() { let mut function = Function::new(std::ptr::null()); let entry = function.entry_block; - let jit_entry = function.jit_entry_block; let side = function.new_block(0); let exit = function.new_block(0); function.push_insn(side, Insn::Jump(BranchEdge { target: exit, args: vec![] })); @@ -4248,16 +4317,15 @@ mod rpo_tests { function.push_insn(entry, Insn::Jump(BranchEdge { target: exit, args: vec![] })); let val = function.push_insn(entry, Insn::Const { val: Const::Value(Qnil) }); function.push_insn(entry, Insn::Return { val }); - assert_eq!(function.rpo(), vec![entry, jit_entry, side, exit]); + assert_eq!(function.rpo(), vec![entry, side, exit]); } #[test] fn a_loop() { let mut function = Function::new(std::ptr::null()); let entry = function.entry_block; - let jit_entry = function.jit_entry_block; function.push_insn(entry, Insn::Jump(BranchEdge { target: entry, args: vec![] })); - assert_eq!(function.rpo(), vec![entry, jit_entry]); + assert_eq!(function.rpo(), vec![entry]); } } @@ -4387,11 +4455,7 @@ mod validation_tests { fn instruction_appears_twice_in_same_block() { let mut function = Function::new(std::ptr::null()); let block = function.new_block(0); - - // Add jumps from entry blocks to avoid BlockHasNoterminator function.push_insn(function.entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - function.push_insn(function.jit_entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - let val = function.push_insn(block, Insn::Const { val: Const::Value(Qnil) }); function.push_insn_id(block, val); function.push_insn(block, Insn::Return { val }); @@ -4402,11 +4466,7 @@ mod validation_tests { fn instruction_appears_twice_with_different_ids() { let mut function = Function::new(std::ptr::null()); let block = function.new_block(0); - - // Add jumps from entry blocks to avoid BlockHasNoterminator function.push_insn(function.entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - function.push_insn(function.jit_entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - let val0 = function.push_insn(block, Insn::Const { val: Const::Value(Qnil) }); let val1 = function.push_insn(block, Insn::Const { val: Const::Value(Qnil) }); function.make_equal_to(val1, val0); @@ -4418,11 +4478,7 @@ mod validation_tests { fn instruction_appears_twice_in_different_blocks() { let mut function = Function::new(std::ptr::null()); let block = function.new_block(0); - - // Add jumps from entry blocks to avoid BlockHasNoterminator function.push_insn(function.entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - function.push_insn(function.jit_entry_block, Insn::Jump(BranchEdge { target: block, args: vec![] })); - let val = function.push_insn(block, Insn::Const { val: Const::Value(Qnil) }); let exit = function.new_block(0); function.push_insn(block, Insn::Jump(BranchEdge { target: exit, args: vec![] })); @@ -4491,7 +4547,6 @@ mod infer_tests { crate::cruby::with_rubyvm(|| { let mut function = Function::new(std::ptr::null()); let param = function.push_insn(function.entry_block, Insn::Param { idx: SELF_PARAM_IDX }); - function.push_insn(function.jit_entry_block, Insn::Param { idx: SELF_PARAM_IDX }); function.param_types.push(types::BasicObject); // self let val = function.push_insn(function.entry_block, Insn::Test { val: param }); function.infer_types(); @@ -4577,7 +4632,7 @@ mod snapshot_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v13:Any = Snapshot FrameState { pc: 0x1000, stack: [], locals: [a=v11, b=v12] } @@ -4634,9 +4689,23 @@ mod tests { } } + /// Combine multiple hir_string() results to match all of them at once, which allows + /// us to avoid running the set of zjit-test -> zjit-test-update multiple times. + #[macro_export] + macro_rules! hir_strings { + ($( $s:expr ),+ $(,)?) => {{ + vec![$( hir_string($s) ),+].join("\n") + }}; + } + #[track_caller] fn hir_string(method: &str) -> String { - let iseq = crate::cruby::with_rubyvm(|| get_method_iseq("self", method)); + hir_string_proc(&format!("{}.method(:{})", "self", method)) + } + + #[track_caller] + fn hir_string_proc(proc: &str) -> String { + let iseq = crate::cruby::with_rubyvm(|| get_proc_iseq(proc)); unsafe { crate::cruby::rb_zjit_profile_disable(iseq) }; let function = iseq_to_hir(iseq).unwrap(); hir_string_function(&function) @@ -4667,15 +4736,20 @@ mod tests { v4:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) v5:CBool = IsBitEqual v3, v4 IfTrue v5, bb2(v1, v2) - SideExit OptionalArgumentsSupplied - bb1(v10:BasicObject, v11:BasicObject): - EntryPoint JIT - Jump bb2(v10, v11) - bb2(v13:BasicObject, v14:BasicObject): - v16:Fixnum[1] = Const Value(1) - v19:Fixnum[123] = Const Value(123) + Jump bb4(v1, v2) + bb1(v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + Jump bb2(v9, v10) + bb2(v12:BasicObject, v13:BasicObject): + v15:Fixnum[1] = Const Value(1) + Jump bb4(v12, v15) + bb3(v18:BasicObject, v19:BasicObject): + EntryPoint JIT(1) + Jump bb4(v18, v19) + bb4(v21:BasicObject, v22:BasicObject): + v26:Fixnum[123] = Const Value(123) CheckInterrupts - Return v19 + Return v26 "); } @@ -4689,7 +4763,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[123] = Const Value(123) @@ -4708,7 +4782,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:ArrayExact = NewArray @@ -4727,7 +4801,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:ArrayExact = NewArray v9 @@ -4746,7 +4820,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 @@ -4765,7 +4839,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[10] = Const Value(10) @@ -4785,7 +4859,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:RangeExact = NewRange v11 NewRangeInclusive v12 @@ -4804,7 +4878,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[10] = Const Value(10) @@ -4824,7 +4898,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:RangeExact = NewRange v11 NewRangeExclusive v12 @@ -4843,7 +4917,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -4863,7 +4937,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -4883,7 +4957,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:HashExact = NewHash @@ -4902,7 +4976,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v16:StaticSymbol[:a] = Const Value(VALUE(0x1000)) @@ -4923,7 +4997,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -4943,7 +5017,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Bignum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -4962,7 +5036,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Flonum[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -4981,7 +5055,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:HeapFloat[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -5000,7 +5074,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) @@ -5019,7 +5093,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -5042,7 +5116,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -5067,7 +5141,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) @@ -5086,7 +5160,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -5111,7 +5185,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): SideExit PatchPoint(BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE)) @@ -5130,7 +5204,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -5155,7 +5229,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE)) @@ -5174,7 +5248,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) @@ -5199,7 +5273,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): SideExit PatchPoint(BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS)) @@ -5219,15 +5293,16 @@ mod tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:Fixnum[1] = Const Value(1) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) CheckInterrupts - Return v11 + Return v13 "); } @@ -5259,7 +5334,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:BasicObject = GetLocal l2, EP@4 @@ -5278,6 +5353,77 @@ mod tests { ); } + #[test] + fn test_setlocal_in_default_args() { + eval(" + def test(a = (b = 1)) = [a, b] + "); + assert_contains_opcode("test", YARVINSN_setlocal_WC_0); + assert_snapshot!(hir_string("test"), @r" + fn test@:2: + bb0(v1:BasicObject, v2:BasicObject): + EntryPoint interpreter + v3:NilClass = Const Value(nil) + v4:CPtr = LoadPC + v5:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v6:CBool = IsBitEqual v4, v5 + IfTrue v6, bb2(v1, v2, v3) + Jump bb4(v1, v2, v3) + bb1(v10:BasicObject, v11:BasicObject): + EntryPoint JIT(0) + v12:NilClass = Const Value(nil) + Jump bb2(v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:NilClass): + v20:Fixnum[1] = Const Value(1) + Jump bb4(v14, v20, v20) + bb3(v23:BasicObject, v24:BasicObject): + EntryPoint JIT(1) + v25:NilClass = Const Value(nil) + Jump bb4(v23, v24, v25) + bb4(v27:BasicObject, v28:BasicObject, v29:NilClass|Fixnum): + v34:ArrayExact = NewArray v28, v29 + CheckInterrupts + Return v34 + "); + } + + #[test] + fn test_setlocal_in_default_args_with_tracepoint() { + eval(" + def test(a = (b = 1)) = [a, b] + TracePoint.new(:line) {}.enable + test + "); + assert_compile_fails("test", ParseError::FailedOptionalArguments); + } + + #[test] + fn test_setlocal_in_default_args_with_side_exit() { + eval(" + def test(a = (def foo = nil)) = a + "); + assert_compile_fails("test", ParseError::FailedOptionalArguments); + } + + #[test] + fn test_setlocal_cyclic_default_args() { + eval(" + def test = proc { |a=a| a } + "); + assert_snapshot!(hir_string_proc("test"), @r" + fn block in test@:2: + bb0(v1:BasicObject, v2:BasicObject): + EntryPoint interpreter + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + CheckInterrupts + Return v9 + "); + } + #[test] fn defined_ivar() { eval(" @@ -5290,7 +5436,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:StringExact|NilClass = DefinedIvar v6, :@foo @@ -5317,7 +5463,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:TrueClass|NilClass = DefinedIvar v6, :@foo @@ -5346,7 +5492,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:NilClass = Const Value(nil) @@ -5378,7 +5524,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): CheckInterrupts @@ -5410,27 +5556,28 @@ mod tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v10:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): CheckInterrupts - v16:CBool = Test v9 - IfFalse v16, bb3(v8, v9, v10) - v20:Fixnum[3] = Const Value(3) + v18:CBool = Test v11 + IfFalse v18, bb3(v10, v11, v12) + v22:Fixnum[3] = Const Value(3) PatchPoint NoEPEscape(test) CheckInterrupts - Jump bb4(v8, v9, v20) - bb3(v26:BasicObject, v27:BasicObject, v28:NilClass): - v32:Fixnum[4] = Const Value(4) + Jump bb4(v10, v11, v22) + bb3(v28:BasicObject, v29:BasicObject, v30:NilClass): + v34:Fixnum[4] = Const Value(4) PatchPoint NoEPEscape(test) - Jump bb4(v26, v27, v32) - bb4(v36:BasicObject, v37:BasicObject, v38:Fixnum): + Jump bb4(v28, v29, v34) + bb4(v38:BasicObject, v39:BasicObject, v40:Fixnum): PatchPoint NoEPEscape(test) CheckInterrupts - Return v38 + Return v40 "); } @@ -5447,7 +5594,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :+, v12 @@ -5469,7 +5616,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :-, v12 @@ -5491,7 +5638,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :*, v12 @@ -5513,7 +5660,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :/, v12 @@ -5535,7 +5682,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :%, v12 @@ -5557,7 +5704,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :==, v12 @@ -5579,7 +5726,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :!=, v12 @@ -5601,7 +5748,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :<, v12 @@ -5623,7 +5770,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :<=, v12 @@ -5645,7 +5792,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :>, v12 @@ -5672,35 +5819,37 @@ mod tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject): + EntryPoint JIT(0) v7:NilClass = Const Value(nil) v8:NilClass = Const Value(nil) - v12:Fixnum[0] = Const Value(0) - v15:Fixnum[10] = Const Value(10) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:NilClass, v12:NilClass): + v16:Fixnum[0] = Const Value(0) + v19:Fixnum[10] = Const Value(10) CheckInterrupts - Jump bb4(v6, v12, v15) - bb4(v21:BasicObject, v22:BasicObject, v23:BasicObject): + Jump bb4(v10, v16, v19) + bb4(v25:BasicObject, v26:BasicObject, v27:BasicObject): PatchPoint NoEPEscape(test) - v27:Fixnum[0] = Const Value(0) - v31:BasicObject = SendWithoutBlock v23, :>, v27 + v31:Fixnum[0] = Const Value(0) + v35:BasicObject = SendWithoutBlock v27, :>, v31 CheckInterrupts - v34:CBool = Test v31 - IfTrue v34, bb3(v21, v22, v23) - v36:NilClass = Const Value(nil) + v38:CBool = Test v35 + IfTrue v38, bb3(v25, v26, v27) + v40:NilClass = Const Value(nil) PatchPoint NoEPEscape(test) CheckInterrupts - Return v22 - bb3(v46:BasicObject, v47:BasicObject, v48:BasicObject): + Return v26 + bb3(v50:BasicObject, v51:BasicObject, v52:BasicObject): PatchPoint NoEPEscape(test) - v54:Fixnum[1] = Const Value(1) - v58:BasicObject = SendWithoutBlock v47, :+, v54 - v61:Fixnum[1] = Const Value(1) - v65:BasicObject = SendWithoutBlock v48, :-, v61 - Jump bb4(v46, v58, v65) + v58:Fixnum[1] = Const Value(1) + v62:BasicObject = SendWithoutBlock v51, :+, v58 + v65:Fixnum[1] = Const Value(1) + v69:BasicObject = SendWithoutBlock v52, :-, v65 + Jump bb4(v50, v62, v69) "); } @@ -5717,7 +5866,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :>=, v12 @@ -5742,23 +5891,24 @@ mod tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:TrueClass = Const Value(true) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:TrueClass = Const Value(true) CheckInterrupts - v16:CBool[true] = Test v11 - IfFalse v16, bb3(v6, v11) - v20:Fixnum[3] = Const Value(3) + v18:CBool[true] = Test v13 + IfFalse v18, bb3(v8, v13) + v22:Fixnum[3] = Const Value(3) CheckInterrupts - Return v20 - bb3(v26, v27): - v31 = Const Value(4) + Return v22 + bb3(v28, v29): + v33 = Const Value(4) CheckInterrupts - Return v31 + Return v33 "); } @@ -5779,7 +5929,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[2] = Const Value(2) @@ -5807,7 +5957,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:BasicObject = GetLocal l0, EP@3 @@ -5832,7 +5982,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -5857,7 +6007,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -5885,7 +6035,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:ArrayExact = ToArray v9 @@ -5904,7 +6054,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:BasicObject = Send v8, 0x1000, :foo, v9 @@ -5924,7 +6074,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -5943,7 +6093,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:BasicObject = SendWithoutBlock v8, :foo, v9 @@ -5965,7 +6115,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = InvokeSuper v6, 0x1000 @@ -5985,7 +6135,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = InvokeSuper v6, 0x1000 @@ -6005,7 +6155,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:NilClass = Const Value(nil) @@ -6026,7 +6176,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): SideExit UnhandledYARVInsn(invokesuperforward) @@ -6042,7 +6192,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:NilClass = Const Value(nil) @@ -6064,7 +6214,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) @@ -6092,7 +6242,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:ArrayExact): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:ArrayExact): v14:ArrayExact = ToNewArray v9 @@ -6113,7 +6263,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:BasicObject = SendForward 0x1000, :foo, v9 @@ -6131,16 +6281,17 @@ mod tests { fn test@:2: bb0(v1:BasicObject, v2:BasicObject, v3:ArrayExact, v4:BasicObject, v5:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:ArrayExact, v11:BasicObject, v12:BasicObject): - EntryPoint JIT - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:ArrayExact, v17:BasicObject, v18:BasicObject): - v19:NilClass = Const Value(nil) - v24:ArrayExact = ToArray v16 + v6:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5, v6) + bb1(v9:BasicObject, v10:BasicObject, v11:ArrayExact, v12:BasicObject, v13:BasicObject): + EntryPoint JIT(0) + v14:NilClass = Const Value(nil) + Jump bb2(v9, v10, v11, v12, v13, v14) + bb2(v16:BasicObject, v17:BasicObject, v18:ArrayExact, v19:BasicObject, v20:BasicObject, v21:NilClass): + v26:ArrayExact = ToArray v18 PatchPoint NoEPEscape(test) GuardBlockParamProxy l0 - v29:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + v31:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) SideExit UnhandledYARVInsn(splatkw) "); } @@ -6158,7 +6309,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = GetConstantPath 0x1000 @@ -6191,7 +6342,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) @@ -6213,7 +6364,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_MAX) @@ -6238,14 +6389,16 @@ mod tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:NilClass = Const Value(nil) - v14:NilClass = Const Value(nil) - v21:BasicObject = SendWithoutBlock v11, :+, v12 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 SideExit UnknownNewarraySend(MIN) "); } @@ -6265,14 +6418,16 @@ mod tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:NilClass = Const Value(nil) - v14:NilClass = Const Value(nil) - v21:BasicObject = SendWithoutBlock v11, :+, v12 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 SideExit UnknownNewarraySend(HASH) "); } @@ -6292,16 +6447,18 @@ mod tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:NilClass = Const Value(nil) - v14:NilClass = Const Value(nil) - v21:BasicObject = SendWithoutBlock v11, :+, v12 - v24:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v26:StringExact = StringCopy v24 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 + v28:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v30:StringExact = StringCopy v28 SideExit UnknownNewarraySend(PACK) "); } @@ -6323,14 +6480,16 @@ mod tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:NilClass = Const Value(nil) - v14:NilClass = Const Value(nil) - v21:BasicObject = SendWithoutBlock v11, :+, v12 + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject): + EntryPoint JIT(0) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:NilClass, v18:NilClass): + v25:BasicObject = SendWithoutBlock v15, :+, v16 SideExit UnknownNewarraySend(INCLUDE_P) "); } @@ -6347,7 +6506,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 @@ -6369,7 +6528,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 @@ -6392,7 +6551,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -6415,7 +6574,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -6439,7 +6598,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -6462,7 +6621,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = GetGlobal :$foo @@ -6483,7 +6642,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:ArrayExact = ToNewArray v9 @@ -6504,7 +6663,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -6528,7 +6687,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:ArrayExact = ToNewArray v9 @@ -6551,7 +6710,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:ArrayExact = ToNewArray v9 @@ -6578,7 +6737,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v16:NilClass = Const Value(nil) @@ -6601,7 +6760,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :[], v12 @@ -6622,7 +6781,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v16:BasicObject = SendWithoutBlock v9, :empty? @@ -6643,7 +6802,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v16:BasicObject = SendWithoutBlock v9, :succ @@ -6664,7 +6823,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :&, v12 @@ -6685,7 +6844,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :|, v12 @@ -6706,7 +6865,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v16:BasicObject = SendWithoutBlock v9, :! @@ -6727,7 +6886,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :=~, v12 @@ -6752,7 +6911,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Class[VMFrozenCore] = Const Value(VALUE(0x1000)) @@ -6779,55 +6938,61 @@ mod tests { end "); assert_contains_opcode("reverse_odd", YARVINSN_opt_reverse); - assert_snapshot!(hir_string("reverse_odd"), @r" + assert_contains_opcode("reverse_even", YARVINSN_opt_reverse); + assert_snapshot!(hir_strings!("reverse_odd", "reverse_even"), @r" fn reverse_odd@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject): + EntryPoint JIT(0) v8:NilClass = Const Value(nil) v9:NilClass = Const Value(nil) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:NilClass, v14:NilClass, v15:NilClass): PatchPoint SingleRactorMode - v15:BasicObject = GetIvar v6, :@a + v21:BasicObject = GetIvar v12, :@a PatchPoint SingleRactorMode - v18:BasicObject = GetIvar v6, :@b + v24:BasicObject = GetIvar v12, :@b PatchPoint SingleRactorMode - v21:BasicObject = GetIvar v6, :@c + v27:BasicObject = GetIvar v12, :@c PatchPoint NoEPEscape(reverse_odd) - v27:ArrayExact = NewArray v15, v18, v21 + v33:ArrayExact = NewArray v21, v24, v27 CheckInterrupts - Return v27 - "); - assert_contains_opcode("reverse_even", YARVINSN_opt_reverse); - assert_snapshot!(hir_string("reverse_even"), @r" + Return v33 + fn reverse_even@:8: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v8:NilClass = Const Value(nil) + v2:NilClass = Const Value(nil) + v3:NilClass = Const Value(nil) + v4:NilClass = Const Value(nil) + v5:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5) + bb1(v8:BasicObject): + EntryPoint JIT(0) v9:NilClass = Const Value(nil) v10:NilClass = Const Value(nil) + v11:NilClass = Const Value(nil) + v12:NilClass = Const Value(nil) + Jump bb2(v8, v9, v10, v11, v12) + bb2(v14:BasicObject, v15:NilClass, v16:NilClass, v17:NilClass, v18:NilClass): PatchPoint SingleRactorMode - v16:BasicObject = GetIvar v6, :@a + v24:BasicObject = GetIvar v14, :@a PatchPoint SingleRactorMode - v19:BasicObject = GetIvar v6, :@b + v27:BasicObject = GetIvar v14, :@b PatchPoint SingleRactorMode - v22:BasicObject = GetIvar v6, :@c + v30:BasicObject = GetIvar v14, :@c PatchPoint SingleRactorMode - v25:BasicObject = GetIvar v6, :@d + v33:BasicObject = GetIvar v14, :@d PatchPoint NoEPEscape(reverse_even) - v31:ArrayExact = NewArray v16, v19, v22, v25 + v39:ArrayExact = NewArray v24, v27, v30, v33 CheckInterrupts - Return v31 + Return v39 "); } @@ -6843,7 +7008,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): CheckInterrupts @@ -6866,7 +7031,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3, v4) bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject, v10:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v7, v8, v9, v10) bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): v20:Float = InvokeBuiltin rb_f_float, v12, v13, v14 @@ -6886,7 +7051,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:Class = InvokeBuiltin _bi20, v6 @@ -6907,28 +7072,29 @@ mod tests { fn open@: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject, v4:BasicObject, v5:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3, v4, v5) - bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): - EntryPoint JIT - Jump bb2(v8, v9, v10, v11, v12) - bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:BasicObject): - v19:NilClass = Const Value(nil) - v24:BasicObject = InvokeBuiltin dir_s_open, v14, v15, v16 + v6:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4, v5, v6) + bb1(v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject, v13:BasicObject): + EntryPoint JIT(0) + v14:NilClass = Const Value(nil) + Jump bb2(v9, v10, v11, v12, v13, v14) + bb2(v16:BasicObject, v17:BasicObject, v18:BasicObject, v19:BasicObject, v20:BasicObject, v21:NilClass): + v26:BasicObject = InvokeBuiltin dir_s_open, v16, v17, v18 PatchPoint NoEPEscape(open) GuardBlockParamProxy l0 - v31:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) + v33:HeapObject[BlockParamProxy] = Const Value(VALUE(0x1000)) CheckInterrupts - v34:CBool[true] = Test v31 - IfFalse v34, bb3(v14, v15, v16, v17, v18, v24) + v36:CBool[true] = Test v33 + IfFalse v36, bb3(v16, v17, v18, v19, v20, v26) PatchPoint NoEPEscape(open) - v41:BasicObject = InvokeBlock, v24 - v45:BasicObject = InvokeBuiltin dir_s_close, v14, v24 + v43:BasicObject = InvokeBlock, v26 + v47:BasicObject = InvokeBuiltin dir_s_close, v16, v26 CheckInterrupts - Return v41 - bb3(v51, v52, v53, v54, v55, v56): + Return v43 + bb3(v53, v54, v55, v56, v57, v58): PatchPoint NoEPEscape(open) CheckInterrupts - Return v56 + Return v58 "); } @@ -6943,7 +7109,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = InvokeBuiltin gc_enable, v6 @@ -6965,7 +7131,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3, v4, v5) bb1(v8:BasicObject, v9:BasicObject, v10:BasicObject, v11:BasicObject, v12:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v8, v9, v10, v11, v12) bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:BasicObject): v22:FalseClass = Const Value(false) @@ -6987,7 +7153,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:NilClass = Const Value(nil) @@ -7019,7 +7185,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -7044,7 +7210,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7074,7 +7240,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -7099,7 +7265,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7129,7 +7295,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7151,26 +7317,25 @@ mod tests { define_method(:throw_break) { break 2 } "); assert_contains_opcode("throw_return", YARVINSN_throw); - assert_snapshot!(hir_string("throw_return"), @r" + assert_contains_opcode("throw_break", YARVINSN_throw); + assert_snapshot!(hir_strings!("throw_return", "throw_break"), @r" fn block in @:2: bb0(v1:BasicObject): EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v12:Fixnum[1] = Const Value(1) Throw TAG_RETURN, v12 - "); - assert_contains_opcode("throw_break", YARVINSN_throw); - assert_snapshot!(hir_string("throw_break"), @r" + fn block in @:3: bb0(v1:BasicObject): EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v12:Fixnum[2] = Const Value(2) @@ -7191,7 +7356,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = InvokeBlock @@ -7213,7 +7378,7 @@ mod tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:BasicObject = InvokeBlock, v11, v12 @@ -7257,7 +7422,7 @@ mod graphviz_tests { bb0:v4 -> bb2:params:n; bb1 [label=< - +
bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject) 
EntryPoint JIT 
EntryPoint JIT(0) 
Jump bb2(v6, v7, v8) 
>]; bb1:v9 -> bb2:params:n; @@ -7303,7 +7468,7 @@ mod graphviz_tests { bb0:v3 -> bb2:params:n; bb1 [label=< - +
bb1(v5:BasicObject, v6:BasicObject) 
EntryPoint JIT 
EntryPoint JIT(0) 
Jump bb2(v5, v6) 
>]; bb1:v7 -> bb2:params:n; @@ -7336,7 +7501,7 @@ mod graphviz_tests { #[cfg(test)] mod opt_tests { use super::*; - use crate::options::*; + use crate::{hir_strings, options::*}; use insta::assert_snapshot; #[track_caller] @@ -7365,17 +7530,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:TrueClass = Const Value(true) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:TrueClass = Const Value(true) CheckInterrupts - v20:Fixnum[3] = Const Value(3) + v22:Fixnum[3] = Const Value(3) CheckInterrupts - Return v20 + Return v22 "); } @@ -7395,17 +7561,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:FalseClass = Const Value(false) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:FalseClass = Const Value(false) CheckInterrupts - v31:Fixnum[4] = Const Value(4) + v33:Fixnum[4] = Const Value(4) CheckInterrupts - Return v31 + Return v33 "); } @@ -7422,7 +7589,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7450,7 +7617,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[5] = Const Value(5) @@ -7478,7 +7645,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[0] = Const Value(0) @@ -7503,7 +7670,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[6] = Const Value(6) @@ -7529,7 +7696,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[0] = Const Value(0) @@ -7564,7 +7731,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7595,7 +7762,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7631,7 +7798,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[2] = Const Value(2) @@ -7662,7 +7829,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[2] = Const Value(2) @@ -7698,7 +7865,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7729,7 +7896,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[2] = Const Value(2) @@ -7760,7 +7927,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -7792,7 +7959,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[2] = Const Value(2) @@ -7821,7 +7988,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -7842,64 +8009,58 @@ mod opt_tests { def post(*rest, post) = post def block(&b) = nil "); - - assert_snapshot!(hir_string("rest"), @r" + assert_snapshot!(hir_strings!("rest", "kw", "kw_rest", "block", "post"), @r" fn rest@:2: bb0(v1:BasicObject, v2:ArrayExact): EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:ArrayExact): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:ArrayExact): CheckInterrupts Return v9 - "); - // extra hidden param for the set of specified keywords - assert_snapshot!(hir_string("kw"), @r" + fn kw@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): CheckInterrupts Return v11 - "); - assert_snapshot!(hir_string("kw_rest"), @r" + fn kw_rest@:4: bb0(v1:BasicObject, v2:BasicObject): EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): CheckInterrupts Return v9 - "); - assert_snapshot!(hir_string("block"), @r" + fn block@:6: bb0(v1:BasicObject, v2:BasicObject): EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:NilClass = Const Value(nil) CheckInterrupts Return v13 - "); - assert_snapshot!(hir_string("post"), @r" + fn post@:5: bb0(v1:BasicObject, v2:ArrayExact, v3:BasicObject): EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:ArrayExact, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:ArrayExact, v12:BasicObject): CheckInterrupts @@ -7923,7 +8084,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) @@ -7951,7 +8112,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = SendWithoutBlock v6, :foo @@ -7977,7 +8138,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) @@ -8002,7 +8163,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[3] = Const Value(3) @@ -8030,7 +8191,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -8062,7 +8223,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) @@ -8090,7 +8251,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -8120,7 +8281,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v19:BasicObject = SendWithoutBlock v11, :+, v12 @@ -8141,7 +8302,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) @@ -8165,7 +8326,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8189,7 +8350,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8213,7 +8374,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) @@ -8237,7 +8398,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8261,7 +8422,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8286,17 +8447,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:Fixnum[2] = Const Value(2) - v14:Fixnum[1] = Const Value(1) - v22:RangeExact = NewRangeFixnum v14 NewRangeInclusive v11 + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[2] = Const Value(2) + v16:Fixnum[1] = Const Value(1) + v24:RangeExact = NewRangeFixnum v16 NewRangeInclusive v13 CheckInterrupts - Return v22 + Return v24 "); } @@ -8314,17 +8476,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:Fixnum[2] = Const Value(2) - v14:Fixnum[1] = Const Value(1) - v22:RangeExact = NewRangeFixnum v14 NewRangeExclusive v11 + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[2] = Const Value(2) + v16:Fixnum[1] = Const Value(1) + v24:RangeExact = NewRangeFixnum v16 NewRangeExclusive v13 CheckInterrupts - Return v22 + Return v24 "); } @@ -8342,7 +8505,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8367,7 +8530,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -8392,7 +8555,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[10] = Const Value(10) @@ -8417,7 +8580,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[10] = Const Value(10) @@ -8441,16 +8604,17 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v12:ArrayExact = NewArray - v15:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray + v17:Fixnum[5] = Const Value(5) CheckInterrupts - Return v15 + Return v17 "); } @@ -8467,16 +8631,17 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v14:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:RangeExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v16:Fixnum[5] = Const Value(5) CheckInterrupts - Return v14 + Return v16 "); } @@ -8493,21 +8658,22 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) - v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v14:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v16:StringExact = StringCopy v14 - v18:RangeExact = NewRange v13 NewRangeInclusive v16 + v15:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v16:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:StringExact = StringCopy v16 + v20:RangeExact = NewRange v15 NewRangeInclusive v18 PatchPoint NoEPEscape(test) - v23:Fixnum[0] = Const Value(0) + v25:Fixnum[0] = Const Value(0) CheckInterrupts - Return v23 + Return v25 "); } @@ -8524,16 +8690,17 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v10:NilClass = Const Value(nil) - v15:ArrayExact = NewArray v9 - v18:Fixnum[5] = Const Value(5) + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + v17:ArrayExact = NewArray v11 + v20:Fixnum[5] = Const Value(5) CheckInterrupts - Return v18 + Return v20 "); } @@ -8549,17 +8716,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v12:HashExact = NewHash + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:HashExact = NewHash PatchPoint NoEPEscape(test) - v17:Fixnum[5] = Const Value(5) + v19:Fixnum[5] = Const Value(5) CheckInterrupts - Return v17 + Return v19 "); } @@ -8575,19 +8743,20 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject, v3:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2, v3) - bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT - Jump bb2(v6, v7, v8) - bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): - v13:NilClass = Const Value(nil) - v17:StaticSymbol[:a] = Const Value(VALUE(0x1000)) - v18:StaticSymbol[:b] = Const Value(VALUE(0x1008)) - v20:HashExact = NewHash v17: v11, v18: v12 + v4:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3, v4) + bb1(v7:BasicObject, v8:BasicObject, v9:BasicObject): + EntryPoint JIT(0) + v10:NilClass = Const Value(nil) + Jump bb2(v7, v8, v9, v10) + bb2(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:NilClass): + v19:StaticSymbol[:a] = Const Value(VALUE(0x1000)) + v20:StaticSymbol[:b] = Const Value(VALUE(0x1008)) + v22:HashExact = NewHash v19: v13, v20: v14 PatchPoint NoEPEscape(test) - v25:Fixnum[5] = Const Value(5) + v27:Fixnum[5] = Const Value(5) CheckInterrupts - Return v25 + Return v27 "); } @@ -8604,17 +8773,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:ArrayExact = ArrayDup v11 - v16:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:ArrayExact = ArrayDup v13 + v18:Fixnum[5] = Const Value(5) CheckInterrupts - Return v16 + Return v18 "); } @@ -8630,17 +8800,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:HashExact = HashDup v11 - v16:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:HashExact = HashDup v13 + v18:Fixnum[5] = Const Value(5) CheckInterrupts - Return v16 + Return v18 "); } @@ -8657,15 +8828,16 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v13:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v15:Fixnum[5] = Const Value(5) CheckInterrupts - Return v13 + Return v15 "); } @@ -8682,17 +8854,18 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v13:StringExact = StringCopy v11 - v16:Fixnum[5] = Const Value(5) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v15:StringExact = StringCopy v13 + v18:Fixnum[5] = Const Value(5) CheckInterrupts - Return v16 + Return v18 "); } @@ -8711,7 +8884,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_PLUS) @@ -8738,7 +8911,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MINUS) @@ -8765,7 +8938,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MULT) @@ -8792,7 +8965,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_DIV) @@ -8820,7 +8993,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_MOD) @@ -8848,7 +9021,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LT) @@ -8875,7 +9048,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_LE) @@ -8902,7 +9075,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GT) @@ -8929,7 +9102,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_GE) @@ -8956,7 +9129,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) @@ -8983,7 +9156,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, BOP_EQ) @@ -9010,7 +9183,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = GetConstantPath 0x1000 @@ -9033,7 +9206,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) @@ -9055,7 +9228,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:ArrayExact = NewArray @@ -9078,19 +9251,20 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v12:ArrayExact = NewArray + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) - v26:BasicObject = CCall itself@0x1038, v12 + v28:BasicObject = CCall itself@0x1038, v14 PatchPoint NoEPEscape(test) - v19:Fixnum[1] = Const Value(1) + v21:Fixnum[1] = Const Value(1) CheckInterrupts - Return v19 + Return v21 "); } @@ -9108,21 +9282,22 @@ mod opt_tests { fn test@:4: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v27:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) - v29:StringExact|NilClass = CCall name@0x1048, v27 + v31:StringExact|NilClass = CCall name@0x1048, v29 PatchPoint NoEPEscape(test) - v19:Fixnum[1] = Const Value(1) + v21:Fixnum[1] = Const Value(1) CheckInterrupts - Return v19 + Return v21 "); } @@ -9138,18 +9313,19 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v12:ArrayExact = NewArray + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) - v26:Fixnum = CCall length@0x1038, v12 - v19:Fixnum[5] = Const Value(5) + v28:Fixnum = CCall length@0x1038, v14 + v21:Fixnum[5] = Const Value(5) CheckInterrupts - Return v19 + Return v21 "); } @@ -9166,7 +9342,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9189,7 +9365,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9222,7 +9398,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9251,7 +9427,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9274,18 +9450,19 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v12:ArrayExact = NewArray + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v14:ArrayExact = NewArray PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) - v26:Fixnum = CCall size@0x1038, v12 - v19:Fixnum[5] = Const Value(5) + v28:Fixnum = CCall size@0x1038, v14 + v21:Fixnum[5] = Const Value(5) CheckInterrupts - Return v19 + Return v21 "); } @@ -9303,7 +9480,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -9325,7 +9502,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:Fixnum[1] = Const Value(1) @@ -9348,18 +9525,19 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject, v2:BasicObject): EntryPoint interpreter - Jump bb2(v1, v2) - bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT - Jump bb2(v5, v6) - bb2(v8:BasicObject, v9:BasicObject): - v10:NilClass = Const Value(nil) - v14:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - v16:ArrayExact = ArrayDup v14 + v3:NilClass = Const Value(nil) + Jump bb2(v1, v2, v3) + bb1(v6:BasicObject, v7:BasicObject): + EntryPoint JIT(0) + v8:NilClass = Const Value(nil) + Jump bb2(v6, v7, v8) + bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): + v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) + v18:ArrayExact = ArrayDup v16 PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) - v27:BasicObject = SendWithoutBlockDirect v16, :first (0x1040) + v29:BasicObject = SendWithoutBlockDirect v18, :first (0x1040) CheckInterrupts - Return v27 + Return v29 "); } @@ -9377,7 +9555,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9411,7 +9589,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) @@ -9436,7 +9614,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -9460,7 +9638,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = Send v6, 0x1000, :foo @@ -9485,19 +9663,20 @@ mod opt_tests { fn test@:4: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:Fixnum[1] = Const Value(1) - SetLocal l0, EP@3, v11 - v16:BasicObject = Send v6, 0x1000, :foo - v17:BasicObject = GetLocal l0, EP@3 - v20:BasicObject = GetLocal l0, EP@3 + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) + SetLocal l0, EP@3, v13 + v18:BasicObject = Send v8, 0x1000, :foo + v19:BasicObject = GetLocal l0, EP@3 + v22:BasicObject = GetLocal l0, EP@3 CheckInterrupts - Return v20 + Return v22 "); } @@ -9515,7 +9694,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -9539,7 +9718,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -9561,7 +9740,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -9582,7 +9761,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -9605,7 +9784,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = GetConstantPath 0x1000 @@ -9627,7 +9806,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:BasicObject = GetConstantPath 0x1000 @@ -9648,7 +9827,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9677,7 +9856,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9701,7 +9880,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9735,7 +9914,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9765,7 +9944,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9794,7 +9973,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9823,7 +10002,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9852,7 +10031,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9880,7 +10059,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9910,7 +10089,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9937,7 +10116,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -9967,7 +10146,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 @@ -9989,7 +10168,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): v17:ArrayExact = NewArray v11, v12 @@ -10011,7 +10190,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): GuardBlockParamProxy l0 @@ -10033,7 +10212,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -10054,7 +10233,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -10076,7 +10255,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10100,7 +10279,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): SideExit PatchPoint(BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE)) @@ -10118,7 +10297,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10140,7 +10319,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:HashExact = NewHash @@ -10162,7 +10341,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:HashExact = NewHash @@ -10184,7 +10363,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10205,7 +10384,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10227,7 +10406,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:ArrayExact = NewArray @@ -10249,7 +10428,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v11:ArrayExact = NewArray @@ -10271,7 +10450,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10292,7 +10471,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10314,7 +10493,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10337,7 +10516,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10360,7 +10539,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_UMINUS) @@ -10381,7 +10560,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10403,7 +10582,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10426,7 +10605,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10449,7 +10628,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10477,7 +10656,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10506,7 +10685,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10532,7 +10711,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10558,16 +10737,17 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:NilClass = Const Value(nil) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:NilClass = Const Value(nil) CheckInterrupts CheckInterrupts - Return v11 + Return v13 "); } @@ -10584,18 +10764,19 @@ mod opt_tests { fn test@:3: bb0(v1:BasicObject): EntryPoint interpreter - Jump bb2(v1) - bb1(v4:BasicObject): - EntryPoint JIT - Jump bb2(v4) - bb2(v6:BasicObject): - v7:NilClass = Const Value(nil) - v11:Fixnum[1] = Const Value(1) + v2:NilClass = Const Value(nil) + Jump bb2(v1, v2) + bb1(v5:BasicObject): + EntryPoint JIT(0) + v6:NilClass = Const Value(nil) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:NilClass): + v13:Fixnum[1] = Const Value(1) CheckInterrupts PatchPoint MethodRedefined(Integer@0x1000, itself@0x1008, cme:0x1010) - v31:BasicObject = CCall itself@0x1038, v11 + v33:BasicObject = CCall itself@0x1038, v13 CheckInterrupts - Return v31 + Return v33 "); } @@ -10610,7 +10791,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10634,7 +10815,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10658,7 +10839,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10682,7 +10863,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10709,7 +10890,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) @@ -10735,7 +10916,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10763,7 +10944,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -10785,7 +10966,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) @@ -10805,7 +10986,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:NilClass = Const Value(nil) @@ -10830,7 +11011,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:NilClass = Const Value(nil) @@ -10852,7 +11033,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -10877,7 +11058,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): v10:Fixnum[1] = Const Value(1) @@ -10901,7 +11082,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(NilClass@0x1000, nil?@0x1008, cme:0x1010) @@ -10925,7 +11106,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(FalseClass@0x1000, nil?@0x1008, cme:0x1010) @@ -10949,7 +11130,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(TrueClass@0x1000, nil?@0x1008, cme:0x1010) @@ -10973,7 +11154,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Symbol@0x1000, nil?@0x1008, cme:0x1010) @@ -10997,7 +11178,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Integer@0x1000, nil?@0x1008, cme:0x1010) @@ -11021,7 +11202,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Float@0x1000, nil?@0x1008, cme:0x1010) @@ -11045,7 +11226,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) @@ -11069,7 +11250,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) @@ -11093,7 +11274,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) @@ -11117,7 +11298,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) @@ -11142,7 +11323,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) @@ -11166,7 +11347,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 28) @@ -11191,7 +11372,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2, v3) bb1(v6:BasicObject, v7:BasicObject, v8:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:BasicObject): PatchPoint BOPRedefined(INTEGER_REDEFINED_OP_FLAG, 29) @@ -11218,7 +11399,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) @@ -11250,7 +11431,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) @@ -11283,7 +11464,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) @@ -11327,7 +11508,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): v14:BasicObject = SendWithoutBlock v9, :foo @@ -11354,7 +11535,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -11386,7 +11567,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1) bb1(v4:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v4) bb2(v6:BasicObject): PatchPoint SingleRactorMode @@ -11417,7 +11598,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) @@ -11446,7 +11627,7 @@ mod opt_tests { EntryPoint interpreter Jump bb2(v1, v2) bb1(v5:BasicObject, v6:BasicObject): - EntryPoint JIT + EntryPoint JIT(0) Jump bb2(v5, v6) bb2(v8:BasicObject, v9:BasicObject): PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 3d9ce07c36cf20..6d4525084ef849 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -110,7 +110,6 @@ make_counters! { exit_obj_to_string_fallback, exit_interrupt, exit_stackoverflow, - exit_optional_arguments, exit_block_param_proxy_modified, exit_block_param_proxy_not_iseq_or_ifunc, } @@ -123,8 +122,14 @@ make_counters! { compile_error_register_spill_on_alloc, compile_error_parse_stack_underflow, compile_error_parse_malformed_iseq, - compile_error_parse_validation, + compile_error_parse_failed_optional_arguments, compile_error_parse_not_allowed, + compile_error_validation_block_has_no_terminator, + compile_error_validation_terminator_not_at_end, + compile_error_validation_mismatched_block_arity, + compile_error_validation_jump_target_not_in_rpo, + compile_error_validation_operand_not_defined, + compile_error_validation_duplicate_instruction, // The number of times YARV instructions are executed on JIT code zjit_insn_count, @@ -203,6 +208,7 @@ pub enum CompileError { /// Return a raw pointer to the exit counter for a given CompileError pub fn exit_counter_for_compile_error(compile_error: &CompileError) -> Counter { use crate::hir::ParseError::*; + use crate::hir::ValidationError::*; use crate::stats::CompileError::*; use crate::stats::Counter::*; match compile_error { @@ -212,10 +218,18 @@ pub fn exit_counter_for_compile_error(compile_error: &CompileError) -> Counter { RegisterSpillOnAlloc => compile_error_register_spill_on_alloc, RegisterSpillOnCCall => compile_error_register_spill_on_ccall, ParseError(parse_error) => match parse_error { - StackUnderflow(_) => compile_error_parse_stack_underflow, - MalformedIseq(_) => compile_error_parse_malformed_iseq, - Validation(_) => compile_error_parse_validation, - NotAllowed => compile_error_parse_not_allowed, + StackUnderflow(_) => compile_error_parse_stack_underflow, + MalformedIseq(_) => compile_error_parse_malformed_iseq, + FailedOptionalArguments => compile_error_parse_failed_optional_arguments, + NotAllowed => compile_error_parse_not_allowed, + Validation(validation) => match validation { + BlockHasNoTerminator(_) => compile_error_validation_block_has_no_terminator, + TerminatorNotAtEnd(_, _, _) => compile_error_validation_terminator_not_at_end, + MismatchedBlockArity(_, _, _) => compile_error_validation_mismatched_block_arity, + JumpTargetNotInRPO(_) => compile_error_validation_jump_target_not_in_rpo, + OperandNotDefined(_, _, _) => compile_error_validation_operand_not_defined, + DuplicateInstruction(_, _) => compile_error_validation_duplicate_instruction, + }, } } } @@ -246,7 +260,6 @@ pub fn exit_counter_ptr(reason: crate::hir::SideExitReason) -> *mut u64 { StackOverflow => exit_stackoverflow, BlockParamProxyModified => exit_block_param_proxy_modified, BlockParamProxyNotIseqOrIfunc => exit_block_param_proxy_not_iseq_or_ifunc, - OptionalArgumentsSupplied => exit_optional_arguments, }; counter_ptr(counter) } From cc1fd6490c920520436ff3bf0d9cf6b180b8a544 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 25 Sep 2025 17:05:51 -0700 Subject: [PATCH 13/13] ZJIT: Remove an obsoleted PC guard A little follow-up on https://github.com/ruby/ruby/pull/14653 Now that we don't generate a PC-less side exit at the entry block, we shouldn't need this guard that was added by https://github.com/ruby/ruby/pull/14643. --- zjit/src/backend/lir.rs | 9 +++------ zjit/src/hir.rs | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/zjit/src/backend/lir.rs b/zjit/src/backend/lir.rs index e931e14e8f24e9..21adc42cd1c753 100644 --- a/zjit/src/backend/lir.rs +++ b/zjit/src/backend/lir.rs @@ -1607,12 +1607,9 @@ impl Assembler self.store(Opnd::mem(64, SP, (-local_size_and_idx_to_ep_offset(locals.len(), idx) - 1) * SIZEOF_VALUE_I32), opnd); } - // Avoid setting cfp->pc when exiting entry_block with optional arguments - if !pc.is_null() { - asm_comment!(self, "save cfp->pc"); - self.load_into(SCRATCH_OPND, Opnd::const_ptr(pc)); - self.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), SCRATCH_OPND); - } + asm_comment!(self, "save cfp->pc"); + self.load_into(SCRATCH_OPND, Opnd::const_ptr(pc)); + self.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), SCRATCH_OPND); asm_comment!(self, "save cfp->sp"); self.lea_into(SCRATCH_OPND, Opnd::mem(64, SP, stack.len() as i32 * SIZEOF_VALUE_I32)); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 63749f0991f467..598bcb00c4e082 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -3323,7 +3323,6 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // optimizing locals in that case because they're shared with other frames. let ep_escaped = iseq_escapes_ep(iseq); - // Compile an entry_block for the interpreter compile_entry_block(&mut fun, &jit_entry_insns);