mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-20 02:29:47 +00:00
Merge pull request #3870 from roc-lang/dev-backend-div
add int/float division to the dev backend
This commit is contained in:
commit
51d5aa789a
10 changed files with 384 additions and 40 deletions
|
@ -55,9 +55,6 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
||||||
Symbol::NUM_TO_F32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F32_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
Symbol::NUM_TO_F32_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F32_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
||||||
Symbol::NUM_TO_F64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F64_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
Symbol::NUM_TO_F64_CHECKED => Some(to_num_checked(Symbol::NUM_TO_F64_CHECKED, var_store, LowLevel::NumToFloatChecked)),
|
||||||
|
|
||||||
Symbol::NUM_DIV_FRAC => Some(lowlevel_2(Symbol::NUM_DIV_FRAC, LowLevel::NumDivUnchecked, var_store)),
|
|
||||||
Symbol::NUM_DIV_TRUNC => Some(lowlevel_2(Symbol::NUM_DIV_TRUNC, LowLevel::NumDivUnchecked, var_store)),
|
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +78,6 @@ macro_rules! map_symbol_to_lowlevel_and_arity {
|
||||||
LowLevel::NumToFloatCast => unreachable!(),
|
LowLevel::NumToFloatCast => unreachable!(),
|
||||||
LowLevel::NumToIntChecked => unreachable!(),
|
LowLevel::NumToIntChecked => unreachable!(),
|
||||||
LowLevel::NumToFloatChecked => unreachable!(),
|
LowLevel::NumToFloatChecked => unreachable!(),
|
||||||
LowLevel::NumDivUnchecked => unreachable!(),
|
|
||||||
|
|
||||||
// these are used internally and not tied to a symbol
|
// these are used internally and not tied to a symbol
|
||||||
LowLevel::Hash => unimplemented!(),
|
LowLevel::Hash => unimplemented!(),
|
||||||
|
@ -162,6 +158,8 @@ map_symbol_to_lowlevel_and_arity! {
|
||||||
NumLt; NUM_LT; 2,
|
NumLt; NUM_LT; 2,
|
||||||
NumLte; NUM_LTE; 2,
|
NumLte; NUM_LTE; 2,
|
||||||
NumCompare; NUM_COMPARE; 2,
|
NumCompare; NUM_COMPARE; 2,
|
||||||
|
NumDivFrac; NUM_DIV_FRAC; 2,
|
||||||
|
NumDivTruncUnchecked; NUM_DIV_TRUNC; 2,
|
||||||
NumDivCeilUnchecked; NUM_DIV_CEIL; 2,
|
NumDivCeilUnchecked; NUM_DIV_CEIL; 2,
|
||||||
NumRemUnchecked; NUM_REM; 2,
|
NumRemUnchecked; NUM_REM; 2,
|
||||||
NumIsMultipleOf; NUM_IS_MULTIPLE_OF; 2,
|
NumIsMultipleOf; NUM_IS_MULTIPLE_OF; 2,
|
||||||
|
|
|
@ -456,6 +456,32 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
todo!("register unsigned multiplication for AArch64");
|
todo!("register unsigned multiplication for AArch64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn idiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_storage_manager: &mut StorageManager<'a, AArch64GeneralReg, AArch64FloatReg, ASM, CC>,
|
||||||
|
_dst: AArch64GeneralReg,
|
||||||
|
_src1: AArch64GeneralReg,
|
||||||
|
_src2: AArch64GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<AArch64GeneralReg, AArch64FloatReg>,
|
||||||
|
CC: CallConv<AArch64GeneralReg, AArch64FloatReg, ASM>,
|
||||||
|
{
|
||||||
|
todo!("register signed division for AArch64");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn udiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
_storage_manager: &mut StorageManager<'a, AArch64GeneralReg, AArch64FloatReg, ASM, CC>,
|
||||||
|
_dst: AArch64GeneralReg,
|
||||||
|
_src1: AArch64GeneralReg,
|
||||||
|
_src2: AArch64GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<AArch64GeneralReg, AArch64FloatReg>,
|
||||||
|
CC: CallConv<AArch64GeneralReg, AArch64FloatReg, ASM>,
|
||||||
|
{
|
||||||
|
todo!("register unsigned division for AArch64");
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mul_freg32_freg32_freg32(
|
fn mul_freg32_freg32_freg32(
|
||||||
_buf: &mut Vec<'_, u8>,
|
_buf: &mut Vec<'_, u8>,
|
||||||
|
@ -475,6 +501,25 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
todo!("multiplication for floats for AArch64");
|
todo!("multiplication for floats for AArch64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn div_freg32_freg32_freg32(
|
||||||
|
_buf: &mut Vec<'_, u8>,
|
||||||
|
_dst: AArch64FloatReg,
|
||||||
|
_src1: AArch64FloatReg,
|
||||||
|
_src2: AArch64FloatReg,
|
||||||
|
) {
|
||||||
|
todo!("division for floats for AArch64");
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn div_freg64_freg64_freg64(
|
||||||
|
_buf: &mut Vec<'_, u8>,
|
||||||
|
_dst: AArch64FloatReg,
|
||||||
|
_src1: AArch64FloatReg,
|
||||||
|
_src2: AArch64FloatReg,
|
||||||
|
) {
|
||||||
|
todo!("division for floats for AArch64");
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn jmp_imm32(_buf: &mut Vec<'_, u8>, _offset: i32) -> usize {
|
fn jmp_imm32(_buf: &mut Vec<'_, u8>, _offset: i32) -> usize {
|
||||||
todo!("jump instructions for AArch64");
|
todo!("jump instructions for AArch64");
|
||||||
|
|
|
@ -222,6 +222,18 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
||||||
src1: FloatReg,
|
src1: FloatReg,
|
||||||
src2: FloatReg,
|
src2: FloatReg,
|
||||||
);
|
);
|
||||||
|
fn div_freg32_freg32_freg32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: FloatReg,
|
||||||
|
src1: FloatReg,
|
||||||
|
src2: FloatReg,
|
||||||
|
);
|
||||||
|
fn div_freg64_freg64_freg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: FloatReg,
|
||||||
|
src1: FloatReg,
|
||||||
|
src2: FloatReg,
|
||||||
|
);
|
||||||
fn imul_reg64_reg64_reg64(
|
fn imul_reg64_reg64_reg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
dst: GeneralReg,
|
dst: GeneralReg,
|
||||||
|
@ -238,6 +250,25 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
||||||
ASM: Assembler<GeneralReg, FloatReg>,
|
ASM: Assembler<GeneralReg, FloatReg>,
|
||||||
CC: CallConv<GeneralReg, FloatReg, ASM>;
|
CC: CallConv<GeneralReg, FloatReg, ASM>;
|
||||||
|
|
||||||
|
fn idiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, CC>,
|
||||||
|
dst: GeneralReg,
|
||||||
|
src1: GeneralReg,
|
||||||
|
src2: GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<GeneralReg, FloatReg>,
|
||||||
|
CC: CallConv<GeneralReg, FloatReg, ASM>;
|
||||||
|
fn udiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, CC>,
|
||||||
|
dst: GeneralReg,
|
||||||
|
src1: GeneralReg,
|
||||||
|
src2: GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<GeneralReg, FloatReg>,
|
||||||
|
CC: CallConv<GeneralReg, FloatReg, ASM>;
|
||||||
|
|
||||||
fn sub_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32);
|
fn sub_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32);
|
||||||
fn sub_reg64_reg64_reg64(
|
fn sub_reg64_reg64_reg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -808,6 +839,62 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_num_div(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>) {
|
||||||
|
match layout {
|
||||||
|
Layout::Builtin(Builtin::Int(
|
||||||
|
IntWidth::I64 | IntWidth::I32 | IntWidth::I16 | IntWidth::I8,
|
||||||
|
)) => {
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
|
let src1_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src1);
|
||||||
|
let src2_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src2);
|
||||||
|
|
||||||
|
ASM::idiv_reg64_reg64_reg64(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
dst_reg,
|
||||||
|
src1_reg,
|
||||||
|
src2_reg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Int(
|
||||||
|
IntWidth::U64 | IntWidth::U32 | IntWidth::U16 | IntWidth::U8,
|
||||||
|
)) => {
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
|
let src1_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src1);
|
||||||
|
let src2_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src2);
|
||||||
|
|
||||||
|
ASM::udiv_reg64_reg64_reg64(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
dst_reg,
|
||||||
|
src1_reg,
|
||||||
|
src2_reg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||||
|
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||||
|
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
|
||||||
|
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
|
||||||
|
ASM::div_freg64_freg64_freg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||||
|
}
|
||||||
|
Layout::Builtin(Builtin::Float(FloatWidth::F32)) => {
|
||||||
|
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||||
|
let src1_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src1);
|
||||||
|
let src2_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src2);
|
||||||
|
ASM::div_freg32_freg32_freg32(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||||
|
}
|
||||||
|
x => todo!("NumDiv: layout, {:?}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_num_neg(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>) {
|
fn build_num_neg(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>) {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
||||||
|
|
|
@ -1061,6 +1061,78 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn div_freg32_freg32_freg32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64FloatReg,
|
||||||
|
src1: X86_64FloatReg,
|
||||||
|
src2: X86_64FloatReg,
|
||||||
|
) {
|
||||||
|
if dst == src1 {
|
||||||
|
divss_freg32_freg32(buf, dst, src2);
|
||||||
|
} else if dst == src2 {
|
||||||
|
divss_freg32_freg32(buf, dst, src1);
|
||||||
|
} else {
|
||||||
|
movsd_freg64_freg64(buf, dst, src1);
|
||||||
|
divss_freg32_freg32(buf, dst, src2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn div_freg64_freg64_freg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64FloatReg,
|
||||||
|
src1: X86_64FloatReg,
|
||||||
|
src2: X86_64FloatReg,
|
||||||
|
) {
|
||||||
|
if dst == src1 {
|
||||||
|
divsd_freg64_freg64(buf, dst, src2);
|
||||||
|
} else if dst == src2 {
|
||||||
|
divsd_freg64_freg64(buf, dst, src1);
|
||||||
|
} else {
|
||||||
|
movsd_freg64_freg64(buf, dst, src1);
|
||||||
|
divsd_freg64_freg64(buf, dst, src2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
storage_manager: &mut StorageManager<'a, X86_64GeneralReg, X86_64FloatReg, ASM, CC>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
src1: X86_64GeneralReg,
|
||||||
|
src2: X86_64GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<X86_64GeneralReg, X86_64FloatReg>,
|
||||||
|
CC: CallConv<X86_64GeneralReg, X86_64FloatReg, ASM>,
|
||||||
|
{
|
||||||
|
use crate::generic64::RegStorage;
|
||||||
|
|
||||||
|
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RAX));
|
||||||
|
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RDX));
|
||||||
|
|
||||||
|
mov_reg64_reg64(buf, X86_64GeneralReg::RAX, src1);
|
||||||
|
idiv_reg64_reg64(buf, src2);
|
||||||
|
mov_reg64_reg64(buf, dst, X86_64GeneralReg::RAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn udiv_reg64_reg64_reg64<'a, ASM, CC>(
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
storage_manager: &mut StorageManager<'a, X86_64GeneralReg, X86_64FloatReg, ASM, CC>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
src1: X86_64GeneralReg,
|
||||||
|
src2: X86_64GeneralReg,
|
||||||
|
) where
|
||||||
|
ASM: Assembler<X86_64GeneralReg, X86_64FloatReg>,
|
||||||
|
CC: CallConv<X86_64GeneralReg, X86_64FloatReg, ASM>,
|
||||||
|
{
|
||||||
|
use crate::generic64::RegStorage;
|
||||||
|
|
||||||
|
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RAX));
|
||||||
|
storage_manager.ensure_reg_free(buf, RegStorage::General(X86_64GeneralReg::RDX));
|
||||||
|
|
||||||
|
mov_reg64_reg64(buf, X86_64GeneralReg::RAX, src1);
|
||||||
|
udiv_reg64_reg64(buf, src2);
|
||||||
|
mov_reg64_reg64(buf, dst, X86_64GeneralReg::RAX);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize {
|
fn jmp_imm32(buf: &mut Vec<'_, u8>, offset: i32) -> usize {
|
||||||
jmp_imm32(buf, offset);
|
jmp_imm32(buf, offset);
|
||||||
|
@ -1481,6 +1553,46 @@ fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64Fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `DIVSS xmm1,xmm2/m64` -> Divide the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
||||||
|
#[inline(always)]
|
||||||
|
fn divss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||||
|
let dst_high = dst as u8 > 7;
|
||||||
|
let dst_mod = dst as u8 % 8;
|
||||||
|
let src_high = src as u8 > 7;
|
||||||
|
let src_mod = src as u8 % 8;
|
||||||
|
if dst_high || src_high {
|
||||||
|
buf.extend(&[
|
||||||
|
0xF3,
|
||||||
|
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||||
|
0x0F,
|
||||||
|
0x5E,
|
||||||
|
0xC0 | (dst_mod << 3) | (src_mod),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
buf.extend(&[0xF3, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `DIVSD xmm1,xmm2/m64` -> Divide the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
||||||
|
#[inline(always)]
|
||||||
|
fn divsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||||
|
let dst_high = dst as u8 > 7;
|
||||||
|
let dst_mod = dst as u8 % 8;
|
||||||
|
let src_high = src as u8 > 7;
|
||||||
|
let src_mod = src as u8 % 8;
|
||||||
|
if dst_high || src_high {
|
||||||
|
buf.extend(&[
|
||||||
|
0xF2,
|
||||||
|
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||||
|
0x0F,
|
||||||
|
0x5E,
|
||||||
|
0xC0 | (dst_mod << 3) | (src_mod),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
buf.extend(&[0xF2, 0x0F, 0x5E, 0xC0 | (dst_mod << 3) | (src_mod)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `ADDSS xmm1,xmm2/m64` -> Add the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
/// `ADDSS xmm1,xmm2/m64` -> Add the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||||
|
@ -1583,6 +1695,47 @@ fn mul_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) {
|
||||||
buf.extend(&[rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]);
|
buf.extend(&[rex, 0xF7, 0b1110_0000 | (src as u8 % 8)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `IDIV r/m64` -> Signed divide RDX:RAX by r/m64, with result stored in RAX ← Quotient, RDX ← Remainder.
|
||||||
|
#[inline(always)]
|
||||||
|
fn idiv_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) {
|
||||||
|
let mut rex = REX_W;
|
||||||
|
rex = add_reg_extension(src, rex);
|
||||||
|
|
||||||
|
if src.value() > 7 {
|
||||||
|
rex |= REX_PREFIX_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CQO instruction can be used to produce a double quadword dividend
|
||||||
|
// from a quadword before a quadword division.
|
||||||
|
//
|
||||||
|
// The CQO instruction (available in 64-bit mode only) copies the sign (bit 63)
|
||||||
|
// of the value in the RAX register into every bit position in the RDX register
|
||||||
|
buf.extend(&[0x48, 0x99]);
|
||||||
|
|
||||||
|
buf.extend(&[rex, 0xF7, 0b1111_1000 | (src as u8 % 8)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `DIV r/m64` -> Unsigned divide RDX:RAX by r/m64, with result stored in RAX ← Quotient, RDX ← Remainder.
|
||||||
|
#[inline(always)]
|
||||||
|
fn udiv_reg64_reg64(buf: &mut Vec<'_, u8>, src: X86_64GeneralReg) {
|
||||||
|
let mut rex = REX_W;
|
||||||
|
rex = add_reg_extension(src, rex);
|
||||||
|
|
||||||
|
if src.value() > 7 {
|
||||||
|
rex |= REX_PREFIX_B;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CQO instruction can be used to produce a double quadword dividend
|
||||||
|
// from a quadword before a quadword division.
|
||||||
|
//
|
||||||
|
// The CQO instruction (available in 64-bit mode only) copies the sign (bit 63)
|
||||||
|
// of the value in the RAX register into every bit position in the RDX register
|
||||||
|
buf.extend(&[0x48, 0x99]);
|
||||||
|
|
||||||
|
// adds a cqo (convert doubleword to quadword)
|
||||||
|
buf.extend(&[rex, 0xF7, 0b1111_0000 | (src as u8 % 8)]);
|
||||||
|
}
|
||||||
|
|
||||||
/// Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits.
|
/// Jump near, relative, RIP = RIP + 32-bit displacement sign extended to 64-bits.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) {
|
fn jmp_imm32(buf: &mut Vec<'_, u8>, imm: i32) {
|
||||||
|
@ -2233,6 +2386,44 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_idiv_reg64_reg64() {
|
||||||
|
disassembler_test!(
|
||||||
|
idiv_reg64_reg64,
|
||||||
|
|reg| format!("cqo\nidiv {}", reg),
|
||||||
|
ALL_GENERAL_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_div_reg64_reg64() {
|
||||||
|
disassembler_test!(
|
||||||
|
udiv_reg64_reg64,
|
||||||
|
|reg| format!("cqo\ndiv {}", reg),
|
||||||
|
ALL_GENERAL_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divsd_freg64_freg64() {
|
||||||
|
disassembler_test!(
|
||||||
|
divsd_freg64_freg64,
|
||||||
|
|reg1, reg2| format!("divsd {}, {}", reg1, reg2),
|
||||||
|
ALL_FLOAT_REGS,
|
||||||
|
ALL_FLOAT_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_divss_freg32_freg32() {
|
||||||
|
disassembler_test!(
|
||||||
|
divss_freg32_freg32,
|
||||||
|
|reg1, reg2| format!("divss {}, {}", reg1, reg2),
|
||||||
|
ALL_FLOAT_REGS,
|
||||||
|
ALL_FLOAT_REGS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_jmp_imm32() {
|
fn test_jmp_imm32() {
|
||||||
const INST_SIZE: i32 = 5;
|
const INST_SIZE: i32 = 5;
|
||||||
|
|
|
@ -442,6 +442,22 @@ trait Backend<'a> {
|
||||||
);
|
);
|
||||||
self.build_num_mul(sym, &args[0], &args[1], ret_layout)
|
self.build_num_mul(sym, &args[0], &args[1], ret_layout)
|
||||||
}
|
}
|
||||||
|
LowLevel::NumDivTruncUnchecked | LowLevel::NumDivFrac => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
2,
|
||||||
|
args.len(),
|
||||||
|
"NumDiv: expected to have exactly two argument"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
arg_layouts[0], arg_layouts[1],
|
||||||
|
"NumDiv: expected all arguments of to have the same layout"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
arg_layouts[0], *ret_layout,
|
||||||
|
"NumDiv: expected to have the same argument and return layout"
|
||||||
|
);
|
||||||
|
self.build_num_div(sym, &args[0], &args[1], ret_layout)
|
||||||
|
}
|
||||||
LowLevel::NumNeg => {
|
LowLevel::NumNeg => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
1,
|
1,
|
||||||
|
@ -702,6 +718,9 @@ trait Backend<'a> {
|
||||||
/// build_num_mul stores `src1 * src2` into dst.
|
/// build_num_mul stores `src1 * src2` into dst.
|
||||||
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
|
fn build_num_mul(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
|
||||||
|
|
||||||
|
/// build_num_mul stores `src1 / src2` into dst.
|
||||||
|
fn build_num_div(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &Layout<'a>);
|
||||||
|
|
||||||
/// build_num_neg stores the negated value of src into dst.
|
/// build_num_neg stores the negated value of src into dst.
|
||||||
fn build_num_neg(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>);
|
fn build_num_neg(&mut self, dst: &Symbol, src: &Symbol, layout: &Layout<'a>);
|
||||||
|
|
||||||
|
|
|
@ -6266,9 +6266,9 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
||||||
| NumIsMultipleOf | NumAddWrap | NumAddChecked | NumAddSaturated | NumDivUnchecked
|
| NumIsMultipleOf | NumAddWrap | NumAddChecked | NumAddSaturated | NumDivFrac
|
||||||
| NumDivCeilUnchecked | NumPow | NumPowInt | NumSubWrap | NumSubChecked
|
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumPow | NumPowInt | NumSubWrap
|
||||||
| NumSubSaturated | NumMulWrap | NumMulSaturated | NumMulChecked => {
|
| NumSubChecked | NumSubSaturated | NumMulWrap | NumMulSaturated | NumMulChecked => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
|
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
@ -7072,7 +7072,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
&[lhs.into(), rhs.into()],
|
&[lhs.into(), rhs.into()],
|
||||||
&bitcode::NUM_POW_INT[int_width],
|
&bitcode::NUM_POW_INT[int_width],
|
||||||
),
|
),
|
||||||
NumDivUnchecked => {
|
NumDivTruncUnchecked => {
|
||||||
if int_width.is_signed() {
|
if int_width.is_signed() {
|
||||||
bd.build_int_signed_div(lhs, rhs, "div_int").into()
|
bd.build_int_signed_div(lhs, rhs, "div_int").into()
|
||||||
} else {
|
} else {
|
||||||
|
@ -7253,7 +7253,7 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
|
||||||
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
||||||
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
||||||
NumDivUnchecked => bd.build_float_div(lhs, rhs, "div_float").into(),
|
NumDivFrac => bd.build_float_div(lhs, rhs, "div_float").into(),
|
||||||
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||||
|
@ -7370,7 +7370,7 @@ fn build_dec_binop<'a, 'ctx, 'env>(
|
||||||
rhs,
|
rhs,
|
||||||
"decimal multiplication overflowed",
|
"decimal multiplication overflowed",
|
||||||
),
|
),
|
||||||
NumDivUnchecked => dec_binop_with_unchecked(env, bitcode::DEC_DIV, lhs, rhs),
|
NumDivFrac => dec_binop_with_unchecked(env, bitcode::DEC_DIV, lhs, rhs),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1191,7 +1191,16 @@ impl<'a> LowLevelCall<'a> {
|
||||||
x => todo!("{:?} for {:?}", self.lowlevel, x),
|
x => todo!("{:?} for {:?}", self.lowlevel, x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumDivUnchecked => {
|
NumDivFrac => {
|
||||||
|
self.load_args(backend);
|
||||||
|
match CodeGenNumType::for_symbol(backend, self.arguments[0]) {
|
||||||
|
F32 => backend.code_builder.f32_div(),
|
||||||
|
F64 => backend.code_builder.f64_div(),
|
||||||
|
Decimal => self.load_args_and_call_zig(backend, bitcode::DEC_DIV),
|
||||||
|
x => todo!("{:?} for {:?}", self.lowlevel, x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumDivTruncUnchecked => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
let is_signed = symbol_is_signed_int(backend, self.arguments[0]);
|
let is_signed = symbol_is_signed_int(backend, self.arguments[0]);
|
||||||
match CodeGenNumType::for_symbol(backend, self.arguments[0]) {
|
match CodeGenNumType::for_symbol(backend, self.arguments[0]) {
|
||||||
|
@ -1209,9 +1218,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
backend.code_builder.i64_div_u()
|
backend.code_builder.i64_div_u()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
F32 => backend.code_builder.f32_div(),
|
|
||||||
F64 => backend.code_builder.f64_div(),
|
|
||||||
Decimal => self.load_args_and_call_zig(backend, bitcode::DEC_DIV),
|
|
||||||
x => todo!("{:?} for {:?}", self.lowlevel, x),
|
x => todo!("{:?} for {:?}", self.lowlevel, x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ pub enum LowLevel {
|
||||||
NumLt,
|
NumLt,
|
||||||
NumLte,
|
NumLte,
|
||||||
NumCompare,
|
NumCompare,
|
||||||
NumDivUnchecked,
|
NumDivFrac,
|
||||||
|
NumDivTruncUnchecked,
|
||||||
NumDivCeilUnchecked,
|
NumDivCeilUnchecked,
|
||||||
NumRemUnchecked,
|
NumRemUnchecked,
|
||||||
NumIsMultipleOf,
|
NumIsMultipleOf,
|
||||||
|
@ -204,7 +205,6 @@ macro_rules! map_symbol_to_lowlevel {
|
||||||
LowLevel::NumToFloatCast => unreachable!(),
|
LowLevel::NumToFloatCast => unreachable!(),
|
||||||
LowLevel::NumToIntChecked => unreachable!(),
|
LowLevel::NumToIntChecked => unreachable!(),
|
||||||
LowLevel::NumToFloatChecked => unreachable!(),
|
LowLevel::NumToFloatChecked => unreachable!(),
|
||||||
LowLevel::NumDivUnchecked => unreachable!(),
|
|
||||||
|
|
||||||
// these are used internally and not tied to a symbol
|
// these are used internally and not tied to a symbol
|
||||||
LowLevel::Hash => unimplemented!(),
|
LowLevel::Hash => unimplemented!(),
|
||||||
|
@ -279,7 +279,9 @@ map_symbol_to_lowlevel! {
|
||||||
NumLt <= NUM_LT,
|
NumLt <= NUM_LT,
|
||||||
NumLte <= NUM_LTE,
|
NumLte <= NUM_LTE,
|
||||||
NumCompare <= NUM_COMPARE,
|
NumCompare <= NUM_COMPARE,
|
||||||
|
NumDivFrac <= NUM_DIV_FRAC,
|
||||||
NumDivCeilUnchecked <= NUM_DIV_CEIL,
|
NumDivCeilUnchecked <= NUM_DIV_CEIL,
|
||||||
|
NumDivTruncUnchecked <= NUM_DIV_TRUNC,
|
||||||
NumRemUnchecked <= NUM_REM,
|
NumRemUnchecked <= NUM_REM,
|
||||||
NumIsMultipleOf <= NUM_IS_MULTIPLE_OF,
|
NumIsMultipleOf <= NUM_IS_MULTIPLE_OF,
|
||||||
NumAbs <= NUM_ABS,
|
NumAbs <= NUM_ABS,
|
||||||
|
|
|
@ -913,10 +913,10 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
|
|
||||||
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
|
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumAddSaturated | NumSub | NumSubWrap
|
||||||
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
|
| NumSubChecked | NumSubSaturated | NumMul | NumMulWrap | NumMulSaturated
|
||||||
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked
|
| NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumDivFrac
|
||||||
| NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf | NumPow | NumPowInt
|
| NumDivTruncUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf
|
||||||
| NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
|
| NumPow | NumPowInt | NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy
|
||||||
| NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
| NumShiftRightBy | NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||||
|
|
||||||
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
||||||
| NumRound | NumCeiling | NumFloor | NumToFrac | Not | NumIsFinite | NumAtan | NumAcos
|
| NumRound | NumCeiling | NumFloor | NumToFrac | Not | NumIsFinite | NumAtan | NumAcos
|
||||||
|
|
|
@ -735,17 +735,15 @@ fn gen_wrap_add_nums() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn gen_div_f64() {
|
fn gen_div_f64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!("48 / 2", 24.0, f64);
|
||||||
indoc!(
|
}
|
||||||
r#"
|
|
||||||
48 / 2
|
#[test]
|
||||||
"#
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
),
|
fn gen_div_f32() {
|
||||||
24.0,
|
assert_evals_to!("48f32 / 2", 24.0, f32);
|
||||||
f64
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1146,17 +1144,15 @@ fn gen_mul_f32() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn gen_div_i64() {
|
fn gen_div_i64() {
|
||||||
assert_evals_to!(
|
assert_evals_to!("1000i64 // 10", 100, i64);
|
||||||
indoc!(
|
}
|
||||||
r#"
|
|
||||||
1000 // 10
|
#[test]
|
||||||
"#
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
),
|
fn gen_div_u64() {
|
||||||
100,
|
assert_evals_to!("1000u64 // 10", 100, u64);
|
||||||
i64
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue