Introduce a specialize instruction for Array#pack
Instructions for this code: ```ruby # frozen_string_literal: true [a].pack("C") ``` Before this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 newarray 1 0005 putobject "C" 0007 opt_send_without_block <calldata!mid:pack, argc:1, ARGS_SIMPLE> 0009 leave ``` After this commit: ``` == disasm: #<ISeq:<main>@test.rb:1 (1,0)-(3,13)> 0000 putself ( 3)[Li] 0001 opt_send_without_block <calldata!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE> 0003 putobject "C" 0005 opt_newarray_send 2, :pack 0008 leave ``` Co-authored-by: Maxime Chevalier-Boisvert <maxime.chevalierboisvert@shopify.com> Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
This commit is contained in:
parent
e5e079e70f
commit
49fcd33e13
13
array.c
13
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
|
size_t
|
||||||
rb_ary_memsize(VALUE ary)
|
rb_ary_memsize(VALUE ary)
|
||||||
{
|
{
|
||||||
|
29
compile.c
29
compile.c
@ -4048,6 +4048,8 @@ insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
|
|||||||
return COMPILE_OK;
|
return COMPILE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define vm_ci_simple(ci) (vm_ci_flag(ci) & VM_CALL_ARGS_SIMPLE)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
|
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;
|
INSN *niobj = (INSN *)iobj->link.next;
|
||||||
if (IS_INSN_ID(niobj, send)) {
|
if (IS_INSN_ID(niobj, send)) {
|
||||||
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(niobj, 0);
|
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)) {
|
switch (vm_ci_mid(ci)) {
|
||||||
case idMax:
|
case idMax:
|
||||||
case idMin:
|
case idMin:
|
||||||
case idHash:
|
case idHash:
|
||||||
{
|
{
|
||||||
VALUE num = iobj->operands[0];
|
VALUE num = iobj->operands[0];
|
||||||
|
int operand_len = insn_len(BIN(opt_newarray_send)) - 1;
|
||||||
iobj->insn_id = BIN(opt_newarray_send);
|
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[0] = num;
|
||||||
iobj->operands[1] = rb_id2sym(vm_ci_mid(ci));
|
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);
|
ELEM_REMOVE(&niobj->link);
|
||||||
return COMPILE_OK;
|
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)) {
|
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);
|
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))
|
#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)) {
|
switch (vm_ci_argc(ci)) {
|
||||||
case 0:
|
case 0:
|
||||||
switch (vm_ci_mid(ci)) {
|
switch (vm_ci_mid(ci)) {
|
||||||
|
@ -59,6 +59,7 @@ firstline, predefined = __LINE__+1, %[\
|
|||||||
name
|
name
|
||||||
nil
|
nil
|
||||||
path
|
path
|
||||||
|
pack
|
||||||
|
|
||||||
_ UScore
|
_ UScore
|
||||||
|
|
||||||
|
@ -977,6 +977,9 @@ opt_newarray_send
|
|||||||
case idMax:
|
case idMax:
|
||||||
val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
|
val = vm_opt_newarray_max(ec, num, STACK_ADDR_FROM_TOP(num));
|
||||||
break;
|
break;
|
||||||
|
case idPack:
|
||||||
|
val = rb_vm_opt_newarray_pack(ec, (long)num-1, STACK_ADDR_FROM_TOP(num), TOPN(0));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rb_bug("unreachable");
|
rb_bug("unreachable");
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ enum ruby_basic_operators {
|
|||||||
BOP_OR,
|
BOP_OR,
|
||||||
BOP_CMP,
|
BOP_CMP,
|
||||||
BOP_DEFAULT,
|
BOP_DEFAULT,
|
||||||
|
BOP_PACK,
|
||||||
|
|
||||||
BOP_LAST_
|
BOP_LAST_
|
||||||
};
|
};
|
||||||
|
6
pack.c
6
pack.c
@ -782,6 +782,12 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
|
|||||||
return res;
|
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[] =
|
static const char uu_table[] =
|
||||||
"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
|
"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
|
||||||
static const char b64_table[] =
|
static const char b64_table[] =
|
||||||
|
1
rjit_c.c
1
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_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_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_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 VALUE rb_vm_splat_array(VALUE flag, VALUE array);
|
||||||
extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
|
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);
|
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) }
|
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_opt_newarray_min) }
|
||||||
end
|
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
|
def C.rb_vm_set_ivar_id
|
||||||
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_set_ivar_id) }
|
Primitive.cexpr! %q{ SIZET2NUM((size_t)rb_vm_set_ivar_id) }
|
||||||
end
|
end
|
||||||
|
@ -895,4 +895,22 @@ EXPECTED
|
|||||||
}
|
}
|
||||||
assert_equal [nil], "a".unpack("C", offset: 1)
|
assert_equal [nil], "a".unpack("C", offset: 1)
|
||||||
end
|
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
|
end
|
||||||
|
@ -540,6 +540,7 @@ generator = BindingGenerator.new(
|
|||||||
rb_vm_opt_newarray_min
|
rb_vm_opt_newarray_min
|
||||||
rb_vm_opt_newarray_max
|
rb_vm_opt_newarray_max
|
||||||
rb_vm_opt_newarray_hash
|
rb_vm_opt_newarray_hash
|
||||||
|
rb_vm_opt_newarray_pack
|
||||||
rb_vm_setinstancevariable
|
rb_vm_setinstancevariable
|
||||||
rb_vm_splat_array
|
rb_vm_splat_array
|
||||||
rjit_full_cfunc_return
|
rjit_full_cfunc_return
|
||||||
|
1
vm.c
1
vm.c
@ -2278,6 +2278,7 @@ vm_redefinition_bop_for_id(ID mid)
|
|||||||
OP(NilP, NIL_P);
|
OP(NilP, NIL_P);
|
||||||
OP(Cmp, CMP);
|
OP(Cmp, CMP);
|
||||||
OP(Default, DEFAULT);
|
OP(Default, DEFAULT);
|
||||||
|
OP(Pack, PACK);
|
||||||
#undef OP
|
#undef OP
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -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);
|
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
|
#undef id_cmp
|
||||||
|
|
||||||
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0
|
#define IMEMO_CONST_CACHE_SHAREABLE IMEMO_FL_USER0
|
||||||
|
@ -4235,11 +4235,50 @@ fn gen_opt_newarray_send(
|
|||||||
gen_opt_newarray_max(jit, asm, _ocb)
|
gen_opt_newarray_max(jit, asm, _ocb)
|
||||||
} else if method == ID!(hash) {
|
} else if method == ID!(hash) {
|
||||||
gen_opt_newarray_hash(jit, asm, _ocb)
|
gen_opt_newarray_hash(jit, asm, _ocb)
|
||||||
|
} else if method == ID!(pack) {
|
||||||
|
gen_opt_newarray_pack(jit, asm, _ocb)
|
||||||
} else {
|
} else {
|
||||||
None
|
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(
|
fn gen_opt_newarray_hash(
|
||||||
jit: &mut JITState,
|
jit: &mut JITState,
|
||||||
asm: &mut Assembler,
|
asm: &mut Assembler,
|
||||||
|
@ -802,6 +802,7 @@ pub(crate) mod ids {
|
|||||||
name: min content: b"min"
|
name: min content: b"min"
|
||||||
name: max content: b"max"
|
name: max content: b"max"
|
||||||
name: hash content: b"hash"
|
name: hash content: b"hash"
|
||||||
|
name: pack content: b"pack"
|
||||||
name: respond_to_missing content: b"respond_to_missing?"
|
name: respond_to_missing content: b"respond_to_missing?"
|
||||||
name: to_ary content: b"to_ary"
|
name: to_ary content: b"to_ary"
|
||||||
name: eq content: b"=="
|
name: eq content: b"=="
|
||||||
|
@ -342,7 +342,8 @@ pub const BOP_AND: ruby_basic_operators = 28;
|
|||||||
pub const BOP_OR: ruby_basic_operators = 29;
|
pub const BOP_OR: ruby_basic_operators = 29;
|
||||||
pub const BOP_CMP: ruby_basic_operators = 30;
|
pub const BOP_CMP: ruby_basic_operators = 30;
|
||||||
pub const BOP_DEFAULT: ruby_basic_operators = 31;
|
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 ruby_basic_operators = u32;
|
||||||
pub type rb_serial_t = ::std::os::raw::c_ulonglong;
|
pub type rb_serial_t = ::std::os::raw::c_ulonglong;
|
||||||
pub const imemo_env: imemo_type = 0;
|
pub const imemo_env: imemo_type = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user