summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--array.c13
-rw-r--r--compile.c29
-rw-r--r--defs/id.def1
-rw-r--r--insns.def3
-rw-r--r--internal/basic_operators.h1
-rw-r--r--pack.c6
-rw-r--r--rjit_c.c1
-rw-r--r--rjit_c.rb4
-rw-r--r--test/ruby/test_pack.rb18
-rwxr-xr-xtool/rjit/bindgen.rb1
-rw-r--r--vm.c1
-rw-r--r--vm_insnhelper.c16
-rw-r--r--yjit/src/codegen.rs39
-rw-r--r--yjit/src/cruby.rs1
-rw-r--r--yjit/src/cruby_bindings.inc.rs3
15 files changed, 132 insertions, 5 deletions
diff --git a/array.c b/array.c
index 6ad69dd23f..8355a45da5 100644
--- a/array.c
+++ b/array.c
@@ -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)
{
diff --git a/compile.c b/compile.c
index 5167f4d61c..bdccd36cf5 100644
--- a/compile.c
+++ b/compile.c
@@ -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
diff --git a/insns.def b/insns.def
index 9c649904b8..3858ad3c6d 100644
--- a/insns.def
+++ b/insns.def
@@ -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_
};
diff --git a/pack.c b/pack.c
index 4fdaf7fd89..1b0d66f990 100644
--- a/pack.c
+++ b/pack.c
@@ -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[] =
diff --git a/rjit_c.c b/rjit_c.c
index e6d8d5da5c..e9863129a1 100644
--- a/rjit_c.c
+++ b/rjit_c.c
@@ -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);
diff --git a/rjit_c.rb b/rjit_c.rb
index b9a5bb0b55..f6d4826135 100644
--- a/rjit_c.rb
+++ b/rjit_c.rb
@@ -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
diff --git a/vm.c b/vm.c
index 7e39940017..550ded3209 100644
--- a/vm.c
+++ b/vm.c
@@ -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;