diff --git a/array.c b/array.c index 90e83fd3ad972f..ac7b73c0dddf49 100644 --- a/array.c +++ b/array.c @@ -2694,18 +2694,33 @@ ary_enum_length(VALUE ary, VALUE args, VALUE eobj) return rb_ary_length(ary); } -// Primitive to avoid a race condition in Array#each. -// Return `true` and write `value` and `index` if the element exists. -static VALUE -ary_fetch_next(VALUE self, VALUE *index, VALUE *value) +// Return true if the index is at or past the end of the array. +VALUE +rb_jit_ary_at_end(rb_execution_context_t *ec, VALUE self, VALUE index) { - long i = NUM2LONG(*index); - if (i >= RARRAY_LEN(self)) { - return Qfalse; - } - *value = RARRAY_AREF(self, i); - *index = LONG2NUM(i + 1); - return Qtrue; + return FIX2LONG(index) >= RARRAY_LEN(self) ? Qtrue : Qfalse; +} + +// Return the element at the given fixnum index. +VALUE +rb_jit_ary_at(rb_execution_context_t *ec, VALUE self, VALUE index) +{ + return RARRAY_AREF(self, FIX2LONG(index)); +} + +// Increment a fixnum by 1. +VALUE +rb_jit_fixnum_inc(rb_execution_context_t *ec, VALUE self, VALUE num) +{ + return LONG2FIX(FIX2LONG(num) + 1); +} + +// Push a value onto an array and return the value. +VALUE +rb_jit_ary_push(rb_execution_context_t *ec, VALUE self, VALUE ary, VALUE val) +{ + rb_ary_push(ary, val); + return val; } /* diff --git a/array.rb b/array.rb index f45e6a0d5e5ade..7ee4e09a4c00eb 100644 --- a/array.rb +++ b/array.rb @@ -217,15 +217,15 @@ def fetch_values(*indexes, &block) undef :each def each # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - yield value + i = 0 + until Primitive.rb_jit_ary_at_end(i) + yield Primitive.rb_jit_ary_at(i) + i = Primitive.rb_jit_fixnum_inc(i) end self end @@ -235,18 +235,18 @@ def each # :nodoc: undef :map def map # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil + i = 0 result = Primitive.ary_sized_alloc - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) - value = yield(value) - Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + until Primitive.rb_jit_ary_at_end(i) + value = yield(Primitive.rb_jit_ary_at(i)) + Primitive.rb_jit_ary_push(result, value) + i = Primitive.rb_jit_fixnum_inc(i) end result end @@ -261,19 +261,20 @@ def map # :nodoc: undef :select def select # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil + i = 0 result = Primitive.ary_sized_alloc - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + until Primitive.rb_jit_ary_at_end(i) + value = Primitive.rb_jit_ary_at(i) if yield value - Primitive.cexpr!(%q{ rb_ary_push(result, value) }) + Primitive.rb_jit_ary_push(result, value) end + i = Primitive.rb_jit_fixnum_inc(i) end result end @@ -288,15 +289,16 @@ def select # :nodoc: undef :find def find(if_none_proc = nil) # :nodoc: - Primitive.attr! :inline_block, :c_trace + Primitive.attr! :inline_block, :c_trace, :without_interrupts unless defined?(yield) return Primitive.cexpr! 'SIZED_ENUMERATOR(self, 0, 0, ary_enum_length)' end - _i = 0 - value = nil - while Primitive.cexpr!(%q{ ary_fetch_next(self, LOCAL_PTR(_i), LOCAL_PTR(value)) }) + i = 0 + until Primitive.rb_jit_ary_at_end(i) + value = Primitive.rb_jit_ary_at(i) return value if yield(value) + i = Primitive.rb_jit_fixnum_inc(i) end if_none_proc&.call end diff --git a/compile.c b/compile.c index a5a0ab46e9b8bc..5d6cfb1e1690c5 100644 --- a/compile.c +++ b/compile.c @@ -4433,6 +4433,7 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization; const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction; const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification; + const int do_without_ints = ISEQ_BODY(iseq)->builtin_attrs & BUILTIN_ATTR_WITHOUT_INTERRUPTS; int rescue_level = 0; int tailcallopt = do_tailcallopt; @@ -4465,6 +4466,22 @@ iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor) insn_operands_unification((INSN *)list); } + if (do_without_ints) { + INSN *item = (INSN *)list; + if (IS_INSN_ID(item, jump)) { + item->insn_id = BIN(jump_without_ints); + } + else if (IS_INSN_ID(item, branchif)) { + item->insn_id = BIN(branchif_without_ints); + } + else if (IS_INSN_ID(item, branchunless)) { + item->insn_id = BIN(branchunless_without_ints); + } + else if (IS_INSN_ID(item, branchnil)) { + item->insn_id = BIN(branchnil_without_ints); + } + } + if (do_block_optimization) { INSN * item = (INSN *)list; // Give up if there is a throw @@ -9223,6 +9240,9 @@ compile_builtin_attr(rb_iseq_t *iseq, const NODE *node) // Let the iseq act like a C method in backtraces ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE; } + else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS; + } else { goto unknown_arg; } diff --git a/insns.def b/insns.def index f9a334d824b31a..b7a60f3d5d3a2a 100644 --- a/insns.def +++ b/insns.def @@ -1265,6 +1265,56 @@ branchnil } } +/* same as jump, but without interrupt check */ +DEFINE_INSN +jump_without_ints +(OFFSET dst) +() +() +// attr bool leaf = true; +{ + JUMP(dst); +} + +/* same as branchif, but without interrupt check */ +DEFINE_INSN +branchif_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (RTEST(val)) { + JUMP(dst); + } +} + +/* same as branchunless, but without interrupt check */ +DEFINE_INSN +branchunless_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (!RTEST(val)) { + JUMP(dst); + } +} + +/* same as branchnil, but without interrupt check */ +DEFINE_INSN +branchnil_without_ints +(OFFSET dst) +(VALUE val) +() +// attr bool leaf = true; +{ + if (NIL_P(val)) { + JUMP(dst); + } +} + /**********************************************************/ /* for optimize */ /**********************************************************/ diff --git a/jit_hook.rb b/jit_hook.rb index 346b7169480031..c605d6e26d3e7a 100644 --- a/jit_hook.rb +++ b/jit_hook.rb @@ -2,10 +2,10 @@ class Module # Internal helper for built-in initializations to define methods only when JIT is enabled. # This method is removed in jit_undef.rb. private def with_jit(&block) # :nodoc: - # ZJIT currently doesn't compile Array#each properly, so it's disabled for now. - if defined?(RubyVM::ZJIT) && false # TODO: remove `&& false` (Shopify/ruby#667) + if defined?(RubyVM::ZJIT) RubyVM::ZJIT.send(:add_jit_hook, block) - elsif defined?(RubyVM::YJIT) + end + if defined?(RubyVM::YJIT) RubyVM::YJIT.send(:add_jit_hook, block) end end diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb index aa495696adce3c..64f289a7b41e50 100644 --- a/lib/rubygems/specification.rb +++ b/lib/rubygems/specification.rb @@ -34,6 +34,15 @@ # Starting in RubyGems 2.0, a Specification can hold arbitrary # metadata. See #metadata for restrictions on the format and size of metadata # items you may add to a specification. +# +# Specifications must be deterministic, as in the example above. For instance, +# you cannot define attributes conditionally: +# +# # INVALID: do not do this. +# unless RUBY_ENGINE == "jruby" +# s.extensions << "ext/example/extconf.rb" +# end +# class Gem::Specification < Gem::BasicSpecification # REFACTOR: Consider breaking out this version stuff into a separate diff --git a/prism_compile.c b/prism_compile.c index 731859b35aee80..5d83e55a776136 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -3441,6 +3441,9 @@ pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, cons // Let the iseq act like a C method in backtraces ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_C_TRACE; } + else if (strcmp(RSTRING_PTR(string), "without_interrupts") == 0) { + ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_WITHOUT_INTERRUPTS; + } else { COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string)); return COMPILE_NG; diff --git a/proc.c b/proc.c index f0cdcae7b1da7f..67963a1b011f9c 100644 --- a/proc.c +++ b/proc.c @@ -2006,6 +2006,8 @@ method_eq(VALUE method, VALUE other) klass1 = method_entry_defined_class(m1->me); klass2 = method_entry_defined_class(m2->me); + if (RB_TYPE_P(klass1, T_ICLASS)) klass1 = RBASIC_CLASS(klass1); + if (RB_TYPE_P(klass2, T_ICLASS)) klass2 = RBASIC_CLASS(klass2); if (!rb_method_entry_eq(m1->me, m2->me) || klass1 != klass2 || diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index 8561f841a8cda6..c3819cdebf7850 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -111,6 +111,20 @@ def foo() :derived; end end end + def test_unbound_method_equality_with_extended_module + m = Module.new { def hello; "hello"; end } + base = Class.new { extend m } + sub = Class.new(base) + + from_module = m.instance_method(:hello) + from_base = base.method(:hello).unbind + from_sub = sub.method(:hello).unbind + + assert_equal(from_module, from_base) + assert_equal(from_module, from_sub) + assert_equal(from_base, from_sub) + end + def test_callee assert_equal(:test_callee, __method__) assert_equal(:m, Class.new {def m; __method__; end}.new.m) diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 7b068e9898fc80..095b690c7b3c49 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -3380,6 +3380,21 @@ def test = RUBY_COPYRIGHT }, call_threshold: 1, insns: [:opt_getconstant_path] end + def test_getconstant + assert_compiles '1', %q{ + class Foo + CONST = 1 + end + + def test(klass) + klass::CONST + end + + test(Foo) + test(Foo) + }, call_threshold: 2, insns: [:getconstant] + end + def test_expandarray_no_splat assert_compiles '[3, 4]', %q{ def test(o) diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 5aa07962f93b4b..3ab36ec45c323d 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -6,7 +6,7 @@ SUBLIBS = {} REQUIRED = {} -BUILTIN_ATTRS = %w[leaf inline_block use_block c_trace] +BUILTIN_ATTRS = %w[leaf inline_block use_block c_trace without_interrupts] module CompileWarning @@warnings = 0 diff --git a/vm_core.h b/vm_core.h index b700a32909d6c7..5f0266da1da516 100644 --- a/vm_core.h +++ b/vm_core.h @@ -403,6 +403,8 @@ enum rb_builtin_attr { BUILTIN_ATTR_INLINE_BLOCK = 0x04, // The iseq acts like a C method in backtraces. BUILTIN_ATTR_C_TRACE = 0x08, + // The iseq uses noint branch/jump opcodes that skip interrupt checking. + BUILTIN_ATTR_WITHOUT_INTERRUPTS = 0x10, }; typedef VALUE (*rb_jit_func_t)(struct rb_execution_context_struct *, struct rb_control_frame_struct *); diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 0fbca85716dff0..0a4832d0949b56 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4641,7 +4641,7 @@ fn gen_branchif( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchif_without_ints as usize { gen_check_ints(asm, Counter::branchif_interrupted); } @@ -4693,7 +4693,7 @@ fn gen_branchunless( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchunless_without_ints as usize { gen_check_ints(asm, Counter::branchunless_interrupted); } @@ -4746,7 +4746,7 @@ fn gen_branchnil( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_branchnil_without_ints as usize { gen_check_ints(asm, Counter::branchnil_interrupted); } @@ -4901,7 +4901,7 @@ fn gen_jump( let jump_offset = jit.get_arg(0).as_i32(); // Check for interrupts, but only on backward branches that may create loops - if jump_offset < 0 { + if jump_offset < 0 && jit.get_opcode() != YARVINSN_jump_without_ints as usize { gen_check_ints(asm, Counter::jump_interrupted); } @@ -10777,6 +10777,10 @@ fn get_gen_fn(opcode: VALUE) -> Option { YARVINSN_branchnil => Some(gen_branchnil), YARVINSN_throw => Some(gen_throw), YARVINSN_jump => Some(gen_jump), + YARVINSN_branchif_without_ints => Some(gen_branchif), + YARVINSN_branchunless_without_ints => Some(gen_branchunless), + YARVINSN_branchnil_without_ints => Some(gen_branchnil), + YARVINSN_jump_without_ints => Some(gen_jump), YARVINSN_opt_new => Some(gen_opt_new), YARVINSN_getblockparamproxy => Some(gen_getblockparamproxy), diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index 57824f49118358..9ce8b5a020042b 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -486,6 +486,7 @@ pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1; pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2; pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4; pub const BUILTIN_ATTR_C_TRACE: rb_builtin_attr = 8; +pub const BUILTIN_ATTR_WITHOUT_INTERRUPTS: rb_builtin_attr = 16; pub type rb_builtin_attr = u32; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -775,181 +776,189 @@ pub const YARVINSN_jump: ruby_vminsn_type = 72; pub const YARVINSN_branchif: ruby_vminsn_type = 73; pub const YARVINSN_branchunless: ruby_vminsn_type = 74; pub const YARVINSN_branchnil: ruby_vminsn_type = 75; -pub const YARVINSN_once: ruby_vminsn_type = 76; -pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 77; -pub const YARVINSN_opt_plus: ruby_vminsn_type = 78; -pub const YARVINSN_opt_minus: ruby_vminsn_type = 79; -pub const YARVINSN_opt_mult: ruby_vminsn_type = 80; -pub const YARVINSN_opt_div: ruby_vminsn_type = 81; -pub const YARVINSN_opt_mod: ruby_vminsn_type = 82; -pub const YARVINSN_opt_eq: ruby_vminsn_type = 83; -pub const YARVINSN_opt_neq: ruby_vminsn_type = 84; -pub const YARVINSN_opt_lt: ruby_vminsn_type = 85; -pub const YARVINSN_opt_le: ruby_vminsn_type = 86; -pub const YARVINSN_opt_gt: ruby_vminsn_type = 87; -pub const YARVINSN_opt_ge: ruby_vminsn_type = 88; -pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 89; -pub const YARVINSN_opt_and: ruby_vminsn_type = 90; -pub const YARVINSN_opt_or: ruby_vminsn_type = 91; -pub const YARVINSN_opt_aref: ruby_vminsn_type = 92; -pub const YARVINSN_opt_aset: ruby_vminsn_type = 93; -pub const YARVINSN_opt_length: ruby_vminsn_type = 94; -pub const YARVINSN_opt_size: ruby_vminsn_type = 95; -pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 96; -pub const YARVINSN_opt_succ: ruby_vminsn_type = 97; -pub const YARVINSN_opt_not: ruby_vminsn_type = 98; -pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 99; -pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 100; -pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 101; -pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 102; -pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 103; -pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 104; -pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 105; -pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 106; -pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 107; -pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 108; -pub const YARVINSN_trace_nop: ruby_vminsn_type = 109; -pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 110; -pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 111; -pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 112; -pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 113; -pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 114; -pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 115; -pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 116; -pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 117; -pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 118; -pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 119; -pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 120; -pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 121; -pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 122; -pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 123; -pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 124; -pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 125; -pub const YARVINSN_trace_putnil: ruby_vminsn_type = 126; -pub const YARVINSN_trace_putself: ruby_vminsn_type = 127; -pub const YARVINSN_trace_putobject: ruby_vminsn_type = 128; -pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 129; -pub const YARVINSN_trace_putstring: ruby_vminsn_type = 130; -pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 131; -pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 132; -pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 133; -pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 134; -pub const YARVINSN_trace_intern: ruby_vminsn_type = 135; -pub const YARVINSN_trace_newarray: ruby_vminsn_type = 136; -pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 137; -pub const YARVINSN_trace_duparray: ruby_vminsn_type = 138; -pub const YARVINSN_trace_duphash: ruby_vminsn_type = 139; -pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 140; -pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 141; -pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 142; -pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 143; -pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 144; -pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 145; -pub const YARVINSN_trace_newhash: ruby_vminsn_type = 146; -pub const YARVINSN_trace_newrange: ruby_vminsn_type = 147; -pub const YARVINSN_trace_pop: ruby_vminsn_type = 148; -pub const YARVINSN_trace_dup: ruby_vminsn_type = 149; -pub const YARVINSN_trace_dupn: ruby_vminsn_type = 150; -pub const YARVINSN_trace_swap: ruby_vminsn_type = 151; -pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 152; -pub const YARVINSN_trace_topn: ruby_vminsn_type = 153; -pub const YARVINSN_trace_setn: ruby_vminsn_type = 154; -pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 155; -pub const YARVINSN_trace_defined: ruby_vminsn_type = 156; -pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 157; -pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 158; -pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 159; -pub const YARVINSN_trace_checktype: ruby_vminsn_type = 160; -pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 161; -pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 162; -pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 163; -pub const YARVINSN_trace_send: ruby_vminsn_type = 164; -pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 165; -pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 166; -pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 167; -pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 168; -pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 169; -pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 170; -pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 171; -pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 172; -pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 173; -pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 174; -pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 175; -pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 176; -pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 177; -pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 178; -pub const YARVINSN_trace_leave: ruby_vminsn_type = 179; -pub const YARVINSN_trace_throw: ruby_vminsn_type = 180; -pub const YARVINSN_trace_jump: ruby_vminsn_type = 181; -pub const YARVINSN_trace_branchif: ruby_vminsn_type = 182; -pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 183; -pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 184; -pub const YARVINSN_trace_once: ruby_vminsn_type = 185; -pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 186; -pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 187; -pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 188; -pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 189; -pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 190; -pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 191; -pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 192; -pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 193; -pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 194; -pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 195; -pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 196; -pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 197; -pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 198; -pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 199; -pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 200; -pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 201; -pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 202; -pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 203; -pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 204; -pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 205; -pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 206; -pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 207; -pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 208; -pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 209; -pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 210; -pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 211; -pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 212; -pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 213; -pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 214; -pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 215; -pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 216; -pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 217; -pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 218; -pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 219; -pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 220; -pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 221; -pub const YARVINSN_zjit_send: ruby_vminsn_type = 222; -pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 223; -pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 247; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 248; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 249; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 250; +pub const YARVINSN_jump_without_ints: ruby_vminsn_type = 76; +pub const YARVINSN_branchif_without_ints: ruby_vminsn_type = 77; +pub const YARVINSN_branchunless_without_ints: ruby_vminsn_type = 78; +pub const YARVINSN_branchnil_without_ints: ruby_vminsn_type = 79; +pub const YARVINSN_once: ruby_vminsn_type = 80; +pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 81; +pub const YARVINSN_opt_plus: ruby_vminsn_type = 82; +pub const YARVINSN_opt_minus: ruby_vminsn_type = 83; +pub const YARVINSN_opt_mult: ruby_vminsn_type = 84; +pub const YARVINSN_opt_div: ruby_vminsn_type = 85; +pub const YARVINSN_opt_mod: ruby_vminsn_type = 86; +pub const YARVINSN_opt_eq: ruby_vminsn_type = 87; +pub const YARVINSN_opt_neq: ruby_vminsn_type = 88; +pub const YARVINSN_opt_lt: ruby_vminsn_type = 89; +pub const YARVINSN_opt_le: ruby_vminsn_type = 90; +pub const YARVINSN_opt_gt: ruby_vminsn_type = 91; +pub const YARVINSN_opt_ge: ruby_vminsn_type = 92; +pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 93; +pub const YARVINSN_opt_and: ruby_vminsn_type = 94; +pub const YARVINSN_opt_or: ruby_vminsn_type = 95; +pub const YARVINSN_opt_aref: ruby_vminsn_type = 96; +pub const YARVINSN_opt_aset: ruby_vminsn_type = 97; +pub const YARVINSN_opt_length: ruby_vminsn_type = 98; +pub const YARVINSN_opt_size: ruby_vminsn_type = 99; +pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 100; +pub const YARVINSN_opt_succ: ruby_vminsn_type = 101; +pub const YARVINSN_opt_not: ruby_vminsn_type = 102; +pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 103; +pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 104; +pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 105; +pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 106; +pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 107; +pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 108; +pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 109; +pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 110; +pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 111; +pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 112; +pub const YARVINSN_trace_nop: ruby_vminsn_type = 113; +pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 114; +pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 115; +pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 116; +pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 117; +pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 118; +pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 119; +pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 120; +pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 121; +pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 122; +pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 123; +pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 124; +pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 125; +pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 126; +pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 127; +pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 128; +pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 129; +pub const YARVINSN_trace_putnil: ruby_vminsn_type = 130; +pub const YARVINSN_trace_putself: ruby_vminsn_type = 131; +pub const YARVINSN_trace_putobject: ruby_vminsn_type = 132; +pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 133; +pub const YARVINSN_trace_putstring: ruby_vminsn_type = 134; +pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 135; +pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 136; +pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 137; +pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 138; +pub const YARVINSN_trace_intern: ruby_vminsn_type = 139; +pub const YARVINSN_trace_newarray: ruby_vminsn_type = 140; +pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 141; +pub const YARVINSN_trace_duparray: ruby_vminsn_type = 142; +pub const YARVINSN_trace_duphash: ruby_vminsn_type = 143; +pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 144; +pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 145; +pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 146; +pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 147; +pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 148; +pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 149; +pub const YARVINSN_trace_newhash: ruby_vminsn_type = 150; +pub const YARVINSN_trace_newrange: ruby_vminsn_type = 151; +pub const YARVINSN_trace_pop: ruby_vminsn_type = 152; +pub const YARVINSN_trace_dup: ruby_vminsn_type = 153; +pub const YARVINSN_trace_dupn: ruby_vminsn_type = 154; +pub const YARVINSN_trace_swap: ruby_vminsn_type = 155; +pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 156; +pub const YARVINSN_trace_topn: ruby_vminsn_type = 157; +pub const YARVINSN_trace_setn: ruby_vminsn_type = 158; +pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 159; +pub const YARVINSN_trace_defined: ruby_vminsn_type = 160; +pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 161; +pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 162; +pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 163; +pub const YARVINSN_trace_checktype: ruby_vminsn_type = 164; +pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 165; +pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 166; +pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 167; +pub const YARVINSN_trace_send: ruby_vminsn_type = 168; +pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 169; +pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 170; +pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 171; +pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 172; +pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 173; +pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 174; +pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 175; +pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 176; +pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 177; +pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 178; +pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 179; +pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 180; +pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 181; +pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 182; +pub const YARVINSN_trace_leave: ruby_vminsn_type = 183; +pub const YARVINSN_trace_throw: ruby_vminsn_type = 184; +pub const YARVINSN_trace_jump: ruby_vminsn_type = 185; +pub const YARVINSN_trace_branchif: ruby_vminsn_type = 186; +pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 187; +pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 188; +pub const YARVINSN_trace_jump_without_ints: ruby_vminsn_type = 189; +pub const YARVINSN_trace_branchif_without_ints: ruby_vminsn_type = 190; +pub const YARVINSN_trace_branchunless_without_ints: ruby_vminsn_type = 191; +pub const YARVINSN_trace_branchnil_without_ints: ruby_vminsn_type = 192; +pub const YARVINSN_trace_once: ruby_vminsn_type = 193; +pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 194; +pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 195; +pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 196; +pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 197; +pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 198; +pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 199; +pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 200; +pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 201; +pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 202; +pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 203; +pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 204; +pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 205; +pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 206; +pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 207; +pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 208; +pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 209; +pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 210; +pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 211; +pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 212; +pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 213; +pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 214; +pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 215; +pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 216; +pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 217; +pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 218; +pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 219; +pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 220; +pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 221; +pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 222; +pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 223; +pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 224; +pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_send: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 249; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 250; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 251; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 252; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 253; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 254; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 255; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 256; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 257; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 258; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index d71e75c444af69..dab1a6d92985d6 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -167,6 +167,8 @@ fn main() { .allowlist_var("rb_cClass") .allowlist_var("rb_cRegexp") .allowlist_var("rb_cISeq") + .allowlist_var("rb_cRubyVM") + .allowlist_function("rb_const_get") .allowlist_type("ruby_fl_type") .allowlist_type("ruby_fl_ushift") diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index e9949705fc8b50..29ea47c68e41e7 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -558,6 +558,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio &Insn::IsBlockParamModified { level } => gen_is_block_param_modified(asm, level), &Insn::GetBlockParam { ep_offset, level, state } => gen_getblockparam(jit, asm, ep_offset, level, &function.frame_state(state)), &Insn::SetLocal { val, ep_offset, level } => no_output!(gen_setlocal(asm, opnd!(val), function.type_of(val), ep_offset, level)), + Insn::GetConstant { klass, id, allow_nil, state } => gen_getconstant(jit, asm, opnd!(klass), *id, opnd!(allow_nil), &function.frame_state(*state)), Insn::GetConstantPath { ic, state } => gen_get_constant_path(jit, asm, *ic, &function.frame_state(*state)), Insn::GetClassVar { id, ic, state } => gen_getclassvar(jit, asm, *id, *ic, &function.frame_state(*state)), Insn::SetClassVar { id, val, ic, state } => no_output!(gen_setclassvar(jit, asm, *id, opnd!(val), *ic, &function.frame_state(*state))), @@ -811,6 +812,17 @@ fn gen_get_constant_path(jit: &JITState, asm: &mut Assembler, ic: *const iseq_in asm_ccall!(asm, rb_vm_opt_getconstant_path, EC, CFP, Opnd::const_ptr(ic)) } +fn gen_getconstant(jit: &mut JITState, asm: &mut Assembler, klass: Opnd, id: ID, allow_nil: Opnd, state: &FrameState) -> Opnd { + unsafe extern "C" { + fn rb_vm_get_ev_const(ec: EcPtr, klass: VALUE, id: ID, allow_nil: VALUE) -> VALUE; + } + + // Constant lookup can raise and run arbitrary Ruby code via const_missing. + gen_prepare_non_leaf_call(jit, asm, state); + + asm_ccall!(asm, rb_vm_get_ev_const, EC, klass, id.0.into(), allow_nil) +} + fn gen_fixnum_bit_check(asm: &mut Assembler, val: Opnd, index: u8) -> Opnd { let bit_test: u64 = 0x01 << (index + 1); asm.test(val, bit_test.into()); diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index 8e569793a87eb5..a47d9bf61f60a2 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -1133,6 +1133,12 @@ pub mod test_utils { crate::cruby::ids::init(); // for ID! usages in tests } + // Call ZJIT hooks to install Ruby implementations of builtins like Array#each + unsafe { + let zjit = rb_const_get(rb_cRubyVM, rust_str_to_id("ZJIT")); + rb_funcallv(zjit, rust_str_to_id("call_jit_hooks"), 0, std::ptr::null()); + } + // Set up globals for convenience let zjit_entry = ZJITState::init(); diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 0cd312abf9264c..f178e7672894d9 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -585,6 +585,7 @@ pub const BUILTIN_ATTR_LEAF: rb_builtin_attr = 1; pub const BUILTIN_ATTR_SINGLE_NOARG_LEAF: rb_builtin_attr = 2; pub const BUILTIN_ATTR_INLINE_BLOCK: rb_builtin_attr = 4; pub const BUILTIN_ATTR_C_TRACE: rb_builtin_attr = 8; +pub const BUILTIN_ATTR_WITHOUT_INTERRUPTS: rb_builtin_attr = 16; pub type rb_builtin_attr = u32; pub type rb_jit_func_t = ::std::option::Option< unsafe extern "C" fn( @@ -1564,181 +1565,189 @@ pub const YARVINSN_jump: ruby_vminsn_type = 72; pub const YARVINSN_branchif: ruby_vminsn_type = 73; pub const YARVINSN_branchunless: ruby_vminsn_type = 74; pub const YARVINSN_branchnil: ruby_vminsn_type = 75; -pub const YARVINSN_once: ruby_vminsn_type = 76; -pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 77; -pub const YARVINSN_opt_plus: ruby_vminsn_type = 78; -pub const YARVINSN_opt_minus: ruby_vminsn_type = 79; -pub const YARVINSN_opt_mult: ruby_vminsn_type = 80; -pub const YARVINSN_opt_div: ruby_vminsn_type = 81; -pub const YARVINSN_opt_mod: ruby_vminsn_type = 82; -pub const YARVINSN_opt_eq: ruby_vminsn_type = 83; -pub const YARVINSN_opt_neq: ruby_vminsn_type = 84; -pub const YARVINSN_opt_lt: ruby_vminsn_type = 85; -pub const YARVINSN_opt_le: ruby_vminsn_type = 86; -pub const YARVINSN_opt_gt: ruby_vminsn_type = 87; -pub const YARVINSN_opt_ge: ruby_vminsn_type = 88; -pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 89; -pub const YARVINSN_opt_and: ruby_vminsn_type = 90; -pub const YARVINSN_opt_or: ruby_vminsn_type = 91; -pub const YARVINSN_opt_aref: ruby_vminsn_type = 92; -pub const YARVINSN_opt_aset: ruby_vminsn_type = 93; -pub const YARVINSN_opt_length: ruby_vminsn_type = 94; -pub const YARVINSN_opt_size: ruby_vminsn_type = 95; -pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 96; -pub const YARVINSN_opt_succ: ruby_vminsn_type = 97; -pub const YARVINSN_opt_not: ruby_vminsn_type = 98; -pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 99; -pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 100; -pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 101; -pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 102; -pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 103; -pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 104; -pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 105; -pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 106; -pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 107; -pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 108; -pub const YARVINSN_trace_nop: ruby_vminsn_type = 109; -pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 110; -pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 111; -pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 112; -pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 113; -pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 114; -pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 115; -pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 116; -pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 117; -pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 118; -pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 119; -pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 120; -pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 121; -pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 122; -pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 123; -pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 124; -pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 125; -pub const YARVINSN_trace_putnil: ruby_vminsn_type = 126; -pub const YARVINSN_trace_putself: ruby_vminsn_type = 127; -pub const YARVINSN_trace_putobject: ruby_vminsn_type = 128; -pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 129; -pub const YARVINSN_trace_putstring: ruby_vminsn_type = 130; -pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 131; -pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 132; -pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 133; -pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 134; -pub const YARVINSN_trace_intern: ruby_vminsn_type = 135; -pub const YARVINSN_trace_newarray: ruby_vminsn_type = 136; -pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 137; -pub const YARVINSN_trace_duparray: ruby_vminsn_type = 138; -pub const YARVINSN_trace_duphash: ruby_vminsn_type = 139; -pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 140; -pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 141; -pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 142; -pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 143; -pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 144; -pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 145; -pub const YARVINSN_trace_newhash: ruby_vminsn_type = 146; -pub const YARVINSN_trace_newrange: ruby_vminsn_type = 147; -pub const YARVINSN_trace_pop: ruby_vminsn_type = 148; -pub const YARVINSN_trace_dup: ruby_vminsn_type = 149; -pub const YARVINSN_trace_dupn: ruby_vminsn_type = 150; -pub const YARVINSN_trace_swap: ruby_vminsn_type = 151; -pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 152; -pub const YARVINSN_trace_topn: ruby_vminsn_type = 153; -pub const YARVINSN_trace_setn: ruby_vminsn_type = 154; -pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 155; -pub const YARVINSN_trace_defined: ruby_vminsn_type = 156; -pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 157; -pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 158; -pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 159; -pub const YARVINSN_trace_checktype: ruby_vminsn_type = 160; -pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 161; -pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 162; -pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 163; -pub const YARVINSN_trace_send: ruby_vminsn_type = 164; -pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 165; -pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 166; -pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 167; -pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 168; -pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 169; -pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 170; -pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 171; -pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 172; -pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 173; -pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 174; -pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 175; -pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 176; -pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 177; -pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 178; -pub const YARVINSN_trace_leave: ruby_vminsn_type = 179; -pub const YARVINSN_trace_throw: ruby_vminsn_type = 180; -pub const YARVINSN_trace_jump: ruby_vminsn_type = 181; -pub const YARVINSN_trace_branchif: ruby_vminsn_type = 182; -pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 183; -pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 184; -pub const YARVINSN_trace_once: ruby_vminsn_type = 185; -pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 186; -pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 187; -pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 188; -pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 189; -pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 190; -pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 191; -pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 192; -pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 193; -pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 194; -pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 195; -pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 196; -pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 197; -pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 198; -pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 199; -pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 200; -pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 201; -pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 202; -pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 203; -pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 204; -pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 205; -pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 206; -pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 207; -pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 208; -pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 209; -pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 210; -pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 211; -pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 212; -pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 213; -pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 214; -pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 215; -pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 216; -pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 217; -pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 218; -pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 219; -pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 220; -pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 221; -pub const YARVINSN_zjit_send: ruby_vminsn_type = 222; -pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 223; -pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 224; -pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 225; -pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 226; -pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 227; -pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 228; -pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 229; -pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 230; -pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 231; -pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 232; -pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 233; -pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 234; -pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 235; -pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 236; -pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 237; -pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 238; -pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 239; -pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 240; -pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 241; -pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 242; -pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 243; -pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 244; -pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 245; -pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 246; -pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 247; -pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 248; -pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 249; -pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 250; +pub const YARVINSN_jump_without_ints: ruby_vminsn_type = 76; +pub const YARVINSN_branchif_without_ints: ruby_vminsn_type = 77; +pub const YARVINSN_branchunless_without_ints: ruby_vminsn_type = 78; +pub const YARVINSN_branchnil_without_ints: ruby_vminsn_type = 79; +pub const YARVINSN_once: ruby_vminsn_type = 80; +pub const YARVINSN_opt_case_dispatch: ruby_vminsn_type = 81; +pub const YARVINSN_opt_plus: ruby_vminsn_type = 82; +pub const YARVINSN_opt_minus: ruby_vminsn_type = 83; +pub const YARVINSN_opt_mult: ruby_vminsn_type = 84; +pub const YARVINSN_opt_div: ruby_vminsn_type = 85; +pub const YARVINSN_opt_mod: ruby_vminsn_type = 86; +pub const YARVINSN_opt_eq: ruby_vminsn_type = 87; +pub const YARVINSN_opt_neq: ruby_vminsn_type = 88; +pub const YARVINSN_opt_lt: ruby_vminsn_type = 89; +pub const YARVINSN_opt_le: ruby_vminsn_type = 90; +pub const YARVINSN_opt_gt: ruby_vminsn_type = 91; +pub const YARVINSN_opt_ge: ruby_vminsn_type = 92; +pub const YARVINSN_opt_ltlt: ruby_vminsn_type = 93; +pub const YARVINSN_opt_and: ruby_vminsn_type = 94; +pub const YARVINSN_opt_or: ruby_vminsn_type = 95; +pub const YARVINSN_opt_aref: ruby_vminsn_type = 96; +pub const YARVINSN_opt_aset: ruby_vminsn_type = 97; +pub const YARVINSN_opt_length: ruby_vminsn_type = 98; +pub const YARVINSN_opt_size: ruby_vminsn_type = 99; +pub const YARVINSN_opt_empty_p: ruby_vminsn_type = 100; +pub const YARVINSN_opt_succ: ruby_vminsn_type = 101; +pub const YARVINSN_opt_not: ruby_vminsn_type = 102; +pub const YARVINSN_opt_regexpmatch2: ruby_vminsn_type = 103; +pub const YARVINSN_invokebuiltin: ruby_vminsn_type = 104; +pub const YARVINSN_opt_invokebuiltin_delegate: ruby_vminsn_type = 105; +pub const YARVINSN_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 106; +pub const YARVINSN_getlocal_WC_0: ruby_vminsn_type = 107; +pub const YARVINSN_getlocal_WC_1: ruby_vminsn_type = 108; +pub const YARVINSN_setlocal_WC_0: ruby_vminsn_type = 109; +pub const YARVINSN_setlocal_WC_1: ruby_vminsn_type = 110; +pub const YARVINSN_putobject_INT2FIX_0_: ruby_vminsn_type = 111; +pub const YARVINSN_putobject_INT2FIX_1_: ruby_vminsn_type = 112; +pub const YARVINSN_trace_nop: ruby_vminsn_type = 113; +pub const YARVINSN_trace_getlocal: ruby_vminsn_type = 114; +pub const YARVINSN_trace_setlocal: ruby_vminsn_type = 115; +pub const YARVINSN_trace_getblockparam: ruby_vminsn_type = 116; +pub const YARVINSN_trace_setblockparam: ruby_vminsn_type = 117; +pub const YARVINSN_trace_getblockparamproxy: ruby_vminsn_type = 118; +pub const YARVINSN_trace_getspecial: ruby_vminsn_type = 119; +pub const YARVINSN_trace_setspecial: ruby_vminsn_type = 120; +pub const YARVINSN_trace_getinstancevariable: ruby_vminsn_type = 121; +pub const YARVINSN_trace_setinstancevariable: ruby_vminsn_type = 122; +pub const YARVINSN_trace_getclassvariable: ruby_vminsn_type = 123; +pub const YARVINSN_trace_setclassvariable: ruby_vminsn_type = 124; +pub const YARVINSN_trace_opt_getconstant_path: ruby_vminsn_type = 125; +pub const YARVINSN_trace_getconstant: ruby_vminsn_type = 126; +pub const YARVINSN_trace_setconstant: ruby_vminsn_type = 127; +pub const YARVINSN_trace_getglobal: ruby_vminsn_type = 128; +pub const YARVINSN_trace_setglobal: ruby_vminsn_type = 129; +pub const YARVINSN_trace_putnil: ruby_vminsn_type = 130; +pub const YARVINSN_trace_putself: ruby_vminsn_type = 131; +pub const YARVINSN_trace_putobject: ruby_vminsn_type = 132; +pub const YARVINSN_trace_putspecialobject: ruby_vminsn_type = 133; +pub const YARVINSN_trace_putstring: ruby_vminsn_type = 134; +pub const YARVINSN_trace_putchilledstring: ruby_vminsn_type = 135; +pub const YARVINSN_trace_concatstrings: ruby_vminsn_type = 136; +pub const YARVINSN_trace_anytostring: ruby_vminsn_type = 137; +pub const YARVINSN_trace_toregexp: ruby_vminsn_type = 138; +pub const YARVINSN_trace_intern: ruby_vminsn_type = 139; +pub const YARVINSN_trace_newarray: ruby_vminsn_type = 140; +pub const YARVINSN_trace_pushtoarraykwsplat: ruby_vminsn_type = 141; +pub const YARVINSN_trace_duparray: ruby_vminsn_type = 142; +pub const YARVINSN_trace_duphash: ruby_vminsn_type = 143; +pub const YARVINSN_trace_expandarray: ruby_vminsn_type = 144; +pub const YARVINSN_trace_concatarray: ruby_vminsn_type = 145; +pub const YARVINSN_trace_concattoarray: ruby_vminsn_type = 146; +pub const YARVINSN_trace_pushtoarray: ruby_vminsn_type = 147; +pub const YARVINSN_trace_splatarray: ruby_vminsn_type = 148; +pub const YARVINSN_trace_splatkw: ruby_vminsn_type = 149; +pub const YARVINSN_trace_newhash: ruby_vminsn_type = 150; +pub const YARVINSN_trace_newrange: ruby_vminsn_type = 151; +pub const YARVINSN_trace_pop: ruby_vminsn_type = 152; +pub const YARVINSN_trace_dup: ruby_vminsn_type = 153; +pub const YARVINSN_trace_dupn: ruby_vminsn_type = 154; +pub const YARVINSN_trace_swap: ruby_vminsn_type = 155; +pub const YARVINSN_trace_opt_reverse: ruby_vminsn_type = 156; +pub const YARVINSN_trace_topn: ruby_vminsn_type = 157; +pub const YARVINSN_trace_setn: ruby_vminsn_type = 158; +pub const YARVINSN_trace_adjuststack: ruby_vminsn_type = 159; +pub const YARVINSN_trace_defined: ruby_vminsn_type = 160; +pub const YARVINSN_trace_definedivar: ruby_vminsn_type = 161; +pub const YARVINSN_trace_checkmatch: ruby_vminsn_type = 162; +pub const YARVINSN_trace_checkkeyword: ruby_vminsn_type = 163; +pub const YARVINSN_trace_checktype: ruby_vminsn_type = 164; +pub const YARVINSN_trace_defineclass: ruby_vminsn_type = 165; +pub const YARVINSN_trace_definemethod: ruby_vminsn_type = 166; +pub const YARVINSN_trace_definesmethod: ruby_vminsn_type = 167; +pub const YARVINSN_trace_send: ruby_vminsn_type = 168; +pub const YARVINSN_trace_sendforward: ruby_vminsn_type = 169; +pub const YARVINSN_trace_opt_send_without_block: ruby_vminsn_type = 170; +pub const YARVINSN_trace_opt_new: ruby_vminsn_type = 171; +pub const YARVINSN_trace_objtostring: ruby_vminsn_type = 172; +pub const YARVINSN_trace_opt_ary_freeze: ruby_vminsn_type = 173; +pub const YARVINSN_trace_opt_hash_freeze: ruby_vminsn_type = 174; +pub const YARVINSN_trace_opt_str_freeze: ruby_vminsn_type = 175; +pub const YARVINSN_trace_opt_nil_p: ruby_vminsn_type = 176; +pub const YARVINSN_trace_opt_str_uminus: ruby_vminsn_type = 177; +pub const YARVINSN_trace_opt_duparray_send: ruby_vminsn_type = 178; +pub const YARVINSN_trace_opt_newarray_send: ruby_vminsn_type = 179; +pub const YARVINSN_trace_invokesuper: ruby_vminsn_type = 180; +pub const YARVINSN_trace_invokesuperforward: ruby_vminsn_type = 181; +pub const YARVINSN_trace_invokeblock: ruby_vminsn_type = 182; +pub const YARVINSN_trace_leave: ruby_vminsn_type = 183; +pub const YARVINSN_trace_throw: ruby_vminsn_type = 184; +pub const YARVINSN_trace_jump: ruby_vminsn_type = 185; +pub const YARVINSN_trace_branchif: ruby_vminsn_type = 186; +pub const YARVINSN_trace_branchunless: ruby_vminsn_type = 187; +pub const YARVINSN_trace_branchnil: ruby_vminsn_type = 188; +pub const YARVINSN_trace_jump_without_ints: ruby_vminsn_type = 189; +pub const YARVINSN_trace_branchif_without_ints: ruby_vminsn_type = 190; +pub const YARVINSN_trace_branchunless_without_ints: ruby_vminsn_type = 191; +pub const YARVINSN_trace_branchnil_without_ints: ruby_vminsn_type = 192; +pub const YARVINSN_trace_once: ruby_vminsn_type = 193; +pub const YARVINSN_trace_opt_case_dispatch: ruby_vminsn_type = 194; +pub const YARVINSN_trace_opt_plus: ruby_vminsn_type = 195; +pub const YARVINSN_trace_opt_minus: ruby_vminsn_type = 196; +pub const YARVINSN_trace_opt_mult: ruby_vminsn_type = 197; +pub const YARVINSN_trace_opt_div: ruby_vminsn_type = 198; +pub const YARVINSN_trace_opt_mod: ruby_vminsn_type = 199; +pub const YARVINSN_trace_opt_eq: ruby_vminsn_type = 200; +pub const YARVINSN_trace_opt_neq: ruby_vminsn_type = 201; +pub const YARVINSN_trace_opt_lt: ruby_vminsn_type = 202; +pub const YARVINSN_trace_opt_le: ruby_vminsn_type = 203; +pub const YARVINSN_trace_opt_gt: ruby_vminsn_type = 204; +pub const YARVINSN_trace_opt_ge: ruby_vminsn_type = 205; +pub const YARVINSN_trace_opt_ltlt: ruby_vminsn_type = 206; +pub const YARVINSN_trace_opt_and: ruby_vminsn_type = 207; +pub const YARVINSN_trace_opt_or: ruby_vminsn_type = 208; +pub const YARVINSN_trace_opt_aref: ruby_vminsn_type = 209; +pub const YARVINSN_trace_opt_aset: ruby_vminsn_type = 210; +pub const YARVINSN_trace_opt_length: ruby_vminsn_type = 211; +pub const YARVINSN_trace_opt_size: ruby_vminsn_type = 212; +pub const YARVINSN_trace_opt_empty_p: ruby_vminsn_type = 213; +pub const YARVINSN_trace_opt_succ: ruby_vminsn_type = 214; +pub const YARVINSN_trace_opt_not: ruby_vminsn_type = 215; +pub const YARVINSN_trace_opt_regexpmatch2: ruby_vminsn_type = 216; +pub const YARVINSN_trace_invokebuiltin: ruby_vminsn_type = 217; +pub const YARVINSN_trace_opt_invokebuiltin_delegate: ruby_vminsn_type = 218; +pub const YARVINSN_trace_opt_invokebuiltin_delegate_leave: ruby_vminsn_type = 219; +pub const YARVINSN_trace_getlocal_WC_0: ruby_vminsn_type = 220; +pub const YARVINSN_trace_getlocal_WC_1: ruby_vminsn_type = 221; +pub const YARVINSN_trace_setlocal_WC_0: ruby_vminsn_type = 222; +pub const YARVINSN_trace_setlocal_WC_1: ruby_vminsn_type = 223; +pub const YARVINSN_trace_putobject_INT2FIX_0_: ruby_vminsn_type = 224; +pub const YARVINSN_trace_putobject_INT2FIX_1_: ruby_vminsn_type = 225; +pub const YARVINSN_zjit_getblockparamproxy: ruby_vminsn_type = 226; +pub const YARVINSN_zjit_getinstancevariable: ruby_vminsn_type = 227; +pub const YARVINSN_zjit_setinstancevariable: ruby_vminsn_type = 228; +pub const YARVINSN_zjit_definedivar: ruby_vminsn_type = 229; +pub const YARVINSN_zjit_send: ruby_vminsn_type = 230; +pub const YARVINSN_zjit_opt_send_without_block: ruby_vminsn_type = 231; +pub const YARVINSN_zjit_objtostring: ruby_vminsn_type = 232; +pub const YARVINSN_zjit_opt_nil_p: ruby_vminsn_type = 233; +pub const YARVINSN_zjit_invokesuper: ruby_vminsn_type = 234; +pub const YARVINSN_zjit_invokeblock: ruby_vminsn_type = 235; +pub const YARVINSN_zjit_opt_plus: ruby_vminsn_type = 236; +pub const YARVINSN_zjit_opt_minus: ruby_vminsn_type = 237; +pub const YARVINSN_zjit_opt_mult: ruby_vminsn_type = 238; +pub const YARVINSN_zjit_opt_div: ruby_vminsn_type = 239; +pub const YARVINSN_zjit_opt_mod: ruby_vminsn_type = 240; +pub const YARVINSN_zjit_opt_eq: ruby_vminsn_type = 241; +pub const YARVINSN_zjit_opt_neq: ruby_vminsn_type = 242; +pub const YARVINSN_zjit_opt_lt: ruby_vminsn_type = 243; +pub const YARVINSN_zjit_opt_le: ruby_vminsn_type = 244; +pub const YARVINSN_zjit_opt_gt: ruby_vminsn_type = 245; +pub const YARVINSN_zjit_opt_ge: ruby_vminsn_type = 246; +pub const YARVINSN_zjit_opt_ltlt: ruby_vminsn_type = 247; +pub const YARVINSN_zjit_opt_and: ruby_vminsn_type = 248; +pub const YARVINSN_zjit_opt_or: ruby_vminsn_type = 249; +pub const YARVINSN_zjit_opt_aref: ruby_vminsn_type = 250; +pub const YARVINSN_zjit_opt_aset: ruby_vminsn_type = 251; +pub const YARVINSN_zjit_opt_length: ruby_vminsn_type = 252; +pub const YARVINSN_zjit_opt_size: ruby_vminsn_type = 253; +pub const YARVINSN_zjit_opt_empty_p: ruby_vminsn_type = 254; +pub const YARVINSN_zjit_opt_succ: ruby_vminsn_type = 255; +pub const YARVINSN_zjit_opt_not: ruby_vminsn_type = 256; +pub const YARVINSN_zjit_opt_regexpmatch2: ruby_vminsn_type = 257; +pub const VM_INSTRUCTION_SIZE: ruby_vminsn_type = 258; pub type ruby_vminsn_type = u32; pub type rb_iseq_callback = ::std::option::Option< unsafe extern "C" fn(arg1: *const rb_iseq_t, arg2: *mut ::std::os::raw::c_void), @@ -1951,6 +1960,7 @@ unsafe extern "C" { pub fn rb_ivar_set(obj: VALUE, name: ID, val: VALUE) -> VALUE; pub fn rb_ivar_defined(obj: VALUE, name: ID) -> VALUE; pub fn rb_attr_get(obj: VALUE, name: ID) -> VALUE; + pub fn rb_const_get(space: VALUE, name: ID) -> VALUE; pub fn rb_class_allocate_instance(klass: VALUE) -> VALUE; pub fn rb_obj_equal(obj1: VALUE, obj2: VALUE) -> VALUE; pub fn rb_reg_new_ary(ary: VALUE, options: ::std::os::raw::c_int) -> VALUE; @@ -1973,6 +1983,7 @@ unsafe extern "C" { id: ID, ) -> *const rb_callable_method_entry_t; pub static mut rb_cISeq: VALUE; + pub static mut rb_cRubyVM: VALUE; pub static mut rb_mRubyVMFrozenCore: VALUE; pub static mut rb_block_param_proxy: VALUE; pub fn rb_vm_ep_local_ep(ep: *const VALUE) -> *const VALUE; diff --git a/zjit/src/cruby_methods.rs b/zjit/src/cruby_methods.rs index aeca0e36bd7783..56ff3ca1aa377a 100644 --- a/zjit/src/cruby_methods.rs +++ b/zjit/src/cruby_methods.rs @@ -14,6 +14,13 @@ use std::ffi::c_void; use crate::hir_type::{types, Type}; use crate::hir; +// Array iteration builtin functions (defined in array.c) +unsafe extern "C" { + fn rb_jit_ary_at_end(ec: EcPtr, self_: VALUE, index: VALUE) -> VALUE; + fn rb_jit_ary_at(ec: EcPtr, self_: VALUE, index: VALUE) -> VALUE; + fn rb_jit_fixnum_inc(ec: EcPtr, self_: VALUE, num: VALUE) -> VALUE; +} + pub struct Annotations { cfuncs: HashMap<*mut c_void, FnProperties>, builtin_funcs: HashMap<*mut c_void, FnProperties>, @@ -270,6 +277,11 @@ pub fn init() -> Annotations { annotate_builtin!(rb_cSymbol, "name", types::StringExact); annotate_builtin!(rb_cSymbol, "to_s", types::StringExact); + // Array iteration builtins (used in with_jit Array#each, map, select, find) + builtin_funcs.insert(rb_jit_fixnum_inc as *mut c_void, FnProperties { inline: inline_fixnum_inc, return_type: types::Fixnum, ..Default::default() }); + builtin_funcs.insert(rb_jit_ary_at as *mut c_void, FnProperties { inline: inline_ary_at, ..Default::default() }); + builtin_funcs.insert(rb_jit_ary_at_end as *mut c_void, FnProperties { inline: inline_ary_at_end, return_type: types::BoolExact, ..Default::default() }); + Annotations { cfuncs: std::mem::take(cfuncs), builtin_funcs: std::mem::take(builtin_funcs), @@ -892,3 +904,33 @@ fn inline_kernel_class(fun: &mut hir::Function, block: hir::BlockId, _recv: hir: let real_class = unsafe { rb_class_real(recv_class) }; Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(real_class) })) } + +/// Inline `fixnum_inc(ec, self, num)` implies FixnumAdd(num, 1). +/// num is always a Fixnum (starts at 0 and is incremented by fixnum_inc). +fn inline_fixnum_inc(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[_self, num] = args else { return None; }; + let one = fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(VALUE::fixnum_from_usize(1)) }); + let result = fun.push_insn(block, hir::Insn::FixnumAdd { left: num, right: one, state }); + Some(result) +} + +/// Inline `ary_at(ec, self, index)` implies ArrayAref. +/// Called from Array#each etc. where self is Array and index is in bounds. +fn inline_ary_at(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option { + let &[recv, index] = args else { return None; }; + let recv = fun.push_insn(block, hir::Insn::RefineType { val: recv, new_type: types::Array }); + let index = fun.push_insn(block, hir::Insn::UnboxFixnum { val: index }); + let result = fun.push_insn(block, hir::Insn::ArrayAref { array: recv, index }); + Some(result) +} + +/// Inline `ary_at_end(ec, self, index)` implies index >= ArrayLength(self). +/// Called from Array#each etc. where self is Array and index is Fixnum. +fn inline_ary_at_end(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option { + let &[recv, index] = args else { return None; }; + let recv = fun.push_insn(block, hir::Insn::RefineType { val: recv, new_type: types::Array }); + let length_cint = fun.push_insn(block, hir::Insn::ArrayLength { array: recv }); + let length = fun.push_insn(block, hir::Insn::BoxFixnum { val: length_cint, state }); + let result = fun.push_insn(block, hir::Insn::FixnumGe { left: index, right: length }); + Some(result) +} diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 7812c6058e3cb2..cf2371ef5a416b 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -803,6 +803,7 @@ pub enum Insn { FixnumAref { recv: InsnId, index: InsnId }, // TODO(max): In iseq body types that are not ISEQ_TYPE_METHOD, rewrite to Constant false. Defined { op_type: usize, obj: VALUE, pushval: VALUE, v: InsnId, state: InsnId }, + GetConstant { klass: InsnId, id: ID, allow_nil: InsnId, state: InsnId }, GetConstantPath { ic: *const iseq_inline_constant_cache, state: InsnId }, /// Kernel#block_given? but without pushing a frame. Similar to [`Insn::Defined`] with /// `DEFINED_YIELD` @@ -1139,6 +1140,7 @@ impl Insn { Insn::UnboxFixnum { .. } => effects::Any, Insn::FixnumAref { .. } => effects::Empty, Insn::Defined { .. } => effects::Any, + Insn::GetConstant { .. } => effects::Any, Insn::GetConstantPath { .. } => effects::Any, Insn::IsBlockGiven { .. } => Effect::read_write(abstract_heaps::Other, abstract_heaps::Empty), Insn::FixnumBitCheck { .. } => effects::Any, @@ -1562,6 +1564,9 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { write!(f, "GetBlockParam {name}l{level}, EP@{ep_offset}") }, Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) }, + Insn::GetConstant { klass, id, allow_nil, .. } => { + write!(f, "GetConstant {klass}, :{}, {allow_nil}", id.contents_lossy()) + } Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) }, Insn::IsBlockGiven { lep } => { write!(f, "IsBlockGiven {lep}") }, Insn::FixnumBitCheck {val, index} => { write!(f, "FixnumBitCheck {val}, {index}") }, @@ -2356,6 +2361,7 @@ impl Function { }, &Defined { op_type, obj, pushval, v, state } => Defined { op_type, obj, pushval, v: find!(v), state: find!(state) }, &DefinedIvar { self_val, pushval, id, state } => DefinedIvar { self_val: find!(self_val), pushval, id, state }, + &GetConstant { klass, id, allow_nil, state } => GetConstant { klass: find!(klass), id, allow_nil: find!(allow_nil), state }, &NewArray { ref elements, state } => NewArray { elements: find_vec!(elements), state: find!(state) }, &NewHash { ref elements, state } => NewHash { elements: find_vec!(elements), state: find!(state) }, &NewRange { low, high, flag, state } => NewRange { low: find!(low), high: find!(high), flag, state: find!(state) }, @@ -2527,6 +2533,7 @@ impl Function { Insn::InvokeBuiltin { return_type, .. } => return_type.unwrap_or(types::BasicObject), Insn::Defined { pushval, .. } => Type::from_value(*pushval).union(types::NilClass), Insn::DefinedIvar { pushval, .. } => Type::from_value(*pushval).union(types::NilClass), + Insn::GetConstant { .. } => types::BasicObject, Insn::GetConstantPath { .. } => types::BasicObject, Insn::IsBlockGiven { .. } => types::BoolExact, Insn::FixnumBitCheck { .. } => types::BoolExact, @@ -4881,6 +4888,11 @@ impl Function { worklist.push_back(self_val); worklist.push_back(state); } + &Insn::GetConstant { klass, allow_nil, state, .. } => { + worklist.push_back(klass); + worklist.push_back(allow_nil); + worklist.push_back(state); + } &Insn::SetIvar { self_val, val, state, .. } => { worklist.push_back(self_val); worklist.push_back(val); @@ -5507,6 +5519,10 @@ impl Function { self.assert_subtype(insn_id, left, types::BasicObject)?; self.assert_subtype(insn_id, right, types::BasicObject) } + Insn::GetConstant { klass, allow_nil, .. } => { + self.assert_subtype(insn_id, klass, types::BasicObject)?; + self.assert_subtype(insn_id, allow_nil, types::BoolExact) + } // Instructions with recv and a Vec of Ruby objects Insn::SendWithoutBlock { recv, ref args, .. } | Insn::SendDirect { recv, ref args, .. } @@ -6087,14 +6103,12 @@ pub fn jit_entry_insns(iseq: IseqPtr) -> Vec { struct BytecodeInfo { jump_targets: Vec, - has_blockiseq: bool, } fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeInfo { let iseq_size = unsafe { get_iseq_encoded_size(iseq) }; let mut insn_idx = 0; 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 let pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx) }; @@ -6105,7 +6119,8 @@ fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeI .unwrap(); insn_idx += insn_len(opcode as usize); match opcode { - YARVINSN_branchunless | YARVINSN_jump | YARVINSN_branchif | YARVINSN_branchnil => { + YARVINSN_branchunless | YARVINSN_jump | YARVINSN_branchif | YARVINSN_branchnil + | YARVINSN_branchunless_without_ints | YARVINSN_jump_without_ints | YARVINSN_branchif_without_ints | YARVINSN_branchnil_without_ints => { let offset = get_arg(pc, 0).as_i64(); jump_targets.insert(insn_idx_at_offset(insn_idx, offset)); } @@ -6118,18 +6133,12 @@ fn compute_bytecode_info(iseq: *const rb_iseq_t, opt_table: &[u32]) -> BytecodeI jump_targets.insert(insn_idx); } } - YARVINSN_send | YARVINSN_invokesuper => { - let blockiseq: IseqPtr = get_arg(pc, 1).as_iseq(); - if !blockiseq.is_null() { - has_blockiseq = true; - } - } _ => {} } } let mut result = jump_targets.into_iter().collect::>(); result.sort(); - BytecodeInfo { jump_targets: result, has_blockiseq } + BytecodeInfo { jump_targets: result } } #[derive(Debug, PartialEq, Clone, Copy)] @@ -6224,6 +6233,10 @@ fn invalidates_locals(opcode: u32, operands: *const VALUE) -> bool { | YARVINSN_branchunless | YARVINSN_branchif | YARVINSN_branchnil + | YARVINSN_jump_without_ints + | YARVINSN_branchunless_without_ints + | YARVINSN_branchif_without_ints + | YARVINSN_branchnil_without_ints | YARVINSN_leave => false, // TODO(max): Read the invokebuiltin target from operands and determine if it's leaf _ => unsafe { !rb_zjit_insn_leaf(opcode as i32, operands) } @@ -6244,7 +6257,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Compute a map of PC->Block by finding jump targets let jit_entry_insns = jit_entry_insns(iseq); - let BytecodeInfo { jump_targets, has_blockiseq } = compute_bytecode_info(iseq, &jit_entry_insns); + let BytecodeInfo { jump_targets } = compute_bytecode_info(iseq, &jit_entry_insns); // Make all empty basic blocks. The ordering of the BBs matters for getting fallthrough jumps // in good places, but it's not necessary for correctness. TODO: Higher quality scheduling during lowering. @@ -6276,7 +6289,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Check if the EP is escaped for the ISEQ from the beginning. We give up // optimizing locals in that case because they're shared with other frames. - let ep_escaped = iseq_escapes_ep(iseq); + let ep_starts_escaped = iseq_escapes_ep(iseq); + // Check if the EP has been escaped at some point in the ISEQ. If it has, then we assume that + // its EP is shared with other frames. + let ep_has_been_escaped = crate::invariants::iseq_escapes_ep(iseq); + let ep_escaped = ep_starts_escaped || ep_has_been_escaped; // Iteratively fill out basic blocks using a queue. // TODO(max): Basic block arguments at edges @@ -6620,7 +6637,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // Use FrameState to get kw_bits when possible, just like getlocal_WC_0. let val = if !local_inval { state.getlocal(ep_offset) - } else if ep_escaped || has_blockiseq { + } else if ep_escaped { fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false }) } else { let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.without_locals() }); @@ -6630,13 +6647,23 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { }; state.stack_push(fun.push_insn(block, Insn::FixnumBitCheck { val, index })); } + YARVINSN_getconstant => { + let id = ID(get_arg(pc, 0).as_u64()); + let allow_nil = state.stack_pop()?; + let klass = state.stack_pop()?; + let result = fun.push_insn(block, Insn::GetConstant { klass, id, allow_nil, state: exit_id }); + state.stack_push(result); + } YARVINSN_opt_getconstant_path => { let ic = get_arg(pc, 0).as_ptr(); + // TODO: Remove this extra Snapshot and pass `exit_id` to `GetConstantPath` instead. let snapshot = fun.push_insn(block, Insn::Snapshot { state: exit_state }); state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: snapshot })); } - YARVINSN_branchunless => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchunless | YARVINSN_branchunless_without_ints => { + if opcode == YARVINSN_branchunless { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::Test { val }); @@ -6655,8 +6682,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.replace(val, not_nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); } - YARVINSN_branchif => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchif | YARVINSN_branchif_without_ints => { + if opcode == YARVINSN_branchif { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::Test { val }); @@ -6675,8 +6704,10 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.replace(val, nil_false); queue.push_back((state.clone(), target, target_idx, local_inval)); } - YARVINSN_branchnil => { - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + YARVINSN_branchnil | YARVINSN_branchnil_without_ints => { + if opcode == YARVINSN_branchnil { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let offset = get_arg(pc, 0).as_i64(); let val = state.stack_pop()?; let test_id = fun.push_insn(block, Insn::IsNil { val }); @@ -6725,9 +6756,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { state.stack_setn(argc, insn_id); state.stack_setn(argc + 1, insn_id); } - YARVINSN_jump => { + YARVINSN_jump | YARVINSN_jump_without_ints => { let offset = get_arg(pc, 0).as_i64(); - fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + if opcode == YARVINSN_jump { + fun.push_insn(block, Insn::CheckInterrupts { state: exit_id }); + } let target_idx = insn_idx_at_offset(insn_idx, offset); let target = insn_idx_to_block[&target_idx]; let _branch_id = fun.push_insn(block, Insn::Jump( @@ -6743,7 +6776,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // In case of JIT-to-JIT send locals might never end up in EP memory. let val = state.getlocal(ep_offset); state.stack_push(val); - } else if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + } else if ep_escaped { // Read the local using EP let val = fun.push_insn(block, Insn::GetLocal { ep_offset, level: 0, use_sp: false, rest_param: false }); state.setlocal(ep_offset, val); // remember the result to spill on side-exits @@ -6764,7 +6797,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { YARVINSN_setlocal_WC_0 => { let ep_offset = get_arg(pc, 0).as_u32(); let val = state.stack_pop()?; - if ep_escaped || has_blockiseq { // TODO: figure out how to drop has_blockiseq here + if ep_escaped { // Write the local using EP fun.push_insn(block, Insn::SetLocal { val, ep_offset, level: 0 }); } else if local_inval { diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index d0d2e190784d4b..d32de872d52563 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -823,11 +823,11 @@ mod hir_opt_tests { bb2(v8:BasicObject, v9:BasicObject): PatchPoint NoSingletonClass(C@0x1000) PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) - v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] - v24:BasicObject = CCallWithFrame v23, :C#fun_new_map@0x1038, block=0x1040 + v22:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] + v23:BasicObject = SendDirect v22, 0x1038, :fun_new_map (0x1048) v15:BasicObject = GetLocal :o, l0, EP@3 CheckInterrupts - Return v24 + Return v23 "); } @@ -2356,6 +2356,33 @@ mod hir_opt_tests { "); } + #[test] + fn test_do_not_eliminate_getconstant() { + eval(" + def test(klass) + klass::ARGV + 5 + end + "); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :klass, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:FalseClass = Const Value(false) + v16:BasicObject = GetConstant v9, :ARGV, v14 + v20:Fixnum[5] = Const Value(5) + CheckInterrupts + Return v20 + "); + } + #[test] fn kernel_itself_const() { eval(" @@ -2926,15 +2953,14 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) - SetLocal :a, l0, EP@3, v13 PatchPoint NoSingletonClass(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v31:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v8, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count - v20:BasicObject = GetLocal :a, l0, EP@3 - v24:BasicObject = GetLocal :a, l0, EP@3 + v19:BasicObject = GetLocal :a, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v24 + Return v19 "); } @@ -3415,15 +3441,14 @@ mod hir_opt_tests { Jump bb2(v6, v7, v8) bb2(v10:BasicObject, v11:BasicObject, v12:NilClass): v16:ArrayExact = NewArray - SetLocal :a, l0, EP@3, v16 - v22:TrueClass = Const Value(true) + v21:TrueClass = Const Value(true) IncrCounter complex_arg_pass_caller_kwarg - v24:BasicObject = Send v11, 0x1000, :each_line, v22 # SendFallbackReason: Complex argument passing - v25:BasicObject = GetLocal :s, l0, EP@4 - v26:BasicObject = GetLocal :a, l0, EP@3 - v30:BasicObject = GetLocal :a, l0, EP@3 + v23:BasicObject = Send v11, 0x1000, :each_line, v21 # SendFallbackReason: Complex argument passing + v24:BasicObject = GetLocal :s, l0, EP@4 + v25:BasicObject = GetLocal :a, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v30 + Return v25 "); } @@ -6490,9 +6515,9 @@ mod hir_opt_tests { v11:ArrayExact = ArrayDup v10 PatchPoint NoSingletonClass(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) - v21:BasicObject = CCallWithFrame v11, :Array#map@0x1040, block=0x1048 + v20:BasicObject = SendDirect v11, 0x1040, :map (0x1050) CheckInterrupts - Return v21 + Return v20 "); } @@ -6523,7 +6548,6 @@ mod hir_opt_tests { Jump bb2(v5, v6) bb2(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - SetLocal :result, l0, EP@3, v13 PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) v36:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) @@ -6533,10 +6557,10 @@ mod hir_opt_tests { PatchPoint NoSingletonClass(Array@0x1020) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) v43:BasicObject = CCallVariadic v36, :zip@0x1058, v39 - v25:BasicObject = GetLocal :result, l0, EP@3 - v29:BasicObject = GetLocal :result, l0, EP@3 + v24:BasicObject = GetLocal :result, l0, EP@3 + PatchPoint NoEPEscape(test) CheckInterrupts - Return v29 + Return v24 "); } @@ -11887,4 +11911,146 @@ mod hir_opt_tests { Return v31 "); } + + #[test] + fn recompile_after_ep_escape_uses_ep_locals() { + // When a method creates a lambda, EP escapes to the heap. After + // invalidation and recompilation, the compiler must use EP-based + // locals (SetLocal/GetLocal) instead of SSA locals, because the + // spill target (stack) and the read target (heap EP) diverge. + eval(" + CONST = {}.freeze + def test_ep_escape(list, sep=nil, iter_method=:each) + sep ||= lambda { } + kwsplat = CONST + list.__send__(iter_method) {|*v| yield(*v) } + end + + test_ep_escape({a: 1}, nil, :each_pair) { |k, v| + test_ep_escape([1], lambda { }) { |x| } + } + test_ep_escape({a: 1}, nil, :each_pair) { |k, v| + test_ep_escape([1], lambda { }) { |x| } + } + "); + assert_snapshot!(hir_string("test_ep_escape"), @r" + fn test_ep_escape@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :list, l0, SP@7 + v3:BasicObject = GetLocal :sep, l0, SP@6 + v4:BasicObject = GetLocal :iter_method, l0, SP@5 + v5:NilClass = Const Value(nil) + v6:CPtr = LoadPC + v7:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v8:CBool = IsBitEqual v6, v7 + IfTrue v8, bb2(v1, v2, v3, v4, v5) + v10:CPtr[CPtr(0x1000)] = Const CPtr(0x1008) + v11:CBool = IsBitEqual v6, v10 + IfTrue v11, bb4(v1, v2, v3, v4, v5) + Jump bb6(v1, v2, v3, v4, v5) + bb1(v15:BasicObject, v16:BasicObject): + EntryPoint JIT(0) + v17:NilClass = Const Value(nil) + v18:NilClass = Const Value(nil) + v19:NilClass = Const Value(nil) + Jump bb2(v15, v16, v17, v18, v19) + bb2(v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass): + v42:NilClass = Const Value(nil) + SetLocal :sep, l0, EP@5, v42 + Jump bb4(v35, v36, v42, v38, v39) + bb3(v22:BasicObject, v23:BasicObject, v24:BasicObject): + EntryPoint JIT(1) + v25:NilClass = Const Value(nil) + v26:NilClass = Const Value(nil) + Jump bb4(v22, v23, v24, v25, v26) + bb4(v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass): + v53:StaticSymbol[:each] = Const Value(VALUE(0x1010)) + SetLocal :iter_method, l0, EP@4, v53 + Jump bb6(v46, v47, v48, v53, v50) + bb5(v29:BasicObject, v30:BasicObject, v31:BasicObject, v32:BasicObject): + EntryPoint JIT(2) + v33:NilClass = Const Value(nil) + Jump bb6(v29, v30, v31, v32, v33) + bb6(v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:NilClass): + CheckInterrupts + v67:CBool = Test v59 + v68:Truthy = RefineType v59, Truthy + IfTrue v67, bb7(v57, v58, v68, v60, v61) + v70:Falsy = RefineType v59, Falsy + PatchPoint NoSingletonClass(Object@0x1018) + PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028) + v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] + v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058 + v74:BasicObject = GetLocal :list, l0, EP@6 + v76:BasicObject = GetLocal :iter_method, l0, EP@4 + v77:BasicObject = GetLocal :kwsplat, l0, EP@3 + SetLocal :sep, l0, EP@5, v115 + Jump bb7(v57, v74, v115, v76, v77) + bb7(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject): + PatchPoint SingleRactorMode + PatchPoint StableConstantNames(0x1060, CONST) + v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) + SetLocal :kwsplat, l0, EP@3, v110 + v95:BasicObject = GetLocal :list, l0, EP@6 + v97:BasicObject = GetLocal :iter_method, l0, EP@4 + v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized + v100:BasicObject = GetLocal :list, l0, EP@6 + v101:BasicObject = GetLocal :sep, l0, EP@5 + v102:BasicObject = GetLocal :iter_method, l0, EP@4 + v103:BasicObject = GetLocal :kwsplat, l0, EP@3 + CheckInterrupts + Return v99 + "); + } + + #[test] + fn test_array_each() { + eval("[1, 2, 3].each { |x| x }"); + assert_snapshot!(hir_string_proc("Array.instance_method(:each)"), @r" + fn each@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + 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) + v15:TrueClass|NilClass = Defined yield, v13 + v17:CBool = Test v15 + IfFalse v17, bb3(v8, v9) + v35:Fixnum[0] = Const Value(0) + Jump bb7(v8, v35) + bb3(v23:BasicObject, v24:NilClass): + v28:BasicObject = InvokeBuiltin , v23 + CheckInterrupts + Return v28 + bb7(v48:BasicObject, v49:Fixnum): + v83:Array = RefineType v48, Array + v84:CInt64 = ArrayLength v83 + v85:Fixnum = BoxFixnum v84 + v86:BoolExact = FixnumGe v49, v85 + IncrCounter inline_cfunc_optimized_send_count + v54:CBool = Test v86 + IfFalse v54, bb6(v48, v49) + CheckInterrupts + Return v48 + bb6(v67:BasicObject, v68:Fixnum): + v88:Array = RefineType v67, Array + v89:CInt64 = UnboxFixnum v68 + v90:BasicObject = ArrayAref v88, v89 + IncrCounter inline_cfunc_optimized_send_count + v74:BasicObject = InvokeBlock, v90 # SendFallbackReason: Uncategorized(invokeblock) + v92:Fixnum[1] = Const Value(1) + v93:Fixnum = FixnumAdd v68, v92 + IncrCounter inline_cfunc_optimized_send_count + PatchPoint NoEPEscape(each) + Jump bb7(v67, v93) + "); + } } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index ef1d9597ddacce..0401ffcd7d6738 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -2629,6 +2629,32 @@ pub mod hir_build_tests { "); } + #[test] + fn test_getconstant() { + eval(" + def test(klass) + klass::ARGV + end + "); + assert_contains_opcode("test", YARVINSN_getconstant); + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :klass, l0, SP@4 + Jump bb2(v1, v2) + bb1(v5:BasicObject, v6:BasicObject): + EntryPoint JIT(0) + Jump bb2(v5, v6) + bb2(v8:BasicObject, v9:BasicObject): + v14:FalseClass = Const Value(false) + v16:BasicObject = GetConstant v9, :ARGV, v14 + CheckInterrupts + Return v16 + "); + } + #[test] fn test_getinstancevariable() { eval(" @@ -4043,6 +4069,54 @@ pub mod hir_build_tests { SideExit TooManyKeywordParameters "); } + + #[test] + fn test_array_each() { + assert_snapshot!(hir_string_proc("Array.instance_method(:each)"), @r" + fn each@: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + 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) + v15:TrueClass|NilClass = Defined yield, v13 + v17:CBool = Test v15 + v18:NilClass = RefineType v15, Falsy + IfFalse v17, bb3(v8, v9) + v20:TrueClass = RefineType v15, Truthy + Jump bb5(v8, v9) + bb3(v23:BasicObject, v24:NilClass): + v28:BasicObject = InvokeBuiltin , v23 + Jump bb4(v23, v24, v28) + bb4(v40:BasicObject, v41:NilClass, v42:BasicObject): + CheckInterrupts + Return v42 + bb5(v30:BasicObject, v31:NilClass): + v35:Fixnum[0] = Const Value(0) + Jump bb7(v30, v35) + bb7(v48:BasicObject, v49:Fixnum): + v52:BoolExact = InvokeBuiltin rb_jit_ary_at_end, v48, v49 + v54:CBool = Test v52 + v55:FalseClass = RefineType v52, Falsy + IfFalse v54, bb6(v48, v49) + v57:TrueClass = RefineType v52, Truthy + v59:NilClass = Const Value(nil) + CheckInterrupts + Return v48 + bb6(v67:BasicObject, v68:Fixnum): + v72:BasicObject = InvokeBuiltin rb_jit_ary_at, v67, v68 + v74:BasicObject = InvokeBlock, v72 # SendFallbackReason: Uncategorized(invokeblock) + v78:Fixnum = InvokeBuiltin rb_jit_fixnum_inc, v67, v68 + PatchPoint NoEPEscape(each) + Jump bb7(v67, v78) + "); + } } /// Test successor and predecessor set computations. diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index f1180acf2a2689..7aa13cbfcb4bdd 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -206,20 +206,22 @@ pub extern "C" fn rb_zjit_invalidate_no_ep_escape(iseq: IseqPtr) { return; } - // Remember that this ISEQ may escape EP - let invariants = ZJITState::get_invariants(); - invariants.ep_escape_iseqs.insert(iseq); + with_vm_lock(src_loc!(), || { + // Remember that this ISEQ may escape EP + let invariants = ZJITState::get_invariants(); + invariants.ep_escape_iseqs.insert(iseq); - // If the ISEQ has been compiled assuming it doesn't escape EP, invalidate the JIT code. - if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.get(&iseq) { - debug!("EP is escaped: {}", iseq_name(iseq)); + // If the ISEQ has been compiled assuming it doesn't escape EP, invalidate the JIT code. + if let Some(patch_points) = invariants.no_ep_escape_iseq_patch_points.get(&iseq) { + debug!("EP is escaped: {}", iseq_name(iseq)); - // Invalidate the patch points for this ISEQ - let cb = ZJITState::get_code_block(); - compile_patch_points!(cb, patch_points, "EP is escaped: {}", iseq_name(iseq)); + // Invalidate the patch points for this ISEQ + let cb = ZJITState::get_code_block(); + compile_patch_points!(cb, patch_points, "EP is escaped: {}", iseq_name(iseq)); - cb.mark_all_executable(); - } + cb.mark_all_executable(); + } + }); } /// Track that JIT code for a ISEQ will assume that base pointer is equal to environment pointer. diff --git a/zjit/src/state.rs b/zjit/src/state.rs index a807be3f12d4c9..04411e7efbac4e 100644 --- a/zjit/src/state.rs +++ b/zjit/src/state.rs @@ -1,7 +1,7 @@ //! Runtime state of ZJIT. use crate::codegen::{gen_entry_trampoline, gen_exit_trampoline, gen_exit_trampoline_with_counter, gen_function_stub_hit_trampoline}; -use crate::cruby::{self, rb_bug_panic_hook, rb_vm_insn_count, src_loc, EcPtr, Qnil, Qtrue, rb_vm_insn_addr2opcode, rb_profile_frames, VALUE, VM_INSTRUCTION_SIZE, size_t, rb_gc_mark, with_vm_lock}; +use crate::cruby::{self, rb_bug_panic_hook, rb_vm_insn_count, src_loc, EcPtr, Qnil, Qtrue, rb_vm_insn_addr2opcode, rb_profile_frames, VALUE, VM_INSTRUCTION_SIZE, size_t, rb_gc_mark, with_vm_lock, rust_str_to_id, rb_funcallv, rb_const_get, rb_cRubyVM}; use crate::cruby_methods; use crate::invariants::Invariants; use crate::asm::CodeBlock; @@ -319,7 +319,11 @@ pub extern "C" fn rb_zjit_init(zjit_enabled: bool) { /// Enable ZJIT compilation. fn zjit_enable() { - // TODO: call RubyVM::ZJIT::call_jit_hooks here + // Call ZJIT hooks before enabling ZJIT to avoid compiling the hooks themselves + unsafe { + let zjit = rb_const_get(rb_cRubyVM, rust_str_to_id("ZJIT")); + rb_funcallv(zjit, rust_str_to_id("call_jit_hooks"), 0, std::ptr::null()); + } // Catch panics to avoid UB for unwinding into C frames. // See https://doc.rust-lang.org/nomicon/exception-safety.html