Skip to content

Commit 5444dde

Browse files
authored
YJIT: Skip type checks on splat args and expandarray if possible (#7363)
YJIT: Skip type checks on splat args and expandarray if possible
1 parent c3cd191 commit 5444dde

File tree

1 file changed

+46
-48
lines changed

1 file changed

+46
-48
lines changed

yjit/src/codegen.rs

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,39 +1292,63 @@ fn gen_newrange(
12921292
}
12931293

12941294
fn guard_object_is_heap(
1295+
ctx: &mut Context,
12951296
asm: &mut Assembler,
1296-
object_opnd: Opnd,
1297+
object: Opnd,
1298+
object_opnd: YARVOpnd,
12971299
side_exit: Target,
12981300
) {
1301+
let object_type = ctx.get_opnd_type(object_opnd);
1302+
if object_type.is_heap() {
1303+
return;
1304+
}
1305+
12991306
asm.comment("guard object is heap");
13001307

13011308
// Test that the object is not an immediate
1302-
asm.test(object_opnd, (RUBY_IMMEDIATE_MASK as u64).into());
1309+
asm.test(object, (RUBY_IMMEDIATE_MASK as u64).into());
13031310
asm.jnz(side_exit);
13041311

13051312
// Test that the object is not false
1306-
asm.cmp(object_opnd, Qfalse.into());
1313+
asm.cmp(object, Qfalse.into());
13071314
asm.je(side_exit);
1315+
1316+
if object_type.diff(Type::UnknownHeap) != usize::MAX {
1317+
ctx.upgrade_opnd_type(object_opnd, Type::UnknownHeap);
1318+
}
13081319
}
13091320

13101321
fn guard_object_is_array(
1322+
ctx: &mut Context,
13111323
asm: &mut Assembler,
1312-
object_opnd: Opnd,
1324+
object: Opnd,
1325+
object_opnd: YARVOpnd,
13131326
side_exit: Target,
13141327
) {
1328+
let object_type = ctx.get_opnd_type(object_opnd);
1329+
if object_type.is_array() {
1330+
return;
1331+
}
1332+
1333+
let object_reg = match object {
1334+
Opnd::Reg(_) => object,
1335+
_ => asm.load(object),
1336+
};
1337+
guard_object_is_heap(ctx, asm, object_reg, object_opnd, side_exit);
1338+
13151339
asm.comment("guard object is array");
13161340

13171341
// Pull out the type mask
1318-
let object_reg = match object_opnd {
1319-
Opnd::Reg(_) => object_opnd,
1320-
_ => asm.load(object_opnd),
1321-
};
13221342
let flags_opnd = Opnd::mem(VALUE_BITS, object_reg, RUBY_OFFSET_RBASIC_FLAGS);
13231343
let flags_opnd = asm.and(flags_opnd, (RUBY_T_MASK as u64).into());
13241344

13251345
// Compare the result with T_ARRAY
13261346
asm.cmp(flags_opnd, (RUBY_T_ARRAY as u64).into());
13271347
asm.jne(side_exit);
1348+
1349+
if object_type.diff(Type::TArray) != usize::MAX {
1350+
ctx.upgrade_opnd_type(object_opnd, Type::TArray);
1351+
}
13281352
}
13291353

13301354
/// This guards that a special flag is not set on a hash.
@@ -1402,12 +1426,12 @@ fn gen_expandarray(
14021426

14031427
let side_exit = get_side_exit(jit, ocb, ctx);
14041428

1405-
let array_type = ctx.get_opnd_type(StackOpnd(0));
1406-
let array_opnd = ctx.stack_pop(1);
1429+
let array_opnd = ctx.stack_opnd(0);
14071430

14081431
// num is the number of requested values. If there aren't enough in the
14091432
// array then we're going to push on nils.
1410-
if matches!(array_type, Type::Nil) {
1433+
if ctx.get_opnd_type(array_opnd.into()) == Type::Nil {
1434+
ctx.stack_pop(1); // pop after using the type info
14111435
// special case for a, b = nil pattern
14121436
// push N nils onto the stack
14131437
for _ in 0..num {
@@ -1418,23 +1442,21 @@ fn gen_expandarray(
14181442
}
14191443

14201444
// Move the array from the stack and check that it's an array.
1421-
let array_reg = asm.load(array_opnd);
1422-
guard_object_is_heap(
1423-
asm,
1424-
array_reg,
1425-
counted_exit!(ocb, side_exit, expandarray_not_array),
1426-
);
14271445
guard_object_is_array(
1446+
ctx,
14281447
asm,
1429-
array_reg,
1448+
array_opnd,
1449+
array_opnd.into(),
14301450
counted_exit!(ocb, side_exit, expandarray_not_array),
14311451
);
1452+
let array_opnd = ctx.stack_pop(1); // pop after using the type info
14321453

14331454
// If we don't actually want any values, then just return.
14341455
if num == 0 {
14351456
return KeepCompiling;
14361457
}
14371458

1459+
let array_reg = asm.load(array_opnd);
14381460
let array_len_opnd = get_array_len(asm, array_reg);
14391461

14401462
// Only handle the case where the number of values in the array is greater
@@ -1989,24 +2011,14 @@ fn gen_get_ivar(
19892011
}
19902012
};
19912013

1992-
// must be before stack_pop
1993-
let recv_type = ctx.get_opnd_type(recv_opnd);
1994-
1995-
// Upgrade type
1996-
if !recv_type.is_heap() {
1997-
ctx.upgrade_opnd_type(recv_opnd, Type::UnknownHeap);
1998-
}
2014+
// Guard heap object (recv_opnd must be used before stack_oop)
2015+
guard_object_is_heap(ctx, asm, recv, recv_opnd, side_exit);
19992016

20002017
// Pop receiver if it's on the temp stack
20012018
if recv_opnd != SelfOpnd {
20022019
ctx.stack_pop(1);
20032020
}
20042021

2005-
// Guard heap object
2006-
if !recv_type.is_heap() {
2007-
guard_object_is_heap(asm, recv, side_exit);
2008-
}
2009-
20102022
// Compile time self is embedded and the ivar index lands within the object
20112023
let embed_test_result = unsafe { FL_TEST_RAW(comptime_receiver, VALUE(ROBJECT_EMBED.as_usize())) != VALUE(0) };
20122024

@@ -2221,16 +2233,12 @@ fn gen_setinstancevariable(
22212233
let mut recv = asm.load(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SELF));
22222234

22232235
let recv_opnd = SelfOpnd;
2224-
let recv_type = ctx.get_opnd_type(recv_opnd);
22252236

22262237
// Generate a side exit
22272238
let side_exit = get_side_exit(jit, ocb, ctx);
22282239

22292240
// Upgrade type
2230-
if !recv_type.is_heap() { // Must be a heap type
2231-
ctx.upgrade_opnd_type(recv_opnd, Type::UnknownHeap);
2232-
guard_object_is_heap(asm, recv, side_exit);
2233-
}
2241+
guard_object_is_heap(ctx, asm, recv, recv_opnd, side_exit);
22342242

22352243
let expected_shape = unsafe { rb_shape_get_shape_id(comptime_receiver) };
22362244
let shape_id_offset = unsafe { rb_shape_id_offset() };
@@ -5106,20 +5114,16 @@ fn get_array_ptr(asm: &mut Assembler, array_reg: Opnd) -> Opnd {
51065114
/// It optimistically compiles to a static size that is the exact number of arguments
51075115
/// needed for the function.
51085116
fn push_splat_args(required_args: u32, ctx: &mut Context, asm: &mut Assembler, ocb: &mut OutlinedCb, side_exit: Target) {
5109-
51105117
asm.comment("push_splat_args");
51115118

51125119
let array_opnd = ctx.stack_opnd(0);
51135120
let array_reg = asm.load(array_opnd);
51145121

5115-
guard_object_is_heap(
5116-
asm,
5117-
array_reg,
5118-
counted_exit!(ocb, side_exit, send_splat_not_array),
5119-
);
51205122
guard_object_is_array(
5123+
ctx,
51215124
asm,
51225125
array_reg,
5126+
array_opnd.into(),
51235127
counted_exit!(ocb, side_exit, send_splat_not_array),
51245128
);
51255129

@@ -5791,16 +5795,10 @@ fn gen_send_iseq(
57915795
// Note that you can't have side exits after this arg0 splat.
57925796
if block_arg0_splat {
57935797
let arg0_opnd = ctx.stack_opnd(0);
5794-
let arg0_type = ctx.get_opnd_type(arg0_opnd.into());
57955798

57965799
// Only handle the case that you don't need to_ary conversion
57975800
let not_array_exit = counted_exit!(ocb, side_exit, invokeblock_iseq_arg0_not_array);
5798-
if !arg0_type.is_heap() {
5799-
guard_object_is_heap(asm, arg0_opnd, not_array_exit);
5800-
}
5801-
if !arg0_type.is_array() {
5802-
guard_object_is_array(asm, arg0_opnd, not_array_exit);
5803-
}
5801+
guard_object_is_array(ctx, asm, arg0_opnd, arg0_opnd.into(), not_array_exit);
58045802

58055803
// Only handle the same that the array length == ISEQ's lead_num (most common)
58065804
let arg0_len_opnd = get_array_len(asm, arg0_opnd);

0 commit comments

Comments
 (0)