8000 YJIT: Optimize putobject+opt_ltlt for integers · ruby/ruby@a707b56 · GitHub
[go: up one dir, main page]

Skip to content

Commit a707b56

Browse files
committed
YJIT: Optimize putobject+opt_ltlt for integers
In `jit_rb_int_lshift()`, we guard against the right hand side changing since we want to avoid generating variable length shifts. When control reaches a putobject and `opt_ltlt` pair, though, we know that the right hand side never changes. This commit detects this situation and substitutes an implementation that does not guard against the right hand side changing, saving that work. Deleted some putobject Rust tests since they aren't that valuable and cause linking issues. Nice boost to `optcarrot` and `protoboeuf`: ``` ---------- ------------------ bench yjit-pre/yjit-post optcarrot 1.09 protoboeuf 1.12 ---------- ------------------ ```
1 parent 8780059 commit a707b56

File tree

2 files changed

+96
-56
lines changed

2 files changed

+96
-56
lines changed

bootstraptest/test_yjit.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4769,3 +4769,20 @@ def tests
47694769
47704770
tests
47714771
}
4772+
4773+
# test integer left shift with constant rhs
4774+
assert_equal [0x80000000000, 'a+', :ok].inspect, %q{
4775+
def shift(val) = val << 43
4776+
4777+
def tests
4778+
int = shift(1)
4779+
str = shift("a")
4780+
4781+
Integer.define_method(:<<) { |_| :ok }
4782+
redef = shift(1)
4783+
4784+
[int, str, redef]
4785+
end
4786+
4787+
tests
4788+
}

yjit/src/codegen.rs

Lines changed: 79 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub use crate::virtualmem::CodePtr;
3030
/// Status returned by code generation functions
3131
#[derive(PartialEq, Debug)]
3232
enum CodegenStatus {
33+
SkipNextInsn,
3334
KeepCompiling,
3435
EndBlock,
3536
}
@@ -1197,6 +1198,13 @@ pub fn gen_single_block(
11971198
// Move to the next instruction to compile
11981199
insn_idx += insn_len(opcode) as u16;
11991200

1201+
// Move past next instruction when instructed
1202+
if status == Some(SkipNextInsn) {
1203+
let next_pc = unsafe { rb_iseq_pc_at_idx(iseq, insn_idx.into()) };
1204+
let next_opcode: usize = unsafe { rb_iseq_opcode_at_pc(iseq, next_pc) }.try_into().unwrap();
1205+
insn_idx += insn_len(next_opcode) as u16;
1206+
}
1207+
12001208
// If the instruction terminates this block
12011209
if status == Some(EndBlock) {
12021210
break;
@@ -1343,30 +1351,92 @@ fn jit_putobject(asm: &mut Assembler, arg: VALUE) {
13431351
fn gen_putobject_int2fix(
13441352
jit: &mut JITState,
13451353
asm: &mut Assembler,
1346-
_ocb: &mut OutlinedCb,
1354+
ocb: &mut OutlinedCb,
13471355
) -> Option<CodegenStatus> {
13481356
let opcode = jit.opcode;
13491357
let cst_val: usize = if opcode == YARVINSN_putobject_INT2FIX_0_.as_usize() {
13501358
0
13511359
} else {
13521360
1
13531361
};
1362+
let cst_val = VALUE::fixnum_from_usize(cst_val);
1363+
1364+
if let Some(result) = fused_putobject_opt_ltlt(jit, asm, cst_val, ocb) {
1365+
return Some(result);
1366+
}
13541367

1355-
jit_putobject(asm, VALUE::fixnum_from_usize(cst_val));
1368+
jit_putobject(asm, cst_val);
13561369
Some(KeepCompiling)
13571370
}
13581371

13591372
fn gen_putobject(
13601373
jit: &mut JITState,
13611374
asm: &mut Assembler,
1362-
_ocb: &mut OutlinedCb,
1375+
ocb: &mut OutlinedCb,
13631376
) -> Option<CodegenStatus> {
13641377
let arg: VALUE = jit.get_arg(0);
13651378

1379+
if let Some(result) = fused_putobject_opt_ltlt(jit, asm, arg, ocb) {
1380+
return Some(result);
1381+
}
1382+
13661383
jit_putobject(asm, arg);
13671384
Some(KeepCompiling)
13681385
}
13691386

1387+
fn fused_putobject_opt_ltlt(
1388+
jit: &mut JITState,
1389+
asm: &mut Assembler,
1390+
constant_object: VALUE,
1391+
ocb: &mut OutlinedCb,
1392+
) -> Option<CodegenStatus> {
1393+
let next_opcode = unsafe { rb_vm_insn_addr2opcode(jit.pc.add(insn_len(jit.opcode).as_usize()).read().as_ptr()) };
1394+
if next_opcode == YARVINSN_opt_ltlt as i32 && constant_object.fixnum_p() {
1395+
// Untag the fixnum shift amount
1396+
let shift_amt = constant_object.as_isize() >> 1;
1397+
if shift_amt > 63 || shift_amt < 0 {
1398+
return None;
1399+
}
1400+
if !jit.at_current_insn() {
1401+
defer_compilation(jit, asm, ocb);
1402+
return Some(EndBlock);
1403+
}
1404+
1405+
let lhs = jit.peek_at_stack(&asm.ctx, 0);
1406+
if !lhs.fixnum_p() {
1407+
return None;
1408+
}
1409+
1410+
if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_LTLT) {
1411+
return None;
1412+
}
1413+
1414+
asm_comment!(asm, "integer left shift with rhs={shift_amt}");
1415+
let lhs = asm.stack_opnd(0);
1416+
1417+
// Guard that lhs is a fixnum if necessary
1418+
let lhs_type = asm.ctx.get_opnd_type(lhs.into());
1419+
if lhs_type != Type::Fixnum {
1420+
asm_comment!(asm, "guard arg0 fixnum");
1421+
asm.test(lhs, Opnd::UImm(RUBY_FIXNUM_FLAG as u64));
1422+
1423+
jit_chain_guard(
1424+
JCC_JZ,
1425+
jit,
1426+
asm,
1427+
ocb,
1428+
SEND_MAX_DEPTH,
1429+
Counter::guard_send_not_fixnums,
1430+
);
1431+
}
1432+
1433+
asm.stack_pop(1);
1434+
fixnum_left_shift_body(asm, lhs, shift_amt as u64);
1435+
return Some(SkipNextInsn);
1436+
}
1437+
return None;
1438+
}
1439+
13701440
fn gen_putself(
13711441
_jit: &mut JITState,
13721442
asm: &mut Assembler,
@@ -5073,8 +5143,13 @@ fn jit_rb_int_lshift(
50735143
Counter::lshift_amount_changed,
50745144
);
50755145

5146+
fixnum_left_shift_body(asm, lhs, shift_amt as u64);
5147+
true
5148+
}
5149+
5150+
fn fixnum_left_shift_body(asm: &mut Assembler, lhs: Opnd, shift_amt: u64) {
50765151
let in_val = asm.sub(lhs, 1.into());
5077-
let shift_opnd = Opnd::UImm(shift_amt as u64);
5152+
let shift_opnd = Opnd::UImm(shift_amt);
50785153
let out_val = asm.lshift(in_val, shift_opnd);
50795154
let unshifted = asm.rshift(out_val, shift_opnd);
50805155

@@ -5087,7 +5162,6 @@ fn jit_rb_int_lshift(
50875162

50885163
let ret_opnd = asm.stack_push(Type::Fixnum);
50895164
asm.mov(ret_opnd, out_val);
5090-
true
50915165
}
50925166

50935167
fn jit_rb_int_rshift(
@@ -10384,57 +10458,6 @@ mod tests {
1038410458
assert!(cb.get_write_pos() > 0);
1038510459
}
1038610460

10387-
#[test]
10388-
fn test_putobject_qtrue() {
10389-
// Test gen_putobject with Qtrue
10390-
let (mut jit, _context, mut asm, mut cb, mut ocb) = setup_codegen();
10391-
10392-
let mut value_array: [u64; 2] = [0, Qtrue.into()];
10393-
let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
10394-
jit.pc = pc;
10395-
10396-
let status = gen_putobject(&mut jit, &mut asm, &mut ocb);
10397-
10398-
let tmp_type_top = asm.ctx.get_opnd_type(StackOpnd(0));
10399-
10400-
assert_eq!(status, Some(KeepCompiling));
10401-
assert_eq!(tmp_type_top, Type::True);
10402-
asm.compile(&mut cb, None).unwrap();
10403-
assert!(cb.get_write_pos() > 0);
10404-
}
10405-
10406-
#[test]
10407-
fn test_putobject_fixnum() {
10408-
// Test gen_putobject with a Fixnum to test another conditional branch
10409-
let (mut jit, _context, mut asm, mut cb, mut ocb) = setup_codegen();
10410-
10411-
// The Fixnum 7 is encoded as 7 * 2 + 1, or 15
10412-
let mut value_array: [u64; 2] = [0, 15];
10413-
let pc: *mut VALUE = &mut value_array as *mut u64 as *mut VALUE;
10414-
jit.pc = pc;
10415-
10416-
let status = gen_putobject(&mut jit, &mut asm, &mut ocb);
10417-
10418-
let tmp_type_top = asm.ctx.get_opnd_type(StackOpnd(0));
10419-
10420-
assert_eq!(status, Some(KeepCompiling));
10421-
assert_eq!(tmp_type_top, Type::Fixnum);
10422-
asm.compile(&mut cb, None).unwrap();
10423-
assert!(cb.get_write_pos() > 0);
10424-
}
10425-
10426-
#[test]
10427-
fn test_int2fix() {
10428-
let (mut jit, _context, mut asm, _cb, mut ocb) = setup_codegen();
10429-
jit.opcode = YARVINSN_putobject_INT2FIX_0_.as_usize();
10430-
let status = gen_putobject_int2fix(&mut jit, &mut asm, &mut ocb);
10431-
10432-
let tmp_type_top = asm.ctx.get_opnd_type(StackOpnd(0));
10433-
10434-
// Right now we're not testing the generated machine code to make sure a literal 1 or 0 was pushed. I've checked locally.
10435-
assert_eq!(status, Some(KeepCompiling));
10436-
assert_eq!(tmp_type_top, Type::Fixnum);
10437-
}
1043810461

1043910462
#[test]
1044010463
fn test_putself() {

0 commit comments

Comments
 (0)
0