implement NumSub (that panics) in the dev backend

This commit is contained in:
Folkert 2024-01-02 19:46:03 +01:00
parent cfdfbe18a4
commit 4bcd195b92
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
4 changed files with 168 additions and 97 deletions

View file

@ -1266,6 +1266,7 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
) { ) {
add_reg64_reg64_reg64(buf, dst, src1, src2); add_reg64_reg64_reg64(buf, dst, src1, src2);
} }
#[inline(always)] #[inline(always)]
fn add_freg32_freg32_freg32( fn add_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -1285,6 +1286,25 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
fadd_freg_freg_freg(buf, FloatWidth::F64, dst, src1, src2); fadd_freg_freg_freg(buf, FloatWidth::F64, dst, src1, src2);
} }
#[inline(always)]
fn sub_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>,
dst: AArch64FloatReg,
src1: AArch64FloatReg,
src2: AArch64FloatReg,
) {
fsub_freg_freg_freg(buf, FloatWidth::F32, dst, src1, src2);
}
#[inline(always)]
fn sub_freg64_freg64_freg64(
buf: &mut Vec<'_, u8>,
dst: AArch64FloatReg,
src1: AArch64FloatReg,
src2: AArch64FloatReg,
) {
fsub_freg_freg_freg(buf, FloatWidth::F64, dst, src1, src2);
}
#[inline(always)] #[inline(always)]
fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String) { fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String) {
let inst = 0b1001_0100_0000_0000_0000_0000_0000_0000u32; let inst = 0b1001_0100_0000_0000_0000_0000_0000_0000u32;
@ -3894,6 +3914,27 @@ fn fadd_freg_freg_freg(
buf.extend(inst.bytes()); buf.extend(inst.bytes());
} }
/// `FSUB Sd/Dd, Sn/Dn, Sm/Dm` -> Sub Sn/Dn and Sm/Dm and place the result into Sd/Dd.
#[inline(always)]
fn fsub_freg_freg_freg(
buf: &mut Vec<'_, u8>,
ftype: FloatWidth,
dst: AArch64FloatReg,
src1: AArch64FloatReg,
src2: AArch64FloatReg,
) {
let inst =
FloatingPointDataProcessingTwoSource::new(FloatingPointDataProcessingTwoSourceParams {
opcode: 0b0011,
ptype: ftype,
rd: dst,
rn: src1,
rm: src2,
});
buf.extend(inst.bytes());
}
/// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags. /// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags.
#[inline(always)] #[inline(always)]
fn fcmp_freg_freg( fn fcmp_freg_freg(

View file

@ -166,6 +166,13 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
); );
fn add_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32); fn add_reg64_reg64_imm32(buf: &mut Vec<'_, u8>, dst: GeneralReg, src1: GeneralReg, imm32: i32);
fn add_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
);
fn add_freg32_freg32_freg32( fn add_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
dst: FloatReg, dst: FloatReg,
@ -178,12 +185,6 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
src1: FloatReg, src1: FloatReg,
src2: FloatReg, src2: FloatReg,
); );
fn add_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: GeneralReg,
src2: GeneralReg,
);
fn and_reg64_reg64_reg64( fn and_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -629,6 +630,19 @@ 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 sub_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>,
dst: FloatReg,
src1: FloatReg,
src2: FloatReg,
);
fn sub_freg64_freg64_freg64(
buf: &mut Vec<'_, u8>,
dst: FloatReg,
src1: FloatReg,
src2: FloatReg,
);
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>,
@ -1717,9 +1731,38 @@ impl<
} }
fn build_num_sub(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>) { fn build_num_sub(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, layout: &InLayout<'a>) {
// for the time being, `num_sub` is implemented as wrapping subtraction. In roc, the normal match self.layout_interner.get_repr(*layout) {
// `sub` should panic on overflow, but we just don't do that yet LayoutRepr::Builtin(Builtin::Int(int_width)) => self.build_fn_call(
self.build_num_sub_wrap(dst, src1, src2, layout) dst,
bitcode::NUM_SUB_OR_PANIC_INT[int_width].to_string(),
&[*src1, *src2],
&[*layout, *layout],
layout,
),
LayoutRepr::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::sub_freg64_freg64_freg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
LayoutRepr::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::sub_freg32_freg32_freg32(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
LayoutRepr::DEC => self.build_fn_call(
dst,
bitcode::DEC_SUB_OR_PANIC.to_string(),
&[*src1, *src2],
&[Layout::DEC, Layout::DEC],
&Layout::DEC,
),
other => unreachable!("NumMul for layout {other:?}"),
}
} }
fn build_num_sub_wrap( fn build_num_sub_wrap(

View file

@ -2004,6 +2004,39 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
} }
} }
#[inline(always)]
fn sub_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>,
dst: X86_64FloatReg,
src1: X86_64FloatReg,
src2: X86_64FloatReg,
) {
if dst == src1 {
subss_freg32_freg32(buf, dst, src2);
} else if dst == src2 {
subss_freg32_freg32(buf, dst, src1);
} else {
movss_freg32_freg32(buf, dst, src1);
subss_freg32_freg32(buf, dst, src2);
}
}
#[inline(always)]
fn sub_freg64_freg64_freg64(
buf: &mut Vec<'_, u8>,
dst: X86_64FloatReg,
src1: X86_64FloatReg,
src2: X86_64FloatReg,
) {
if dst == src1 {
subsd_freg64_freg64(buf, dst, src2);
} else if dst == src2 {
subsd_freg64_freg64(buf, dst, src1);
} else {
movsd_freg64_freg64(buf, dst, src1);
subsd_freg64_freg64(buf, dst, src2);
}
}
#[inline(always)] #[inline(always)]
fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String) { fn call(buf: &mut Vec<'_, u8>, relocs: &mut Vec<'_, Relocation>, fn_name: String) {
buf.extend([0xE8, 0x00, 0x00, 0x00, 0x00]); buf.extend([0xE8, 0x00, 0x00, 0x00, 0x00]);
@ -3052,124 +3085,78 @@ fn sar_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg) {
buf.extend([rex, 0xD3, 0xC0 | (7 << 3) | dst_mod]); buf.extend([rex, 0xD3, 0xC0 | (7 << 3) | dst_mod]);
} }
/// `ADDSD xmm1,xmm2/m64` -> Add the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1. fn double_binary_operation(
#[inline(always)] buf: &mut Vec<'_, u8>,
fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { dst: X86_64FloatReg,
src: X86_64FloatReg,
float_width: FloatWidth,
op_code2: u8,
) {
let op_code1 = match float_width {
FloatWidth::F32 => 0xF3,
FloatWidth::F64 => 0xF2,
};
let dst_high = dst as u8 > 7; let dst_high = dst as u8 > 7;
let dst_mod = dst as u8 % 8; let dst_mod = dst as u8 % 8;
let src_high = src as u8 > 7; let src_high = src as u8 > 7;
let src_mod = src as u8 % 8; let src_mod = src as u8 % 8;
if dst_high || src_high { if dst_high || src_high {
buf.extend([ buf.extend([
0xF2, op_code1,
0x40 | ((dst_high as u8) << 2) | (src_high as u8), 0x40 | ((dst_high as u8) << 2) | (src_high as u8),
0x0F, 0x0F,
0x58, op_code2,
0xC0 | (dst_mod << 3) | (src_mod), 0xC0 | (dst_mod << 3) | (src_mod),
]) ])
} else { } else {
buf.extend([0xF2, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)]) buf.extend([op_code1, 0x0F, op_code2, 0xC0 | (dst_mod << 3) | (src_mod)])
} }
} }
/// `ADDSD xmm1,xmm2/m64` -> Add the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
#[inline(always)]
fn addsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
double_binary_operation(buf, dst, src, FloatWidth::F64, 0x58)
}
/// `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 addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { fn addss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
let dst_high = dst as u8 > 7; double_binary_operation(buf, dst, src, FloatWidth::F32, 0x58)
let dst_mod = dst as u8 % 8; }
let src_high = src as u8 > 7;
let src_mod = src as u8 % 8; /// `SUBSD xmm1,xmm2/m64` -> Sub the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
if dst_high || src_high { #[inline(always)]
buf.extend([ fn subsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
0xF3, double_binary_operation(buf, dst, src, FloatWidth::F64, 0x5C)
0x40 | ((dst_high as u8) << 2) | (src_high as u8), }
0x0F,
0x58, /// `SUBSS xmm1,xmm2/m64` -> Sub the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
0xC0 | (dst_mod << 3) | (src_mod), #[inline(always)]
]) fn subss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
} else { double_binary_operation(buf, dst, src, FloatWidth::F32, 0x5C)
buf.extend([0xF3, 0x0F, 0x58, 0xC0 | (dst_mod << 3) | (src_mod)])
}
} }
/// `MULSD xmm1,xmm2/m64` -> Multiply the low double-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1.
#[inline(always)] #[inline(always)]
fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { fn mulsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
let dst_high = dst as u8 > 7; double_binary_operation(buf, dst, src, FloatWidth::F64, 0x59)
let dst_mod = dst as u8 % 8; }
let src_high = src as u8 > 7;
let src_mod = src as u8 % 8; #[inline(always)]
if dst_high || src_high { fn mulss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
buf.extend([ double_binary_operation(buf, dst, src, FloatWidth::F32, 0x59)
0xF2,
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
0x0F,
0x59,
0xC0 | (dst_mod << 3) | (src_mod),
])
} else {
buf.extend([0xF2, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)])
}
} }
/// `DIVSS xmm1,xmm2/m64` -> Divide the low single-precision floating-point value from xmm2/mem to xmm1 and store the result in xmm1. /// `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)] #[inline(always)]
fn divss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { fn divss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
let dst_high = dst as u8 > 7; double_binary_operation(buf, dst, src, FloatWidth::F32, 0x5E)
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. /// `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)] #[inline(always)]
fn divsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { fn divsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
let dst_high = dst as u8 > 7; double_binary_operation(buf, dst, src, FloatWidth::F64, 0x5E)
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.
#[inline(always)]
fn mulss_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,
0x59,
0xC0 | (dst_mod << 3) | (src_mod),
])
} else {
buf.extend([0xF3, 0x0F, 0x59, 0xC0 | (dst_mod << 3) | (src_mod)])
}
} }
#[inline(always)] #[inline(always)]

View file

@ -1904,7 +1904,7 @@ fn float_add_overflow() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
#[should_panic(expected = r#"Roc failed with message: "Integer subtraction overflowed!"#)] #[should_panic(expected = r#"Roc failed with message: "Integer subtraction overflowed!"#)]
fn int_sub_overflow() { fn int_sub_overflow() {
assert_evals_to!("-9_223_372_036_854_775_808 - 1", 0, i64); assert_evals_to!("-9_223_372_036_854_775_808 - 1", 0, i64);