Merge pull request #5068 from roc-lang/gen-dev-i128

gen-dev: num additions
This commit is contained in:
Brendan Hansknecht 2023-02-28 16:29:42 +00:00 committed by GitHub
commit a87aec77b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 422 additions and 120 deletions

View file

@ -748,6 +748,7 @@ sqrtChecked = \x ->
else
Ok (Num.sqrt x)
## Natural logarithm
log : Frac a -> Frac a
logChecked : Frac a -> Result (Frac a) [LogNeedsPositive]

View file

@ -2,10 +2,13 @@ use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
use crate::Relocation;
use bumpalo::collections::Vec;
use packed_struct::prelude::*;
use roc_builtins::bitcode::FloatWidth;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{InLayout, STLayoutInterner};
use super::CompareOperation;
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[allow(dead_code)]
pub enum AArch64GeneralReg {
@ -888,6 +891,18 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
todo!("registers unsigned less than for AArch64");
}
#[inline(always)]
fn cmp_freg_freg_reg64(
_buf: &mut Vec<'_, u8>,
_dst: AArch64GeneralReg,
_src1: AArch64FloatReg,
_src2: AArch64FloatReg,
_width: FloatWidth,
_operation: CompareOperation,
) {
todo!("registers float comparison for AArch64");
}
#[inline(always)]
fn igt_reg64_reg64_reg64(
_buf: &mut Vec<'_, u8>,
@ -1038,6 +1053,14 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
{
todo!("sar for AArch64")
}
fn sqrt_freg64_freg64(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) {
todo!("sqrt")
}
fn sqrt_freg32_freg32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _src: AArch64FloatReg) {
todo!("sqrt")
}
}
impl AArch64Assembler {}

View file

@ -113,6 +113,13 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
);
}
pub enum CompareOperation {
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
/// Assembler contains calls to the backend assembly generator.
/// These calls do not necessarily map directly to a single assembly instruction.
/// They are higher level in cases where an instruction would not be common and shared between multiple architectures.
@ -310,6 +317,9 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
fn mov_stack32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
fn sqrt_freg64_freg64(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
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 mul_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>,
@ -406,6 +416,15 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
src2: GeneralReg,
);
fn cmp_freg_freg_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
src1: FloatReg,
src2: FloatReg,
width: FloatWidth,
operation: CompareOperation,
);
fn igt_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
dst: GeneralReg,
@ -1273,6 +1292,20 @@ impl<
.load_to_general_reg(&mut self.buf, src2);
ASM::ult_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
Layout::Builtin(Builtin::Float(width)) => {
let dst_reg = self.storage_manager.claim_general_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::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::LessThan,
);
}
x => todo!("NumLt: layout, {:?}", x),
}
}
@ -1305,6 +1338,20 @@ impl<
.load_to_general_reg(&mut self.buf, src2);
ASM::ugt_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
Layout::Builtin(Builtin::Float(width)) => {
let dst_reg = self.storage_manager.claim_general_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::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::GreaterThan,
);
}
x => todo!("NumGt: layout, {:?}", x),
}
}
@ -1385,6 +1432,26 @@ impl<
.load_to_general_reg(&mut self.buf, src2);
ASM::lte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
Layout::F64 | Layout::F32 => {
let width = if *arg_layout == Layout::F64 {
FloatWidth::F64
} else {
FloatWidth::F32
};
let dst_reg = self.storage_manager.claim_general_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::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::LessThanOrEqual,
);
}
x => todo!("NumLte: layout, {:?}", x),
}
}
@ -1407,6 +1474,26 @@ impl<
.load_to_general_reg(&mut self.buf, src2);
ASM::gte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
}
Layout::F64 | Layout::F32 => {
let width = if *arg_layout == Layout::F64 {
FloatWidth::F64
} else {
FloatWidth::F32
};
let dst_reg = self.storage_manager.claim_general_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::cmp_freg_freg_reg64(
&mut self.buf,
dst_reg,
src1_reg,
src2_reg,
width,
CompareOperation::GreaterThanOrEqual,
);
}
x => todo!("NumGte: layout, {:?}", x),
}
}
@ -2147,6 +2234,28 @@ impl<
let val = *x;
ASM::mov_reg64_imm64(&mut self.buf, reg, i128::from_ne_bytes(val) as i64);
}
(
Literal::Int(bytes),
Layout::Builtin(Builtin::Int(IntWidth::I128 | IntWidth::U128)),
) => {
self.storage_manager.with_tmp_general_reg(
&mut self.buf,
|storage_manager, buf, reg| {
let base_offset = storage_manager.claim_stack_area(sym, 16);
let mut num_bytes = [0; 8];
num_bytes.copy_from_slice(&bytes[..8]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset, reg);
num_bytes.copy_from_slice(&bytes[8..16]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset + 8, reg);
},
);
}
(Literal::Byte(x), Layout::Builtin(Builtin::Int(IntWidth::U8 | IntWidth::I8))) => {
let reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
let val = *x;
@ -2167,6 +2276,25 @@ impl<
let val = *x as f32;
ASM::mov_freg32_imm32(&mut self.buf, &mut self.relocs, reg, val);
}
(Literal::Decimal(bytes), Layout::Builtin(Builtin::Decimal)) => {
self.storage_manager.with_tmp_general_reg(
&mut self.buf,
|storage_manager, buf, reg| {
let base_offset = storage_manager.claim_stack_area(sym, 16);
let mut num_bytes = [0; 8];
num_bytes.copy_from_slice(&bytes[..8]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset, reg);
num_bytes.copy_from_slice(&bytes[8..16]);
let num = i64::from_ne_bytes(num_bytes);
ASM::mov_reg64_imm64(buf, reg, num);
ASM::mov_base32_reg64(buf, base_offset + 8, reg);
},
);
}
(Literal::Str(x), Layout::Builtin(Builtin::Str)) => {
if x.len() < 24 {
// Load small string.
@ -2446,6 +2574,18 @@ impl<
}
}
}
fn build_num_sqrt(&mut self, dst: Symbol, src: Symbol, float_width: FloatWidth) {
let buf = &mut self.buf;
let dst_reg = self.storage_manager.claim_float_reg(buf, &dst);
let src_reg = self.storage_manager.load_to_float_reg(buf, &src);
match float_width {
FloatWidth::F32 => ASM::sqrt_freg32_freg32(buf, dst_reg, src_reg),
FloatWidth::F64 => ASM::sqrt_freg64_freg64(buf, dst_reg, src_reg),
}
}
}
/// This impl block is for ir related instructions that need backend specific information.

View file

@ -753,7 +753,7 @@ impl<
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_symbol_to_stack_offset_help(buf, size, from_offset, to_offset)
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
IntWidth::I64 | IntWidth::U64 => {
debug_assert_eq!(to_offset % 8, 0);
@ -795,7 +795,7 @@ impl<
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_symbol_to_stack_offset_help(buf, size, from_offset, to_offset)
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
},
Layout::Boxed(_) => {
@ -824,13 +824,13 @@ impl<
debug_assert_eq!(from_offset % 8, 0);
debug_assert_eq!(size % 8, 0);
debug_assert_eq!(size, layout_interner.stack_size(*layout));
self.copy_symbol_to_stack_offset_help(buf, size, from_offset, to_offset)
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
}
x => todo!("copying data to the stack with layout, {:?}", x),
}
}
pub fn copy_symbol_to_stack_offset_help(
pub fn copy_to_stack_offset(
&mut self,
buf: &mut Vec<'a, u8>,
size: u32,

View file

@ -4,10 +4,13 @@ use crate::{
single_register_layouts, Relocation,
};
use bumpalo::collections::Vec;
use roc_builtins::bitcode::FloatWidth;
use roc_error_macros::internal_error;
use roc_module::symbol::Symbol;
use roc_mono::layout::{InLayout, Layout, LayoutInterner, STLayoutInterner};
use super::CompareOperation;
// Not sure exactly how I want to represent registers.
// If we want max speed, we would likely make them structs that impl the same trait to avoid ifs.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
@ -1592,6 +1595,33 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
setb_reg64(buf, dst);
}
#[inline(always)]
fn cmp_freg_freg_reg64(
buf: &mut Vec<'_, u8>,
dst: X86_64GeneralReg,
src1: X86_64FloatReg,
src2: X86_64FloatReg,
width: FloatWidth,
operation: CompareOperation,
) {
use CompareOperation::*;
let (arg1, arg2) = match operation {
LessThan | LessThanOrEqual => (src1, src2),
GreaterThan | GreaterThanOrEqual => (src2, src1),
};
match width {
FloatWidth::F32 => cmp_freg32_freg32(buf, arg2, arg1),
FloatWidth::F64 => cmp_freg64_freg64(buf, arg2, arg1),
}
match operation {
LessThan | GreaterThan => seta_reg64(buf, dst),
LessThanOrEqual | GreaterThanOrEqual => setae_reg64(buf, dst),
};
}
#[inline(always)]
fn igt_reg64_reg64_reg64(
buf: &mut Vec<'_, u8>,
@ -1715,6 +1745,14 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
{
shift_reg64_reg64_reg64(buf, storage_manager, sar_reg64_reg64, dst, src1, src2)
}
fn sqrt_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
sqrtsd_freg64_freg64(buf, dst, src)
}
fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
sqrtss_freg32_freg32(buf, dst, src)
}
}
fn shift_reg64_reg64_reg64<'a, 'r, ASM, CC>(
@ -2085,6 +2123,90 @@ fn cmp_reg64_reg64(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, src: X86_64Gene
binop_reg64_reg64(0x39, buf, dst, src);
}
#[inline(always)]
fn cmp_freg64_freg64(buf: &mut Vec<'_, u8>, src1: X86_64FloatReg, src2: X86_64FloatReg) {
let src1_high = src1 as u8 > 7;
let src1_mod = src1 as u8 % 8;
let src2_high = src2 as u8 > 7;
let src2_mod = src2 as u8 % 8;
if src1_high || src2_high {
buf.extend([
0x66,
0x40 | ((src1_high as u8) << 2) | (src2_high as u8),
0x0F,
0x2E,
0xC0 | (src1_mod << 3) | (src2_mod),
])
} else {
buf.extend([0x66, 0x0F, 0x2E, 0xC0 | (src1_mod << 3) | (src2_mod)])
}
}
#[inline(always)]
fn cmp_freg32_freg32(buf: &mut Vec<'_, u8>, src1: X86_64FloatReg, src2: X86_64FloatReg) {
let src1_high = src1 as u8 > 7;
let src1_mod = src1 as u8 % 8;
let src2_high = src2 as u8 > 7;
let src2_mod = src2 as u8 % 8;
if src1_high || src2_high {
buf.extend([
0x65,
0x40 | ((src1_high as u8) << 2) | (src2_high as u8),
0x0F,
0x2E,
0xC0 | (src1_mod << 3) | (src2_mod),
])
} else {
buf.extend([0x65, 0x0F, 0x2E, 0xC0 | (src1_mod << 3) | (src2_mod)])
}
}
#[inline(always)]
fn sqrtsd_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,
0x51,
0xC0 | (dst_mod << 3) | (src_mod),
])
} else {
buf.extend([0xF2, 0x0F, 0x51, 0xC0 | (dst_mod << 3) | (src_mod)])
}
}
#[inline(always)]
fn sqrtss_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,
0x51,
0xC0 | (dst_mod << 3) | (src_mod),
])
} else {
buf.extend([0xF3, 0x0F, 0x51, 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)]
@ -2757,6 +2879,12 @@ fn seta_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
set_reg64_help(0x97, buf, reg);
}
/// `SETAE r/m64` -> Set byte if above or equal (CF=0).
#[inline(always)]
fn setae_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
set_reg64_help(0x93, buf, reg);
}
/// `SETLE r/m64` -> Set byte if less or equal (ZF=1 or SF≠ OF).
#[inline(always)]
fn setle_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
@ -3523,4 +3651,24 @@ mod tests {
fn test_push_reg64() {
disassembler_test!(push_reg64, |reg| format!("push {}", reg), ALL_GENERAL_REGS);
}
#[test]
fn test_sqrt_freg64_freg64() {
disassembler_test!(
sqrtsd_freg64_freg64,
|dst, src| format!("sqrtsd {dst}, {src}"),
ALL_FLOAT_REGS,
ALL_FLOAT_REGS
);
}
#[test]
fn test_sqrt_freg32_freg32() {
disassembler_test!(
sqrtss_freg32_freg32,
|dst, src| format!("sqrtss {dst}, {src}"),
ALL_FLOAT_REGS,
ALL_FLOAT_REGS
);
}
}

