YJIT: Chain-guard opt_mult overflow (#8554)
* YJIT: Chain-guard opt_mult overflow * YJIT: Support regenerating Jo after Mul
This commit is contained in:
parent
f9f728e804
commit
0b67e3fd3e
@ -4181,3 +4181,6 @@ assert_equal '[6, -6, 9671406556917033397649408, -9671406556917033397649408, 212
|
|||||||
|
|
||||||
[r1, r2, r3, r4, r5]
|
[r1, r2, r3, r4, r5]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Integer multiplication and overflow (minimized regression test from test-basic)
|
||||||
|
assert_equal '8515157028618240000', %q{2128789257154560000 * 4}
|
||||||
|
@ -1347,6 +1347,10 @@ class TestYJIT < Test::Unit::TestCase
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_opt_mult_overflow
|
||||||
|
assert_no_exits('0xfff_ffff_ffff_ffff * 0x10')
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def code_gc_helpers
|
def code_gc_helpers
|
||||||
|
@ -824,7 +824,6 @@ impl Assembler
|
|||||||
let start_write_pos = cb.get_write_pos();
|
let start_write_pos = cb.get_write_pos();
|
||||||
let mut insn_idx: usize = 0;
|
let mut insn_idx: usize = 0;
|
||||||
while let Some(insn) = self.insns.get(insn_idx) {
|
while let Some(insn) = self.insns.get(insn_idx) {
|
||||||
let mut next_insn_idx = insn_idx + 1;
|
|
||||||
let src_ptr = cb.get_write_ptr();
|
let src_ptr = cb.get_write_ptr();
|
||||||
let had_dropped_bytes = cb.has_dropped_bytes();
|
let had_dropped_bytes = cb.has_dropped_bytes();
|
||||||
let old_label_state = cb.get_label_state();
|
let old_label_state = cb.get_label_state();
|
||||||
@ -878,8 +877,9 @@ impl Assembler
|
|||||||
},
|
},
|
||||||
Insn::Mul { left, right, out } => {
|
Insn::Mul { left, right, out } => {
|
||||||
// If the next instruction is jo (jump on overflow)
|
// If the next instruction is jo (jump on overflow)
|
||||||
match self.insns.get(insn_idx + 1) {
|
match (self.insns.get(insn_idx + 1), self.insns.get(insn_idx + 2)) {
|
||||||
Some(Insn::Jo(target)) => {
|
(Some(Insn::JoMul(target)), _) |
|
||||||
|
(Some(Insn::PosMarker(_)), Some(Insn::JoMul(target))) => {
|
||||||
// Compute the high 64 bits
|
// Compute the high 64 bits
|
||||||
smulh(cb, Self::SCRATCH0, left.into(), right.into());
|
smulh(cb, Self::SCRATCH0, left.into(), right.into());
|
||||||
|
|
||||||
@ -895,9 +895,7 @@ impl Assembler
|
|||||||
// If the high 64-bits are not all zeros or all ones,
|
// If the high 64-bits are not all zeros or all ones,
|
||||||
// matching the sign bit, then we have an overflow
|
// matching the sign bit, then we have an overflow
|
||||||
cmp(cb, Self::SCRATCH0, Self::SCRATCH1);
|
cmp(cb, Self::SCRATCH0, Self::SCRATCH1);
|
||||||
emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb));
|
// Insn::JoMul will emit_conditional_jump::<{Condition::NE}>
|
||||||
|
|
||||||
next_insn_idx += 1;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
mul(cb, out.into(), left.into(), right.into());
|
mul(cb, out.into(), left.into(), right.into());
|
||||||
@ -1120,7 +1118,7 @@ impl Assembler
|
|||||||
Insn::Je(target) | Insn::Jz(target) => {
|
Insn::Je(target) | Insn::Jz(target) => {
|
||||||
emit_conditional_jump::<{Condition::EQ}>(cb, compile_side_exit(*target, self, ocb));
|
emit_conditional_jump::<{Condition::EQ}>(cb, compile_side_exit(*target, self, ocb));
|
||||||
},
|
},
|
||||||
Insn::Jne(target) | Insn::Jnz(target) => {
|
Insn::Jne(target) | Insn::Jnz(target) | Insn::JoMul(target) => {
|
||||||
emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb));
|
emit_conditional_jump::<{Condition::NE}>(cb, compile_side_exit(*target, self, ocb));
|
||||||
},
|
},
|
||||||
Insn::Jl(target) => {
|
Insn::Jl(target) => {
|
||||||
@ -1197,7 +1195,7 @@ impl Assembler
|
|||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
insn_idx = next_insn_idx;
|
insn_idx += 1;
|
||||||
gc_offsets.append(&mut insn_gc_offsets);
|
gc_offsets.append(&mut insn_gc_offsets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -447,6 +447,9 @@ pub enum Insn {
|
|||||||
/// Jump if overflow
|
/// Jump if overflow
|
||||||
Jo(Target),
|
Jo(Target),
|
||||||
|
|
||||||
|
/// Jump if overflow in multiplication
|
||||||
|
JoMul(Target),
|
||||||
|
|
||||||
/// Jump if zero
|
/// Jump if zero
|
||||||
Jz(Target),
|
Jz(Target),
|
||||||
|
|
||||||
@ -590,6 +593,7 @@ impl Insn {
|
|||||||
Insn::Jne(_) => "Jne",
|
Insn::Jne(_) => "Jne",
|
||||||
Insn::Jnz(_) => "Jnz",
|
Insn::Jnz(_) => "Jnz",
|
||||||
Insn::Jo(_) => "Jo",
|
Insn::Jo(_) => "Jo",
|
||||||
|
Insn::JoMul(_) => "JoMul",
|
||||||
Insn::Jz(_) => "Jz",
|
Insn::Jz(_) => "Jz",
|
||||||
Insn::Label(_) => "Label",
|
Insn::Label(_) => "Label",
|
||||||
Insn::LeaLabel { .. } => "LeaLabel",
|
Insn::LeaLabel { .. } => "LeaLabel",
|
||||||
@ -743,6 +747,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
|
|||||||
Insn::Jne(_) |
|
Insn::Jne(_) |
|
||||||
Insn::Jnz(_) |
|
Insn::Jnz(_) |
|
||||||
Insn::Jo(_) |
|
Insn::Jo(_) |
|
||||||
|
Insn::JoMul(_) |
|
||||||
Insn::Jz(_) |
|
Insn::Jz(_) |
|
||||||
Insn::Label(_) |
|
Insn::Label(_) |
|
||||||
Insn::LeaLabel { .. } |
|
Insn::LeaLabel { .. } |
|
||||||
@ -843,6 +848,7 @@ impl<'a> InsnOpndMutIterator<'a> {
|
|||||||
Insn::Jne(_) |
|
Insn::Jne(_) |
|
||||||
Insn::Jnz(_) |
|
Insn::Jnz(_) |
|
||||||
Insn::Jo(_) |
|
Insn::Jo(_) |
|
||||||
|
Insn::JoMul(_) |
|
||||||
Insn::Jz(_) |
|
Insn::Jz(_) |
|
||||||
Insn::Label(_) |
|
Insn::Label(_) |
|
||||||
Insn::LeaLabel { .. } |
|
Insn::LeaLabel { .. } |
|
||||||
@ -1861,6 +1867,10 @@ impl Assembler {
|
|||||||
self.push_insn(Insn::Jo(target));
|
self.push_insn(Insn::Jo(target));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jo_mul(&mut self, target: Target) {
|
||||||
|
self.push_insn(Insn::JoMul(target));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jz(&mut self, target: Target) {
|
pub fn jz(&mut self, target: Target) {
|
||||||
self.push_insn(Insn::Jz(target));
|
self.push_insn(Insn::Jz(target));
|
||||||
}
|
}
|
||||||
|
@ -740,7 +740,8 @@ impl Assembler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Insn::Jo(target) => {
|
Insn::Jo(target) |
|
||||||
|
Insn::JoMul(target) => {
|
||||||
match compile_side_exit(*target, self, ocb) {
|
match compile_side_exit(*target, self, ocb) {
|
||||||
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr),
|
Target::CodePtr(code_ptr) | Target::SideExitPtr(code_ptr) => jo_ptr(cb, code_ptr),
|
||||||
Target::Label(label_idx) => jo_label(cb, label_idx),
|
Target::Label(label_idx) => jo_label(cb, label_idx),
|
||||||
|
@ -245,6 +245,7 @@ pub enum JCCKinds {
|
|||||||
JCC_JBE,
|
JCC_JBE,
|
||||||
JCC_JNA,
|
JCC_JNA,
|
||||||
JCC_JNAE,
|
JCC_JNAE,
|
||||||
|
JCC_JO_MUL,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -2025,6 +2026,7 @@ fn jit_chain_guard(
|
|||||||
JCC_JZ | JCC_JE => BranchGenFn::JZToTarget0,
|
JCC_JZ | JCC_JE => BranchGenFn::JZToTarget0,
|
||||||
JCC_JBE | JCC_JNA => BranchGenFn::JBEToTarget0,
|
JCC_JBE | JCC_JNA => BranchGenFn::JBEToTarget0,
|
||||||
JCC_JB | JCC_JNAE => BranchGenFn::JBToTarget0,
|
JCC_JB | JCC_JNAE => BranchGenFn::JBToTarget0,
|
||||||
|
JCC_JO_MUL => BranchGenFn::JOMulToTarget0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (asm.ctx.get_chain_depth() as i32) < depth_limit {
|
if (asm.ctx.get_chain_depth() as i32) < depth_limit {
|
||||||
@ -3415,7 +3417,8 @@ fn gen_opt_mult(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if two_fixnums {
|
// Fallback to a method call if it overflows
|
||||||
|
if two_fixnums && asm.ctx.get_chain_depth() == 0 {
|
||||||
if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_MULT) {
|
if !assume_bop_not_redefined(jit, asm, ocb, INTEGER_REDEFINED_OP_FLAG, BOP_MULT) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -3432,7 +3435,7 @@ fn gen_opt_mult(
|
|||||||
let arg0_untag = asm.rshift(arg0, Opnd::UImm(1));
|
let arg0_untag = asm.rshift(arg0, Opnd::UImm(1));
|
||||||
let arg1_untag = asm.sub(arg1, Opnd::UImm(1));
|
let arg1_untag = asm.sub(arg1, Opnd::UImm(1));
|
||||||
let out_val = asm.mul(arg0_untag, arg1_untag);
|
let out_val = asm.mul(arg0_untag, arg1_untag);
|
||||||
asm.jo(Target::side_exit(Counter::opt_mult_overflow));
|
jit_chain_guard(JCC_JO_MUL, jit, asm, ocb, 1, Counter::opt_mult_overflow);
|
||||||
let out_val = asm.add(out_val, Opnd::UImm(1));
|
let out_val = asm.add(out_val, Opnd::UImm(1));
|
||||||
|
|
||||||
// Push the output on the stack
|
// Push the output on the stack
|
||||||
|
@ -493,6 +493,7 @@ pub enum BranchGenFn {
|
|||||||
JZToTarget0,
|
JZToTarget0,
|
||||||
JBEToTarget0,
|
JBEToTarget0,
|
||||||
JBToTarget0,
|
JBToTarget0,
|
||||||
|
JOMulToTarget0,
|
||||||
JITReturn,
|
JITReturn,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -549,6 +550,9 @@ impl BranchGenFn {
|
|||||||
BranchGenFn::JBToTarget0 => {
|
BranchGenFn::JBToTarget0 => {
|
||||||
asm.jb(target0)
|
asm.jb(target0)
|
||||||
}
|
}
|
||||||
|
BranchGenFn::JOMulToTarget0 => {
|
||||||
|
asm.jo_mul(target0)
|
||||||
|
}
|
||||||
BranchGenFn::JITReturn => {
|
BranchGenFn::JITReturn => {
|
||||||
asm_comment!(asm, "update cfp->jit_return");
|
asm_comment!(asm, "update cfp->jit_return");
|
||||||
asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), Opnd::const_ptr(target0.unwrap_code_ptr().raw_ptr()));
|
asm.mov(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_JIT_RETURN), Opnd::const_ptr(target0.unwrap_code_ptr().raw_ptr()));
|
||||||
@ -566,6 +570,7 @@ impl BranchGenFn {
|
|||||||
BranchGenFn::JZToTarget0 |
|
BranchGenFn::JZToTarget0 |
|
||||||
BranchGenFn::JBEToTarget0 |
|
BranchGenFn::JBEToTarget0 |
|
||||||
BranchGenFn::JBToTarget0 |
|
BranchGenFn::JBToTarget0 |
|
||||||
|
BranchGenFn::JOMulToTarget0 |
|
||||||
BranchGenFn::JITReturn => BranchShape::Default,
|
BranchGenFn::JITReturn => BranchShape::Default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -587,6 +592,7 @@ impl BranchGenFn {
|
|||||||
BranchGenFn::JZToTarget0 |
|
BranchGenFn::JZToTarget0 |
|
||||||
BranchGenFn::JBEToTarget0 |
|
BranchGenFn::JBEToTarget0 |
|
||||||
BranchGenFn::JBToTarget0 |
|
BranchGenFn::JBToTarget0 |
|
||||||
|
BranchGenFn::JOMulToTarget0 |
|
||||||
BranchGenFn::JITReturn => {
|
BranchGenFn::JITReturn => {
|
||||||
assert_eq!(new_shape, BranchShape::Default);
|
assert_eq!(new_shape, BranchShape::Default);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user