mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-01 10:52:18 +00:00
gen-dev: impl Num.neg for Dec,F32,F64
Dec negation was implemented across gen-dev, gen-llvm, gen-wasm as a call to the compiled zig function `bitcode::DEC_NEGATE`. f32 and f64 negation were implemented already for gen-llvm, gen-wasm. for gen-dev x86_64, float negation is implemented by flipping the sign bit, which means `xorps` for f32, and `xorpd` for f64 for gen-dev aarch64, there is conveniently a `fneg` instruction
This commit is contained in:
parent
6a3db1e59a
commit
a98acff0b9
6 changed files with 156 additions and 2 deletions
|
@ -1846,6 +1846,26 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
neg_reg64_reg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F64, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F32, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
@ -3953,6 +3973,24 @@ fn fsub_freg_freg_freg(
|
|||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FNEG Sd/Dd, Sn/Dn`
|
||||
#[inline(always)]
|
||||
fn fneg_freg_freg(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
ftype: FloatWidth,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
let inst =
|
||||
FloatingPointDataProcessingOneSource::new(FloatingPointDataProcessingOneSourceParams {
|
||||
ptype: ftype,
|
||||
opcode: 0b00010,
|
||||
rn: src,
|
||||
rd: dst,
|
||||
});
|
||||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags.
|
||||
#[inline(always)]
|
||||
fn fcmp_freg_freg(
|
||||
|
|
|
@ -557,6 +557,18 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||
fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
||||
|
||||
fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn mul_freg32_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: FloatReg,
|
||||
|
@ -1791,7 +1803,24 @@ impl<
|
|||
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src);
|
||||
ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
||||
}
|
||||
x => todo!("NumNeg: layout, {:?}", x),
|
||||
LayoutRepr::F32 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg32_freg32(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::F64 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::DEC => self.build_fn_call(
|
||||
dst,
|
||||
bitcode::DEC_NEGATE.to_string(),
|
||||
&[*src],
|
||||
&[Layout::DEC],
|
||||
&Layout::DEC,
|
||||
),
|
||||
other => internal_error!("unreachable: NumNeg for layout, {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2602,6 +2602,28 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
neg_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg64_imm64(buf, relocs, dst, f64::from_bits(0x8000_0000_0000_0000));
|
||||
xorpd_freg64_freg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg32_imm32(buf, relocs, dst, f32::from_bits(0x8000_0000));
|
||||
xorps_freg32_freg32(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
@ -3352,6 +3374,49 @@ fn sqrtss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64F
|
|||
}
|
||||
}
|
||||
|
||||
/// `XORPD xmm1, xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorpd_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([
|
||||
0x66,
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
])
|
||||
} else {
|
||||
buf.extend([0x66, 0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `XORPS xmm1,xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorps_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([
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
]);
|
||||
} else {
|
||||
buf.extend([0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result.
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
|
|
|
@ -2215,6 +2215,7 @@ fn build_dec_unary_op<'a, 'ctx>(
|
|||
|
||||
match op {
|
||||
NumAbs => dec_unary_op(env, bitcode::DEC_ABS, arg),
|
||||
NumNeg => dec_unary_op(env, bitcode::DEC_NEGATE, arg),
|
||||
NumAcos => dec_unary_op(env, bitcode::DEC_ACOS, arg),
|
||||
NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg),
|
||||
NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg),
|
||||
|
|
|
@ -1624,6 +1624,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
F32 => backend.code_builder.f32_neg(),
|
||||
F64 => backend.code_builder.f64_neg(),
|
||||
Decimal => self.load_args_and_call_zig(backend, bitcode::DEC_NEGATE),
|
||||
_ => todo!("{:?} for {:?}", self.lowlevel, self.ret_layout),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1607,7 +1607,7 @@ fn tail_call_elimination() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn int_negate() {
|
||||
fn num_negate() {
|
||||
assert_evals_to!("Num.neg 123i8", -123, i8);
|
||||
assert_evals_to!("Num.neg Num.maxI8", -i8::MAX, i8);
|
||||
assert_evals_to!("Num.neg (Num.minI8 + 1)", i8::MAX, i8);
|
||||
|
@ -1623,6 +1623,26 @@ fn int_negate() {
|
|||
assert_evals_to!("Num.neg 123", -123, i64);
|
||||
assert_evals_to!("Num.neg Num.maxI64", -i64::MAX, i64);
|
||||
assert_evals_to!("Num.neg (Num.minI64 + 1)", i64::MAX, i64);
|
||||
|
||||
assert_evals_to!("Num.neg 12.3f32", -12.3, f32);
|
||||
assert_evals_to!("Num.neg 0.0f32", -0.0, f32);
|
||||
assert_evals_to!("Num.neg Num.maxF32", -f32::MAX, f32);
|
||||
assert_evals_to!("Num.neg Num.minF32", -f32::MIN, f32);
|
||||
assert_evals_to!("Num.neg Num.infinityF32", -f32::INFINITY, f32);
|
||||
// can't test equality for nan
|
||||
assert_evals_to!("Num.isNaN (Num.neg Num.nanF32)", true, bool);
|
||||
|
||||
assert_evals_to!("Num.neg 12.3f64", -12.3, f64);
|
||||
assert_evals_to!("Num.neg 0.0f64", -0.0, f64);
|
||||
assert_evals_to!("Num.neg Num.maxF64", -f64::MAX, f64);
|
||||
assert_evals_to!("Num.neg Num.minF64", -f64::MIN, f64);
|
||||
assert_evals_to!("Num.neg Num.infinityF64", -f64::INFINITY, f64);
|
||||
// can't test equality for nan
|
||||
assert_evals_to!("Num.isNaN (Num.neg Num.nanF64)", true, bool);
|
||||
|
||||
assert_evals_to!("Num.neg 123dec", RocDec::from(-123), RocDec);
|
||||
// 0 is signless, unlike f32/f64
|
||||
assert_evals_to!("Num.neg 0dec", RocDec::from(0), RocDec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue