diff options
-rw-r--r-- | array.c | 13 | ||||
-rw-r--r-- | compile.c | 29 | ||||
-rw-r--r-- | defs/id.def | 1 | ||||
-rw-r--r-- | insns.def | 3 | ||||
-rw-r--r-- | internal/basic_operators.h | 1 | ||||
-rw-r--r-- | pack.c | 6 | ||||
-rw-r--r-- | rjit_c.c | 1 | ||||
-rw-r--r-- | rjit_c.rb | 4 | ||||
-rw-r--r-- | test/ruby/test_pack.rb | 18 | ||||
-rwxr-xr-x | tool/rjit/bindgen.rb | 1 | ||||
-rw-r--r-- | vm.c | 1 | ||||
-rw-r--r-- | vm_insnhelper.c | 16 | ||||
-rw-r--r-- | yjit/src/codegen.rs | 39 | ||||
-rw-r--r-- | yjit/src/cruby.rs | 1 | ||||
-rw-r--r-- | yjit/src/cruby_bindings.inc.rs | 3 |
15 files changed, 132 insertions, 5 deletions
@@ -880,6 +880,19 @@ rb_ary_free(VALUE ary) } } +VALUE +rb_setup_fake_ary(struct RArray *fake_ary, const VALUE *list, long len, bool freeze) +{ + fake_ary->basic.flags = T_ARRAY; + VALUE ary = (VALUE)fake_ary; + RBASIC_CLEAR_CLASS(ary); + ARY_SET_PTR(ary, list); + ARY_SET_HEAP_LEN(ary, len); + ARY_SET_CAPA(ary, len); + if (freeze) OBJ_FREEZE(ary); + return ary; +} + size_t rb_ary_memsize(VALUE ary) { @@ -4048,6 +4048,8 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id) return COMPILE_OK; } +#define vm_ci_simple(ci) (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) + static int iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) { @@ -4059,24 +4061,43 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) INSN *niobj = (INSN *)iobj->link.next; if (IS_INSN_ID(niobj, send)) { const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0); - if ((vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) && vm_ci_argc(ci) == 0) { + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 0) { switch (vm_ci_mid(ci)) { case idMax: case idMin: case idHash: { VALUE num = iobj->operands[0]; + int operand_len = insn_len(BIN(opt_newarray_send)) - 1; iobj->insn_id = BIN(opt_newarray_send); - iobj->operands = compile_data_calloc2(iseq, insn_len(iobj->insn_id) - 1, sizeof(VALUE)); + iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); iobj->operands[0] = num; iobj->operands[1] = rb_id2sym(vm_ci_mid(ci)); - iobj->operand_size = insn_len(iobj->insn_id) - 1; + iobj->operand_size = operand_len; ELEM_REMOVE(&niobj->link); return COMPILE_OK; } } } } + else if ((IS_INSN_ID(niobj, putstring) || + (IS_INSN_ID(niobj, putobject) && RB_TYPE_P(OPERAND_AT(niobj, 0), T_STRING))) && + IS_NEXT_INSN_ID(&niobj->link, send)) { + const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT((INSN *)niobj->link.next, 0); + if (vm_ci_simple(ci) && vm_ci_argc(ci) == 1 && vm_ci_mid(ci) == idPack) { + VALUE num = iobj->operands[0]; + int operand_len = insn_len(BIN(opt_newarray_send)) - 1; + iobj->insn_id = BIN(opt_newarray_send); + iobj->operands = compile_data_calloc2(iseq, operand_len, sizeof(VALUE)); + iobj->operands[0] = FIXNUM_INC(num, 1); + iobj->operands[1] = rb_id2sym(vm_ci_mid(ci)); + iobj->operand_size = operand_len; + ELEM_REMOVE(&iobj->link); + ELEM_REMOVE(niobj->link.next); + ELEM_INSERT_NEXT(&niobj->link, &iobj->link); + return COMPILE_OK; + } + } } if (IS_INSN_ID(iobj, send)) { @@ -4084,7 +4105,7 @@ iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj) const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 1); #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt)) - if (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE) { + if (vm_ci_simple(ci)) { switch (vm_ci_argc(ci)) { case 0: switch (vm_ci_mid(ci)) { diff --git a/defs/id.def b/defs/id.def index 2ddde7be70..71baa4f968 100644 --- a/defs/id.def +++ b/defs/id.def @@ -59,6 +59,7 @@ firstline, predefined = __LINE__+1, %[\ name nil path + pack _ UScore @@ -977,6 +977,9 @@ opt_newarray_send case idMax: val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num)); break; + case idPack: + val = rb_vm_opt_newarray_pack(ec, (long)num-1, STACK_ADDR_FROM_TOP(num), TOPN(0)); + break; default: rb_bug("unreachable"); } diff --git a/internal/basic_operators.h b/internal/basic_operators.h index a59403631e..4732a65403 100644 --- a/internal/basic_operators.h +++ b/internal/basic_operators.h @@ -37,6 +37,7 @@ enum ruby_basic_operators { BOP_OR, BOP_CMP, BOP_DEFAULT, + BOP_PACK, BOP_LAST_ }; @@ -782,6 +782,12 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) return res; } +VALUE +rb_ec_pack_ary(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) +{ + return pack_pack(ec, ary, fmt, buffer); +} + static const char uu_table[] = "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; static const char b64_table[] = @@ -519,6 +519,7 @@ extern VALUE rb_vm_getclassvariable(const rb_iseq_t *iseq, const rb_control_fram extern VALUE rb_vm_opt_newarray_min(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr); extern VALUE rb_vm_opt_newarray_max(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr); extern VALUE rb_vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr); +extern VALUE rb_vm_opt_newarray_pack(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr, VALUE fmt); extern VALUE rb_vm_splat_array(VALUE flag, VALUE array); extern bool rb_simple_iseq_p(const rb_iseq_t *iseq); extern bool rb_vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_type, VALUE obj, VALUE v); @@ -691,6 +691,10 @@ module RubyVM::RJIT # :nodoc: all Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_opt_newarray_min) } end + def C.rb_vm_opt_newarray_pack + Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_opt_newarray_pack) } + end + def C.rb_vm_set_ivar_id Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_set_ivar_id) } end diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb index 1ce46e8916..a0c66c188b 100644 --- a/test/ruby/test_pack.rb +++ b/test/ruby/test_pack.rb @@ -895,4 +895,22 @@ EXPECTED } assert_equal [nil], "a".unpack("C", offset: 1) end + + def test_monkey_pack + assert_separately([], <<-'end;') + $-w = false + class Array + alias :old_pack :pack + def pack _; "oh no"; end + end + + v = [2 ** 15].pack('n') + + class Array + alias :pack :old_pack + end + + assert_equal "oh no", v + end; + end end diff --git a/tool/rjit/bindgen.rb b/tool/rjit/bindgen.rb index fb6653ed9c..2fc4576216 100755 --- a/tool/rjit/bindgen.rb +++ b/tool/rjit/bindgen.rb @@ -540,6 +540,7 @@ generator = BindingGenerator.new( rb_vm_opt_newarray_min rb_vm_opt_newarray_max rb_vm_opt_newarray_hash + rb_vm_opt_newarray_pack rb_vm_setinstancevariable rb_vm_splat_array rjit_full_cfunc_return @@ -2278,6 +2278,7 @@ vm_redefinition_bop_for_id(ID mid) OP(NilP, NIL_P); OP(Cmp, CMP); OP(Default, DEFAULT); + OP(Pack, PACK); #undef OP } return -1; diff --git a/vm_insnhelper.c b/vm_insnhelper.c index a1893b1ba2..1e1fcea120 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -5932,6 +5932,22 @@ rb_vm_opt_newarray_hash(rb_execution_context_t *ec, rb_num_t num, const VALUE *p return vm_opt_newarray_hash(ec, num, ptr); } +VALUE rb_setup_fake_ary(struct RArray *fake_ary, const VALUE *list, long len, bool freeze); +VALUE rb_ec_pack_ary(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer); + +VALUE +rb_vm_opt_newarray_pack(rb_execution_context_t *ec, rb_num_t num, const VALUE *ptr, VALUE fmt) +{ + if (BASIC_OP_UNREDEFINED_P(BOP_PACK, ARRAY_REDEFINED_OP_FLAG)) { + struct RArray fake_ary; + VALUE ary = rb_setup_fake_ary(&fake_ary, ptr, num, true); + return rb_ec_pack_ary(ec, ary, fmt, Qnil); + } + else { + return rb_vm_call_with_refinements(ec, rb_ary_new4(num, ptr), idPack, 1, &fmt, RB_PASS_CALLED_KEYWORDS); + } +} + #undef id_cmp #define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0 diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 072d96f1b0..747fa1769d 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -4235,11 +4235,50 @@ fn gen_opt_newarray_send( gen_opt_newarray_max(jit, asm, _ocb) } else if method == ID!(hash) { gen_opt_newarray_hash(jit, asm, _ocb) + } else if method == ID!(pack) { + gen_opt_newarray_pack(jit, asm, _ocb) } else { None } } +fn gen_opt_newarray_pack( + jit: &mut JITState, + asm: &mut Assembler, + _ocb: &mut OutlinedCb, +) -> Option<CodegenStatus> { + // num == 4 ( for this code ) + let num = jit.get_arg(0).as_u32(); + + // Save the PC and SP because we may call #pack + jit_prepare_non_leaf_call(jit, asm); + + extern "C" { + fn rb_vm_opt_newarray_pack(ec: EcPtr, num: u32, elts: *const VALUE, fmt: VALUE) -> VALUE; + } + + let values_opnd = asm.ctx.sp_opnd(-(num as i32)); + let values_ptr = asm.lea(values_opnd); + + let fmt_string = asm.ctx.sp_opnd(-1); + + let val_opnd = asm.ccall( + rb_vm_opt_newarray_pack as *const u8, + vec![ + EC, + (num - 1).into(), + values_ptr, + fmt_string + ], + ); + + asm.stack_pop(num.as_usize()); + let stack_ret = asm.stack_push(Type::CString); + asm.mov(stack_ret, val_opnd); + + Some(KeepCompiling) +} + fn gen_opt_newarray_hash( jit: &mut JITState, asm: &mut Assembler, diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 68c0304b06..53586cb4f4 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -802,6 +802,7 @@ pub(crate) mod ids { name: min content: b"min" name: max content: b"max" name: hash content: b"hash" + name: pack content: b"pack" name: respond_to_missing content: b"respond_to_missing?" name: to_ary content: b"to_ary" name: eq content: b"==" diff --git a/yjit/src/cruby_bindings.inc.rs b/yjit/src/cruby_bindings.inc.rs index a03c2d0f00..d53e630a4a 100644 --- a/yjit/src/cruby_bindings.inc.rs +++ b/yjit/src/cruby_bindings.inc.rs @@ -342,7 +342,8 @@ pub const BOP_AND: ruby_basic_operators = 28; pub const BOP_OR: ruby_basic_operators = 29; pub const BOP_CMP: ruby_basic_operators = 30; pub const BOP_DEFAULT: ruby_basic_operators = 31; -pub const BOP_LAST_: ruby_basic_operators = 32; +pub const BOP_PACK: ruby_basic_operators = 32; +pub const BOP_LAST_: ruby_basic_operators = 33; pub type ruby_basic_operators = u32; pub type rb_serial_t = ::std::os::raw::c_ulonglong; pub const imemo_env: imemo_type = 0; |