Implement splat for cfuncs. Split exit exit cases to better capture where we are exiting (#6929)
YJIT: Implement splat for cfuncs. Split exit cases This also implements a new check for ruby2keywords as the last argument of a splat. This does mean that we generate more code, but in actual benchmarks where we gained speed from this (binarytrees) I don't see any significant slow down. I did have to struggle here with the register allocator to find code that didn't allocate too many registers. It's a bit hard when everything is implicit. But I think I got to the minimal amount of copying and stuff given our current allocation strategy.
This commit is contained in:
parent
8872ebec6a
commit
762a3d80f7
Notes:
git
2023-01-19 18:43:09 +00:00
Merged-By: maximecb <maximecb@ruby-lang.org>
@ -3456,3 +3456,28 @@ assert_equal 'ok', %q{
|
|||||||
|
|
||||||
cw(4)
|
cw(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_equal 'threw', %q{
|
||||||
|
def foo(args)
|
||||||
|
wrap(*args)
|
||||||
|
rescue ArgumentError
|
||||||
|
'threw'
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap(a)
|
||||||
|
[a]
|
||||||
|
end
|
||||||
|
|
||||||
|
foo([Hash.ruby2_keywords_hash({})])
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_equal 'threw', %q{
|
||||||
|
# C call
|
||||||
|
def bar(args)
|
||||||
|
Array(*args)
|
||||||
|
rescue ArgumentError
|
||||||
|
'threw'
|
||||||
|
end
|
||||||
|
|
||||||
|
bar([Hash.ruby2_keywords_hash({})])
|
||||||
|
}
|
||||||
|
@ -227,6 +227,9 @@ fn main() {
|
|||||||
// From include/ruby/internal/value_type.h
|
// From include/ruby/internal/value_type.h
|
||||||
.allowlist_type("ruby_value_type") // really old C extension API
|
.allowlist_type("ruby_value_type") // really old C extension API
|
||||||
|
|
||||||
|
// From include/ruby/internal/hash.h
|
||||||
|
.allowlist_type("ruby_rhash_flags") // really old C extension API
|
||||||
|
|
||||||
// Autogenerated into id.h
|
// Autogenerated into id.h
|
||||||
.allowlist_type("ruby_method_ids")
|
.allowlist_type("ruby_method_ids")
|
||||||
|
|
||||||
|
@ -1326,6 +1326,40 @@ fn guard_object_is_array(
|
|||||||
asm.jne(side_exit.as_side_exit());
|
asm.jne(side_exit.as_side_exit());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This guards that a special flag is not set on a hash.
|
||||||
|
/// By passing a hash with this flag set as the last argument
|
||||||
|
/// in a splat call, you can change the way keywords are handled
|
||||||
|
/// to behave like ruby 2. We don't currently support this.
|
||||||
|
fn guard_object_is_not_ruby2_keyword_hash(
|
||||||
|
asm: &mut Assembler,
|
||||||
|
object_opnd: Opnd,
|
||||||
|
side_exit: CodePtr,
|
||||||
|
) {
|
||||||
|
asm.comment("guard object is not ruby2 keyword hash");
|
||||||
|
|
||||||
|
let not_ruby2_keyword = asm.new_label("not_ruby2_keyword");
|
||||||
|
asm.test(object_opnd, (RUBY_IMMEDIATE_MASK as u64).into());
|
||||||
|
asm.jnz(not_ruby2_keyword);
|
||||||
|
|
||||||
|
asm.cmp(object_opnd, Qfalse.into());
|
||||||
|
asm.je(not_ruby2_keyword);
|
||||||
|
|
||||||
|
let flags_opnd = asm.load(Opnd::mem(
|
||||||
|
VALUE_BITS,
|
||||||
|
object_opnd,
|
||||||
|
RUBY_OFFSET_RBASIC_FLAGS,
|
||||||
|
));
|
||||||
|
let type_opnd = asm.and(flags_opnd, (RUBY_T_MASK as u64).into());
|
||||||
|
|
||||||
|
asm.cmp(type_opnd, (RUBY_T_HASH as u64).into());
|
||||||
|
asm.jne(not_ruby2_keyword);
|
||||||
|
|
||||||
|
asm.test(flags_opnd, (RHASH_PASS_AS_KEYWORDS as u64).into());
|
||||||
|
asm.jnz(side_exit.as_side_exit());
|
||||||
|
|
||||||
|
asm.write_label(not_ruby2_keyword);
|
||||||
|
}
|
||||||
|
|
||||||
fn guard_object_is_string(
|
fn guard_object_is_string(
|
||||||
asm: &mut Assembler,
|
asm: &mut Assembler,
|
||||||
object_reg: Opnd,
|
object_reg: Opnd,
|
||||||
@ -4567,7 +4601,7 @@ fn gen_send_cfunc(
|
|||||||
) -> CodegenStatus {
|
) -> CodegenStatus {
|
||||||
let cfunc = unsafe { get_cme_def_body_cfunc(cme) };
|
let cfunc = unsafe { get_cme_def_body_cfunc(cme) };
|
||||||
let cfunc_argc = unsafe { get_mct_argc(cfunc) };
|
let cfunc_argc = unsafe { get_mct_argc(cfunc) };
|
||||||
let argc = argc;
|
let mut argc = argc;
|
||||||
|
|
||||||
// Create a side-exit to fall back to the interpreter
|
// Create a side-exit to fall back to the interpreter
|
||||||
let side_exit = get_side_exit(jit, ocb, ctx);
|
let side_exit = get_side_exit(jit, ocb, ctx);
|
||||||
@ -4578,8 +4612,30 @@ fn gen_send_cfunc(
|
|||||||
return CantCompile;
|
return CantCompile;
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
// We aren't handling a vararg cfuncs with splat currently.
|
||||||
gen_counter_incr!(asm, send_args_splat_cfunc);
|
if flags & VM_CALL_ARGS_SPLAT != 0 && cfunc_argc == -1 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_cfunc_var_args);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 && flags & VM_CALL_ZSUPER != 0 {
|
||||||
|
// zsuper methods are super calls without any arguments.
|
||||||
|
// They are also marked as splat, but don't actually have an array
|
||||||
|
// they pull arguments from, instead we need to change to call
|
||||||
|
// a different method with the current stack.
|
||||||
|
gen_counter_incr!(asm, send_args_splat_cfunc_zuper);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to handle backwards compatibility between ruby 3 and 2
|
||||||
|
// ruby2_keywords was introduced. It is called only on methods
|
||||||
|
// with splat and changes they way they handle them.
|
||||||
|
// We are just going to not compile these.
|
||||||
|
// https://docs.ruby-lang.org/en/3.2/Module.html#method-i-ruby2_keywords
|
||||||
|
if unsafe {
|
||||||
|
get_iseq_flags_ruby2_keywords(jit.iseq) && flags & VM_CALL_ARGS_SPLAT != 0
|
||||||
|
} {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_cfunc_ruby2_keywords);
|
||||||
return CantCompile;
|
return CantCompile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4590,6 +4646,11 @@ fn gen_send_cfunc(
|
|||||||
unsafe { get_cikw_keyword_len(kw_arg) }
|
unsafe { get_cikw_keyword_len(kw_arg) }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if kw_arg_num != 0 && flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_cfunc_splat_with_kw);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
if c_method_tracing_currently_enabled(jit) {
|
if c_method_tracing_currently_enabled(jit) {
|
||||||
// Don't JIT if tracing c_call or c_return
|
// Don't JIT if tracing c_call or c_return
|
||||||
gen_counter_incr!(asm, send_cfunc_tracing);
|
gen_counter_incr!(asm, send_cfunc_tracing);
|
||||||
@ -4622,14 +4683,15 @@ fn gen_send_cfunc(
|
|||||||
|
|
||||||
// Number of args which will be passed through to the callee
|
// Number of args which will be passed through to the callee
|
||||||
// This is adjusted by the kwargs being combined into a hash.
|
// This is adjusted by the kwargs being combined into a hash.
|
||||||
let passed_argc = if kw_arg.is_null() {
|
let mut passed_argc = if kw_arg.is_null() {
|
||||||
argc
|
argc
|
||||||
} else {
|
} else {
|
||||||
argc - kw_arg_num + 1
|
argc - kw_arg_num + 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// If the argument count doesn't match
|
// If the argument count doesn't match
|
||||||
if cfunc_argc >= 0 && cfunc_argc != passed_argc {
|
if cfunc_argc >= 0 && cfunc_argc != passed_argc && flags & VM_CALL_ARGS_SPLAT == 0 {
|
||||||
gen_counter_incr!(asm, send_cfunc_argc_mismatch);
|
gen_counter_incr!(asm, send_cfunc_argc_mismatch);
|
||||||
return CantCompile;
|
return CantCompile;
|
||||||
}
|
}
|
||||||
@ -4682,6 +4744,22 @@ fn gen_send_cfunc(
|
|||||||
handle_opt_send_shift_stack(asm, argc, ctx);
|
handle_opt_send_shift_stack(asm, argc, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// push_splat_args does stack manipulation so we can no longer side exit
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
let required_args : u32 = (cfunc_argc as u32).saturating_sub(argc as u32 - 1);
|
||||||
|
// + 1 because we pass self
|
||||||
|
if required_args + 1 >= C_ARG_OPNDS.len() as u32 {
|
||||||
|
gen_counter_incr!(asm, send_cfunc_toomany_args);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
// We are going to assume that the splat fills
|
||||||
|
// all the remaining arguments. In the generated code
|
||||||
|
// we test if this is true and if not side exit.
|
||||||
|
argc = required_args as i32;
|
||||||
|
passed_argc = argc;
|
||||||
|
push_splat_args(required_args, ctx, asm, ocb, side_exit)
|
||||||
|
}
|
||||||
|
|
||||||
// Points to the receiver operand on the stack
|
// Points to the receiver operand on the stack
|
||||||
let recv = ctx.stack_opnd(argc);
|
let recv = ctx.stack_opnd(argc);
|
||||||
|
|
||||||
@ -4812,13 +4890,13 @@ fn gen_return_branch(
|
|||||||
/// Pushes arguments from an array to the stack that are passed with a splat (i.e. *args)
|
/// Pushes arguments from an array to the stack that are passed with a splat (i.e. *args)
|
||||||
/// It optimistically compiles to a static size that is the exact number of arguments
|
/// It optimistically compiles to a static size that is the exact number of arguments
|
||||||
/// needed for the function.
|
/// needed for the function.
|
||||||
fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr) {
|
fn push_splat_args(required_args: u32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: CodePtr) {
|
||||||
|
|
||||||
asm.comment("push_splat_args");
|
asm.comment("push_splat_args");
|
||||||
|
|
||||||
let array_opnd = ctx.stack_opnd(0);
|
let array_opnd = ctx.stack_opnd(0);
|
||||||
|
|
||||||
let array_reg = asm.load(array_opnd);
|
let array_reg = asm.load(array_opnd);
|
||||||
|
|
||||||
guard_object_is_heap(
|
guard_object_is_heap(
|
||||||
asm,
|
asm,
|
||||||
array_reg,
|
array_reg,
|
||||||
@ -4830,6 +4908,8 @@ fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, o
|
|||||||
counted_exit!(ocb, side_exit, send_splat_not_array),
|
counted_exit!(ocb, side_exit, send_splat_not_array),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
asm.comment("Get array length for embedded or heap");
|
||||||
|
|
||||||
// Pull out the embed flag to check if it's an embedded array.
|
// Pull out the embed flag to check if it's an embedded array.
|
||||||
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
|
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
|
||||||
|
|
||||||
@ -4840,25 +4920,53 @@ fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, o
|
|||||||
// Conditionally move the length of the heap array
|
// Conditionally move the length of the heap array
|
||||||
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
|
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
|
||||||
asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
|
asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
|
||||||
|
|
||||||
|
// Need to repeat this here to deal with register allocation
|
||||||
|
let array_opnd = ctx.stack_opnd(0);
|
||||||
|
let array_reg = asm.load(array_opnd);
|
||||||
|
|
||||||
let array_len_opnd = Opnd::mem(
|
let array_len_opnd = Opnd::mem(
|
||||||
(8 * size_of::<std::os::raw::c_long>()) as u8,
|
(8 * size_of::<std::os::raw::c_long>()) as u8,
|
||||||
asm.load(array_opnd),
|
array_reg,
|
||||||
RUBY_OFFSET_RARRAY_AS_HEAP_LEN,
|
RUBY_OFFSET_RARRAY_AS_HEAP_LEN,
|
||||||
);
|
);
|
||||||
let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd);
|
let array_len_opnd = asm.csel_nz(emb_len_opnd, array_len_opnd);
|
||||||
|
|
||||||
// Only handle the case where the number of values in the array is equal to the number requested
|
asm.comment("Side exit if length doesn't not equal remaining args");
|
||||||
asm.cmp(array_len_opnd, required_args.into());
|
asm.cmp(array_len_opnd, required_args.into());
|
||||||
asm.jne(counted_exit!(ocb, side_exit, send_splatarray_length_not_equal).as_side_exit());
|
asm.jne(counted_exit!(ocb, side_exit, send_splatarray_length_not_equal).as_side_exit());
|
||||||
|
|
||||||
|
asm.comment("Check last argument is not ruby2keyword hash");
|
||||||
|
|
||||||
|
// Need to repeat this here to deal with register allocation
|
||||||
|
let array_reg = asm.load(ctx.stack_opnd(0));
|
||||||
|
|
||||||
|
let flags_opnd = Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RBASIC_FLAGS);
|
||||||
|
asm.test(flags_opnd, (RARRAY_EMBED_FLAG as u64).into());
|
||||||
|
let heap_ptr_opnd = Opnd::mem(
|
||||||
|
(8 * size_of::<usize>()) as u8,
|
||||||
|
array_reg,
|
||||||
|
RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
|
||||||
|
);
|
||||||
|
// Load the address of the embedded array
|
||||||
|
// (struct RArray *)(obj)->as.ary
|
||||||
|
let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
|
||||||
|
let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
|
||||||
|
|
||||||
|
let last_array_value = asm.load(Opnd::mem(64, ary_opnd, (required_args as i32 - 1) * (SIZEOF_VALUE as i32)));
|
||||||
|
|
||||||
|
guard_object_is_not_ruby2_keyword_hash(
|
||||||
|
asm,
|
||||||
|
last_array_value,
|
||||||
|
counted_exit!(ocb, side_exit, send_splatarray_last_ruby_2_keywords));
|
||||||
|
|
||||||
|
asm.comment("Push arguments from array");
|
||||||
let array_opnd = ctx.stack_pop(1);
|
let array_opnd = ctx.stack_pop(1);
|
||||||
|
|
||||||
if required_args > 0 {
|
if required_args > 0 {
|
||||||
|
|
||||||
// Load the address of the embedded array
|
// Load the address of the embedded array
|
||||||
// (struct RArray *)(obj)->as.ary
|
// (struct RArray *)(obj)->as.ary
|
||||||
let array_reg = asm.load(array_opnd);
|
let array_reg = asm.load(array_opnd);
|
||||||
let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
|
|
||||||
|
|
||||||
// Conditionally load the address of the heap array
|
// Conditionally load the address of the heap array
|
||||||
// (struct RArray *)(obj)->as.heap.ptr
|
// (struct RArray *)(obj)->as.heap.ptr
|
||||||
@ -4866,16 +4974,20 @@ fn push_splat_args(required_args: i32, ctx: &mut Context, asm: &mut Assembler, o
|
|||||||
asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64));
|
asm.test(flags_opnd, Opnd::UImm(RARRAY_EMBED_FLAG as u64));
|
||||||
let heap_ptr_opnd = Opnd::mem(
|
let heap_ptr_opnd = Opnd::mem(
|
||||||
(8 * size_of::<usize>()) as u8,
|
(8 * size_of::<usize>()) as u8,
|
||||||
asm.load(array_opnd),
|
array_reg,
|
||||||
RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
|
RUBY_OFFSET_RARRAY_AS_HEAP_PTR,
|
||||||
);
|
);
|
||||||
|
// Load the address of the embedded array
|
||||||
|
// (struct RArray *)(obj)->as.ary
|
||||||
|
let ary_opnd = asm.lea(Opnd::mem(VALUE_BITS, array_reg, RUBY_OFFSET_RARRAY_AS_ARY));
|
||||||
let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
|
let ary_opnd = asm.csel_nz(ary_opnd, heap_ptr_opnd);
|
||||||
|
|
||||||
for i in 0..required_args {
|
for i in 0..required_args {
|
||||||
let top = ctx.stack_push(Type::Unknown);
|
let top = ctx.stack_push(Type::Unknown);
|
||||||
asm.mov(top, Opnd::mem(64, ary_opnd, i * SIZEOF_VALUE_I32));
|
asm.mov(top, Opnd::mem(64, ary_opnd, i as i32 * SIZEOF_VALUE_I32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asm.comment("end push_each");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5250,7 +5362,7 @@ fn gen_send_iseq(
|
|||||||
|
|
||||||
// push_splat_args does stack manipulation so we can no longer side exit
|
// push_splat_args does stack manipulation so we can no longer side exit
|
||||||
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
let required_args = num_params as i32 - (argc - 1);
|
let required_args = num_params - (argc as u32 - 1);
|
||||||
// We are going to assume that the splat fills
|
// We are going to assume that the splat fills
|
||||||
// all the remaining arguments. In the generated code
|
// all the remaining arguments. In the generated code
|
||||||
// we test if this is true and if not side exit.
|
// we test if this is true and if not side exit.
|
||||||
@ -5722,11 +5834,6 @@ fn gen_send_general(
|
|||||||
loop {
|
loop {
|
||||||
let def_type = unsafe { get_cme_def_type(cme) };
|
let def_type = unsafe { get_cme_def_type(cme) };
|
||||||
|
|
||||||
if flags & VM_CALL_ARGS_SPLAT != 0 && def_type != VM_METHOD_TYPE_ISEQ {
|
|
||||||
gen_counter_incr!(asm, send_args_splat_non_iseq);
|
|
||||||
return CantCompile;
|
|
||||||
}
|
|
||||||
|
|
||||||
match def_type {
|
match def_type {
|
||||||
VM_METHOD_TYPE_ISEQ => {
|
VM_METHOD_TYPE_ISEQ => {
|
||||||
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
|
let iseq = unsafe { get_def_iseq_ptr((*cme).def) };
|
||||||
@ -5748,6 +5855,11 @@ fn gen_send_general(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
VM_METHOD_TYPE_IVAR => {
|
VM_METHOD_TYPE_IVAR => {
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_ivar);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
if argc != 0 {
|
if argc != 0 {
|
||||||
// Argument count mismatch. Getters take no arguments.
|
// Argument count mismatch. Getters take no arguments.
|
||||||
gen_counter_incr!(asm, send_getter_arity);
|
gen_counter_incr!(asm, send_getter_arity);
|
||||||
@ -5795,6 +5907,10 @@ fn gen_send_general(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
VM_METHOD_TYPE_ATTRSET => {
|
VM_METHOD_TYPE_ATTRSET => {
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_attrset);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
if flags & VM_CALL_KWARG != 0 {
|
if flags & VM_CALL_KWARG != 0 {
|
||||||
gen_counter_incr!(asm, send_attrset_kwargs);
|
gen_counter_incr!(asm, send_attrset_kwargs);
|
||||||
return CantCompile;
|
return CantCompile;
|
||||||
@ -5816,6 +5932,10 @@ fn gen_send_general(
|
|||||||
}
|
}
|
||||||
// Block method, e.g. define_method(:foo) { :my_block }
|
// Block method, e.g. define_method(:foo) { :my_block }
|
||||||
VM_METHOD_TYPE_BMETHOD => {
|
VM_METHOD_TYPE_BMETHOD => {
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_bmethod);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
return gen_send_bmethod(jit, ctx, asm, ocb, ci, cme, block, flags, argc);
|
return gen_send_bmethod(jit, ctx, asm, ocb, ci, cme, block, flags, argc);
|
||||||
}
|
}
|
||||||
VM_METHOD_TYPE_ZSUPER => {
|
VM_METHOD_TYPE_ZSUPER => {
|
||||||
@ -5842,6 +5962,11 @@ fn gen_send_general(
|
|||||||
return CantCompile;
|
return CantCompile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_optimized);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
|
|
||||||
let opt_type = unsafe { get_cme_def_body_optimized_type(cme) };
|
let opt_type = unsafe { get_cme_def_body_optimized_type(cme) };
|
||||||
match opt_type {
|
match opt_type {
|
||||||
OPTIMIZED_METHOD_TYPE_SEND => {
|
OPTIMIZED_METHOD_TYPE_SEND => {
|
||||||
@ -6008,6 +6133,10 @@ fn gen_send_general(
|
|||||||
return CantCompile;
|
return CantCompile;
|
||||||
}
|
}
|
||||||
OPTIMIZED_METHOD_TYPE_STRUCT_AREF => {
|
OPTIMIZED_METHOD_TYPE_STRUCT_AREF => {
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_aref);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
return gen_struct_aref(
|
return gen_struct_aref(
|
||||||
jit,
|
jit,
|
||||||
ctx,
|
ctx,
|
||||||
@ -6022,6 +6151,10 @@ fn gen_send_general(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
OPTIMIZED_METHOD_TYPE_STRUCT_ASET => {
|
OPTIMIZED_METHOD_TYPE_STRUCT_ASET => {
|
||||||
|
if flags & VM_CALL_ARGS_SPLAT != 0 {
|
||||||
|
gen_counter_incr!(asm, send_args_splat_aset);
|
||||||
|
return CantCompile;
|
||||||
|
}
|
||||||
return gen_struct_aset(
|
return gen_struct_aset(
|
||||||
jit,
|
jit,
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -832,6 +832,17 @@ pub struct rb_call_data {
|
|||||||
pub ci: *const rb_callinfo,
|
pub ci: *const rb_callinfo,
|
||||||
pub cc: *const rb_callcache,
|
pub cc: *const rb_callcache,
|
||||||
}
|
}
|
||||||
|
pub const RHASH_PASS_AS_KEYWORDS: ruby_rhash_flags = 8192;
|
||||||
|
pub const RHASH_PROC_DEFAULT: ruby_rhash_flags = 16384;
|
||||||
|
pub const RHASH_ST_TABLE_FLAG: ruby_rhash_flags = 32768;
|
||||||
|
pub const RHASH_AR_TABLE_SIZE_MASK: ruby_rhash_flags = 983040;
|
||||||
|
pub const RHASH_AR_TABLE_SIZE_SHIFT: ruby_rhash_flags = 16;
|
||||||
|
pub const RHASH_AR_TABLE_BOUND_MASK: ruby_rhash_flags = 15728640;
|
||||||
|
pub const RHASH_AR_TABLE_BOUND_SHIFT: ruby_rhash_flags = 20;
|
||||||
|
pub const RHASH_TRANSIENT_FLAG: ruby_rhash_flags = 16777216;
|
||||||
|
pub const RHASH_LEV_SHIFT: ruby_rhash_flags = 25;
|
||||||
|
pub const RHASH_LEV_MAX: ruby_rhash_flags = 127;
|
||||||
|
pub type ruby_rhash_flags = u32;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct rb_builtin_function {
|
pub struct rb_builtin_function {
|
||||||
|
@ -188,6 +188,7 @@ make_counters! {
|
|||||||
send_cfunc_toomany_args,
|
send_cfunc_toomany_args,
|
||||||
send_cfunc_tracing,
|
send_cfunc_tracing,
|
||||||
send_cfunc_kwargs,
|
send_cfunc_kwargs,
|
||||||
|
send_cfunc_splat_with_kw,
|
||||||
send_attrset_kwargs,
|
send_attrset_kwargs,
|
||||||
send_iseq_tailcall,
|
send_iseq_tailcall,
|
||||||
send_iseq_arity_error,
|
send_iseq_arity_error,
|
||||||
@ -209,9 +210,18 @@ make_counters! {
|
|||||||
send_se_cf_overflow,
|
send_se_cf_overflow,
|
||||||
send_se_protected_check_failed,
|
send_se_protected_check_failed,
|
||||||
send_splatarray_length_not_equal,
|
send_splatarray_length_not_equal,
|
||||||
|
send_splatarray_last_ruby_2_keywords,
|
||||||
send_splat_not_array,
|
send_splat_not_array,
|
||||||
send_args_splat_non_iseq,
|
send_args_splat_non_iseq,
|
||||||
send_args_splat_cfunc,
|
send_args_splat_ivar,
|
||||||
|
send_args_splat_attrset,
|
||||||
|
send_args_splat_bmethod,
|
||||||
|
send_args_splat_aref,
|
||||||
|
send_args_splat_aset,
|
||||||
|
send_args_splat_optimized,
|
||||||
|
send_args_splat_cfunc_var_args,
|
||||||
|
send_args_splat_cfunc_zuper,
|
||||||
|
send_args_splat_cfunc_ruby2_keywords,
|
||||||
send_iseq_ruby2_keywords,
|
send_iseq_ruby2_keywords,
|
||||||
send_send_not_imm,
|
send_send_not_imm,
|
||||||
send_send_wrong_args,
|
send_send_wrong_args,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user