View file

@ -751,6 +751,30 @@ trait Backend<'a> {
);
self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0])
}
LowLevel::NumLogUnchecked => {
let float_width = match arg_layouts[0] {
Layout::F64 => FloatWidth::F64,
Layout::F32 => FloatWidth::F32,
_ => unreachable!("invalid layout for sqrt"),
};
self.build_fn_call(
sym,
bitcode::NUM_LOG[float_width].to_string(),
args,
arg_layouts,
ret_layout,
)
}
LowLevel::NumSqrtUnchecked => {
let float_width = match arg_layouts[0] {
Layout::F64 => FloatWidth::F64,
Layout::F32 => FloatWidth::F32,
_ => unreachable!("invalid layout for sqrt"),
};
self.build_num_sqrt(*sym, args[0], float_width);
}
LowLevel::NumRound => self.build_fn_call(
sym,
bitcode::NUM_ROUND_F64[IntWidth::I64].to_string(),
@ -1261,6 +1285,9 @@ trait Backend<'a> {
arg_layout: &InLayout<'a>,
);
/// build_sqrt stores the result of `sqrt(src)` into dst.
fn build_num_sqrt(&mut self, dst: Symbol, src: Symbol, float_width: FloatWidth);
/// build_list_len returns the length of a list.
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol);

View file

@ -19,11 +19,11 @@ fn nat_alias() {
assert_evals_to!(
indoc!(
r#"
i : Num.Nat
i = 1
i : Num.Nat
i = 1
i
"#
i
"#
),
1,
usize
@ -31,16 +31,16 @@ fn nat_alias() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn i128_signed_int_alias() {
assert_evals_to!(
indoc!(
r#"
i : I128
i = 128
i : I128
i = 128
i
"#
i
"#
),
128,
i128
@ -71,11 +71,11 @@ fn i32_signed_int_alias() {
assert_evals_to!(
indoc!(
r#"
i : I32
i = 32
i : I32
i = 32
i
"#
i
"#
),
32,
i32
@ -115,7 +115,7 @@ fn i8_signed_int_alias() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn i128_hex_int_alias() {
assert_evals_to!(
indoc!(
@ -196,7 +196,7 @@ fn i8_hex_int_alias() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn u128_signed_int_alias() {
assert_evals_to!(
indoc!(
@ -277,7 +277,7 @@ fn u8_signed_int_alias() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn u128_hex_int_alias() {
assert_evals_to!(
indoc!(
@ -418,7 +418,7 @@ fn character_literal_new_line() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn dec_float_alias() {
assert_evals_to!(
indoc!(
@ -451,7 +451,7 @@ fn f64_float_alias() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f32_float_alias() {
assert_evals_to!(
indoc!(
@ -468,112 +468,51 @@ fn f32_float_alias() {
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_sqrt() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrtChecked 100 is
Ok val -> val
Err _ -> -1
"#
),
10.0,
f64
);
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_sqrt_100() {
assert_evals_to!("Num.sqrt 100", 10.0, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_sqrt_checked_0() {
assert_evals_to!("Num.sqrt 0", 0.0, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log() {
assert_evals_to!(
indoc!(
r#"
Num.log 7.38905609893
"#
),
1.999999999999912,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_one() {
assert_evals_to!(
indoc!(
r#"
when Num.logChecked 1 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_sqrt_zero() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrtChecked 0 is
Ok val -> val
Err _ -> -1
"#
),
0.0,
f64
);
fn f64_sqrt_checked_positive() {
assert_evals_to!("Num.sqrtChecked 100", RocResult::ok(10.0), RocResult<f64, ()>);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_sqrt_checked_negative() {
assert_evals_to!(
indoc!(
r#"
when Num.sqrtChecked -1 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
assert_evals_to!("Num.sqrtChecked -1f64", RocResult::err(()), RocResult<f64, ()>);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn f64_log() {
assert_evals_to!("Num.log 7.38905609893", 1.999999999999912, f64);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_one() {
assert_evals_to!("Num.logChecked 1", RocResult::ok(0.0), RocResult<f64, ()>);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_checked_zero() {
assert_evals_to!(
indoc!(
r#"
when Num.logChecked 0 is
Err _ -> 42
Ok val -> val
"#
),
42.0,
f64
);
assert_evals_to!("Num.logChecked 0", RocResult::err(()), RocResult<f64, ()>);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn f64_log_negative() {
assert_evals_to!(
indoc!(
r#"
Num.log -1
"#
),
true,
f64,
|f: f64| f.is_nan()
);
assert_evals_to!("Num.log -1", true, f64, |f: f64| f.is_nan());
}
#[test]
@ -890,16 +829,20 @@ fn gen_int_neq() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn gen_int_less_than() {
assert_evals_to!(
indoc!(
r#"
4 < 5
"#
),
true,
bool
);
fn int_less_than() {
assert_evals_to!("4 < 5", true, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn float_less_than() {
assert_evals_to!("4.0 < 5.0", true, bool);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn float_greater_than() {
assert_evals_to!("5.0 > 4.0", true, bool);
}
#[test]

View file

@ -227,7 +227,7 @@ fn is_err() {
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_ok() {
fn roc_result_ok_i64() {
assert_evals_to!(
indoc!(
r#"
@ -242,6 +242,26 @@ fn roc_result_ok() {
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn roc_result_ok_f64() {
// NOTE: the dev backend does not currently use float registers when returning a more
// complex type, but the rust side does expect it to. Hence this test fails with gen-dev
assert_evals_to!(
indoc!(
r#"
result : Result F64 {}
result = Ok 42.0
result
"#
),
RocResult::ok(42.0),
RocResult<f64, ()>
);
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
fn roc_result_err() {