YJIT: Skip defer_compilation for fixnums if possible (#7168)

* YJIT: Skip defer_compilation for fixnums if possible

* YJIT: It should be Some(false)

* YJIT: Define two_fixnums_on_stack on Context
This commit is contained in:
Takashi Kokubun 2023-01-30 10:55:00 -08:00 committed by GitHub
parent e1ffafb285
commit bc0dc9d40e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
Notes: git 2023-01-30 18:55:21 +00:00
Merged-By: maximecb <maximecb@ruby-lang.org>
2 changed files with 104 additions and 77 deletions

View File

@ -129,14 +129,14 @@ fn jit_next_insn_idx(jit: &JITState) -> u32 {
// Check if we are compiling the instruction at the stub PC // Check if we are compiling the instruction at the stub PC
// Meaning we are compiling the instruction that is next to execute // Meaning we are compiling the instruction that is next to execute
fn jit_at_current_insn(jit: &JITState) -> bool { pub fn jit_at_current_insn(jit: &JITState) -> bool {
let ec_pc: *mut VALUE = unsafe { get_cfp_pc(get_ec_cfp(jit.ec.unwrap())) }; let ec_pc: *mut VALUE = unsafe { get_cfp_pc(get_ec_cfp(jit.ec.unwrap())) };
ec_pc == jit.pc ec_pc == jit.pc
} }
// Peek at the nth topmost value on the Ruby stack. // Peek at the nth topmost value on the Ruby stack.
// Returns the topmost value when n == 0. // Returns the topmost value when n == 0.
fn jit_peek_at_stack(jit: &JITState, ctx: &Context, n: isize) -> VALUE { pub fn jit_peek_at_stack(jit: &JITState, ctx: &Context, n: isize) -> VALUE {
assert!(jit_at_current_insn(jit)); assert!(jit_at_current_insn(jit));
assert!(n < ctx.get_stack_size() as isize); assert!(n < ctx.get_stack_size() as isize);
@ -1093,15 +1093,15 @@ fn gen_opt_plus(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
if !jit_at_current_insn(jit) { let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
defer_compilation(jit, ctx, asm, ocb); Some(two_fixnums) => two_fixnums,
return EndBlock; None => {
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);
@ -2622,16 +2622,16 @@ fn gen_fixnum_cmp(
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
cmov_op: CmovFn, cmov_op: CmovFn,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize base on a runtime receiver let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
if !jit_at_current_insn(jit) { Some(two_fixnums) => two_fixnums,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize based on a runtime receiver
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);
@ -2698,24 +2698,29 @@ fn gen_opt_gt(
} }
// Implements specialized equality for either two fixnum or two strings // Implements specialized equality for either two fixnum or two strings
// Returns true if code was generated, otherwise false // Returns None if enough type information isn't available, Some(true)
// if code was generated, otherwise Some(false).
fn gen_equality_specialized( fn gen_equality_specialized(
jit: &mut JITState, jit: &mut JITState,
ctx: &mut Context, ctx: &mut Context,
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
side_exit: Target, ) -> Option<bool> {
) -> bool { // Create a side-exit to fall back to the interpreter
let comptime_a = jit_peek_at_stack(jit, ctx, 1); let side_exit = get_side_exit(jit, ocb, ctx);
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
let a_opnd = ctx.stack_opnd(1); let a_opnd = ctx.stack_opnd(1);
let b_opnd = ctx.stack_opnd(0); let b_opnd = ctx.stack_opnd(0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() { let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
Some(two_fixnums) => two_fixnums,
None => return None,
};
if two_fixnums {
if !assume_bop_not_redefined(jit, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_EQ) { if !assume_bop_not_redefined(jit, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_EQ) {
// if overridden, emit the generic version // if overridden, emit the generic version
return false; return Some(false);
} }
guard_two_fixnums(jit, ctx, asm, ocb, side_exit); guard_two_fixnums(jit, ctx, asm, ocb, side_exit);
@ -2729,13 +2734,19 @@ fn gen_equality_specialized(
let dst = ctx.stack_push(Type::UnknownImm); let dst = ctx.stack_push(Type::UnknownImm);
asm.mov(dst, val); asm.mov(dst, val);
true return Some(true);
} }
else if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString }
{ if !jit_at_current_insn(jit) {
return None;
}
let comptime_a = jit_peek_at_stack(jit, ctx, 1);
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if unsafe { comptime_a.class_of() == rb_cString && comptime_b.class_of() == rb_cString } {
if !assume_bop_not_redefined(jit, ocb, STRING_REDEFINED_OP_FLAG, BOP_EQ) { if !assume_bop_not_redefined(jit, ocb, STRING_REDEFINED_OP_FLAG, BOP_EQ) {
// if overridden, emit the generic version // if overridden, emit the generic version
return false; return Some(false);
} }
// Guard that a is a String // Guard that a is a String
@ -2792,9 +2803,9 @@ fn gen_equality_specialized(
asm.write_label(ret); asm.write_label(ret);
true Some(true)
} else { } else {
false Some(false)
} }
} }
@ -2804,16 +2815,16 @@ fn gen_opt_eq(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize base on a runtime receiver let specialized = match gen_equality_specialized(jit, ctx, asm, ocb) {
if !jit_at_current_insn(jit) { Some(specialized) => specialized,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize base on a runtime receiver
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
// Create a side-exit to fall back to the interpreter if specialized {
let side_exit = get_side_exit(jit, ocb, ctx);
if gen_equality_specialized(jit, ctx, asm, ocb, side_exit) {
jump_to_next_insn(jit, ctx, asm, ocb); jump_to_next_insn(jit, ctx, asm, ocb);
EndBlock EndBlock
} else { } else {
@ -3068,16 +3079,16 @@ fn gen_opt_and(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize on a runtime `self` let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
if !jit_at_current_insn(jit) { Some(two_fixnums) => two_fixnums,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize on a runtime `self`
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);
@ -3113,16 +3124,16 @@ fn gen_opt_or(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize on a runtime `self` let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
if !jit_at_current_insn(jit) { Some(two_fixnums) => two_fixnums,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize on a runtime `self`
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);
@ -3158,16 +3169,16 @@ fn gen_opt_minus(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize on a runtime `self` let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
if !jit_at_current_insn(jit) { Some(two_fixnums) => two_fixnums,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize on a runtime `self`
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);
@ -3225,16 +3236,16 @@ fn gen_opt_mod(
asm: &mut Assembler, asm: &mut Assembler,
ocb: &mut OutlinedCb, ocb: &mut OutlinedCb,
) -> CodegenStatus { ) -> CodegenStatus {
// Defer compilation so we can specialize on a runtime `self` let two_fixnums = match ctx.two_fixnums_on_stack(jit) {
if !jit_at_current_insn(jit) { Some(two_fixnums) => two_fixnums,
defer_compilation(jit, ctx, asm, ocb); None => {
return EndBlock; // Defer compilation so we can specialize on a runtime `self`
} defer_compilation(jit, ctx, asm, ocb);
return EndBlock;
}
};
let comptime_a = jit_peek_at_stack(jit, ctx, 1); if two_fixnums {
let comptime_b = jit_peek_at_stack(jit, ctx, 0);
if comptime_a.fixnum_p() && comptime_b.fixnum_p() {
// Create a side-exit to fall back to the interpreter // Create a side-exit to fall back to the interpreter
// Note: we generate the side-exit before popping operands from the stack // Note: we generate the side-exit before popping operands from the stack
let side_exit = get_side_exit(jit, ocb, ctx); let side_exit = get_side_exit(jit, ocb, ctx);

View File

@ -1474,6 +1474,22 @@ impl Context {
return diff; return diff;
} }
pub fn two_fixnums_on_stack(&self, jit: &mut JITState) -> Option<bool> {
if jit_at_current_insn(jit) {
let comptime_recv = jit_peek_at_stack(jit, self, 1);
let comptime_arg = jit_peek_at_stack(jit, self, 0);
return Some(comptime_recv.fixnum_p() && comptime_arg.fixnum_p());
}
let recv_type = self.get_opnd_type(StackOpnd(1));
let arg_type = self.get_opnd_type(StackOpnd(0));
match (recv_type, arg_type) {
(Type::Fixnum, Type::Fixnum) => Some(true),
(Type::Unknown | Type::UnknownImm, Type::Unknown | Type::UnknownImm) => None,
_ => Some(false),
}
}
} }
impl BlockId { impl BlockId {