From f98076ae88f295e01fc24c1bcbc9d306d2badae5 Mon Sep 17 00:00:00 2001 From: Afonso Bordado Date: Thu, 7 Jul 2022 00:43:54 +0100 Subject: [PATCH] cranelift: Implement float rounding operations (#4397) Implements the following operations on the interpreter: * `ceil` * `floor` * `nearest` * `trunc` --- cranelift/codegen/src/ir/immediates.rs | 40 ++++++ .../filetests/filetests/runtests/ceil.clif | 119 ++++++++++++++++++ .../filetests/filetests/runtests/floor.clif | 119 ++++++++++++++++++ .../filetests/filetests/runtests/nearest.clif | 119 ++++++++++++++++++ .../filetests/filetests/runtests/trunc.clif | 119 ++++++++++++++++++ cranelift/interpreter/src/step.rs | 8 +- cranelift/interpreter/src/value.rs | 20 +++ 7 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 cranelift/filetests/filetests/runtests/ceil.clif create mode 100644 cranelift/filetests/filetests/runtests/floor.clif create mode 100644 cranelift/filetests/filetests/runtests/nearest.clif create mode 100644 cranelift/filetests/filetests/runtests/trunc.clif diff --git a/cranelift/codegen/src/ir/immediates.rs b/cranelift/codegen/src/ir/immediates.rs index 2ed2bb884c..27108de5f7 100644 --- a/cranelift/codegen/src/ir/immediates.rs +++ b/cranelift/codegen/src/ir/immediates.rs @@ -800,6 +800,26 @@ impl Ieee32 { pub fn is_zero(&self) -> bool { self.as_f32() == 0.0 } + + /// Returns the smallest integer greater than or equal to `self`. + pub fn ceil(self) -> Self { + Self::with_float(self.as_f32().ceil()) + } + + /// Returns the largest integer less than or equal to `self`. + pub fn floor(self) -> Self { + Self::with_float(self.as_f32().floor()) + } + + /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero. + pub fn trunc(self) -> Self { + Self::with_float(self.as_f32().trunc()) + } + + /// Returns the nearest integer to `self`. Round half-way cases away from `0.0`. + pub fn nearest(self) -> Self { + Self::with_float(self.as_f32().round()) + } } impl PartialOrd for Ieee32 { @@ -929,6 +949,26 @@ impl Ieee64 { pub fn is_zero(&self) -> bool { self.as_f64() == 0.0 } + + /// Returns the smallest integer greater than or equal to `self`. + pub fn ceil(self) -> Self { + Self::with_float(self.as_f64().ceil()) + } + + /// Returns the largest integer less than or equal to `self`. + pub fn floor(self) -> Self { + Self::with_float(self.as_f64().floor()) + } + + /// Returns the integer part of `self`. This means that non-integer numbers are always truncated towards zero. + pub fn trunc(self) -> Self { + Self::with_float(self.as_f64().trunc()) + } + + /// Returns the nearest integer to `self`. Round half-way cases away from `0.0`. + pub fn nearest(self) -> Self { + Self::with_float(self.as_f64().round()) + } } impl PartialOrd for Ieee64 { diff --git a/cranelift/filetests/filetests/runtests/ceil.clif b/cranelift/filetests/filetests/runtests/ceil.clif new file mode 100644 index 0000000000..e7367550c1 --- /dev/null +++ b/cranelift/filetests/filetests/runtests/ceil.clif @@ -0,0 +1,119 @@ +test interpret +test run +target x86_64 +target aarch64 +target s390x + +function %ceil_f32(f32) -> f32 { +block0(v0: f32): + v1 = ceil v0 + return v1 +} +; run: %ceil_f32(0x0.5) == 0x1.0 +; run: %ceil_f32(0x1.0) == 0x1.0 +; run: %ceil_f32(0x1.5) == 0x1.0p1 +; run: %ceil_f32(0x2.9) == 0x1.8p1 +; run: %ceil_f32(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %ceil_f32(-0x0.5) == -0x0.0 +; run: %ceil_f32(-0x1.0) == -0x1.0 +; run: %ceil_f32(-0x1.5) == -0x1.0 +; run: %ceil_f32(-0x2.9) == -0x1.0p1 +; run: %ceil_f32(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %ceil_f32(0x0.0) == 0x0.0 +; run: %ceil_f32(-0x0.0) == -0x0.0 +; run: %ceil_f32(+Inf) == +Inf +; run: %ceil_f32(-Inf) == -Inf + +; F32 Epsilon / Max / Min Positive +; run: %ceil_f32(0x1.000000p-23) == 0x1.0 +; run: %ceil_f32(0x1.fffffep127) == 0x1.fffffep127 +; run: %ceil_f32(0x1.000000p-126) == 0x1.0 + +; F32 Subnormals +; run: %ceil_f32(0x0.800000p-126) == 0x1.0 +; run: %ceil_f32(-0x0.800002p-126) == -0x0.0 + +; F32 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %ceil_is_nan_f32(f32) -> i32 { +block0(v0: f32): + v1 = ceil v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %ceil_is_nan_f32(+NaN) == 1 +; run: %ceil_is_nan_f32(-NaN) == 1 +; run: %ceil_is_nan_f32(+NaN:0x0) == 1 +; run: %ceil_is_nan_f32(+NaN:0x1) == 1 +; run: %ceil_is_nan_f32(+NaN:0x300001) == 1 +; run: %ceil_is_nan_f32(-NaN:0x0) == 1 +; run: %ceil_is_nan_f32(-NaN:0x1) == 1 +; run: %ceil_is_nan_f32(-NaN:0x300001) == 1 +; run: %ceil_is_nan_f32(+sNaN:0x1) == 1 +; run: %ceil_is_nan_f32(-sNaN:0x1) == 1 +; run: %ceil_is_nan_f32(+sNaN:0x200001) == 1 +; run: %ceil_is_nan_f32(-sNaN:0x200001) == 1 + + + +function %ceil_f64(f64) -> f64 { +block0(v0: f64): + v1 = ceil v0 + return v1 +} +; run: %ceil_f64(0x0.5) == 0x1.0 +; run: %ceil_f64(0x1.0) == 0x1.0 +; run: %ceil_f64(0x1.5) == 0x1.0p1 +; run: %ceil_f64(0x2.9) == 0x1.8p1 +; run: %ceil_f64(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %ceil_f64(-0x0.5) == -0x0.0 +; run: %ceil_f64(-0x1.0) == -0x1.0 +; run: %ceil_f64(-0x1.5) == -0x1.0 +; run: %ceil_f64(-0x2.9) == -0x1.0p1 +; run: %ceil_f64(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %ceil_f64(0x0.0) == 0x0.0 +; run: %ceil_f64(-0x0.0) == -0x0.0 +; run: %ceil_f64(+Inf) == +Inf +; run: %ceil_f64(-Inf) == -Inf + +; F64 Epsilon / Max / Min Positive +; run: %ceil_f64(0x1.0000000000000p-52) == 0x1.0 +; run: %ceil_f64(0x1.fffffffffffffp1023) == 0x1.fffffffffffffp1023 +; run: %ceil_f64(0x1.0000000000000p-1022) == 0x1.0 + +; F64 Subnormals +; run: %ceil_f64(0x0.8000000000000p-1022) == 0x1.0 +; run: %ceil_f64(-0x0.8000000000000p-1022) == -0x0.0 + + + +; F64 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %ceil_is_nan_f64(f64) -> i32 { +block0(v0: f64): + v1 = ceil v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %ceil_is_nan_f64(+NaN) == 1 +; run: %ceil_is_nan_f64(-NaN) == 1 +; run: %ceil_is_nan_f64(+NaN:0x0) == 1 +; run: %ceil_is_nan_f64(+NaN:0x1) == 1 +; run: %ceil_is_nan_f64(+NaN:0x4000000000001) == 1 +; run: %ceil_is_nan_f64(-NaN:0x0) == 1 +; run: %ceil_is_nan_f64(-NaN:0x1) == 1 +; run: %ceil_is_nan_f64(-NaN:0x4000000000001) == 1 +; run: %ceil_is_nan_f64(+sNaN:0x1) == 1 +; run: %ceil_is_nan_f64(-sNaN:0x1) == 1 +; run: %ceil_is_nan_f64(+sNaN:0x4000000000001) == 1 +; run: %ceil_is_nan_f64(-sNaN:0x4000000000001) == 1 diff --git a/cranelift/filetests/filetests/runtests/floor.clif b/cranelift/filetests/filetests/runtests/floor.clif new file mode 100644 index 0000000000..6df5568834 --- /dev/null +++ b/cranelift/filetests/filetests/runtests/floor.clif @@ -0,0 +1,119 @@ +test interpret +test run +target x86_64 +target aarch64 +target s390x + +function %floor_f32(f32) -> f32 { +block0(v0: f32): + v1 = floor v0 + return v1 +} +; run: %floor_f32(0x0.5) == 0x0.0 +; run: %floor_f32(0x1.0) == 0x1.0 +; run: %floor_f32(0x1.5) == 0x1.0 +; run: %floor_f32(0x2.9) == 0x1.0p1 +; run: %floor_f32(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %floor_f32(-0x0.5) == -0x1.0 +; run: %floor_f32(-0x1.0) == -0x1.0 +; run: %floor_f32(-0x1.5) == -0x1.0p1 +; run: %floor_f32(-0x2.9) == -0x1.8p1 +; run: %floor_f32(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %floor_f32(0x0.0) == 0x0.0 +; run: %floor_f32(-0x0.0) == -0x0.0 +; run: %floor_f32(+Inf) == +Inf +; run: %floor_f32(-Inf) == -Inf + +; F32 Epsilon / Max / Min Positive +; run: %floor_f32(0x1.000000p-23) == 0x0.0 +; run: %floor_f32(0x1.fffffep127) == 0x1.fffffep127 +; run: %floor_f32(0x1.000000p-126) == 0x0.0 + +; F32 Subnormals +; run: %floor_f32(0x0.800000p-126) == 0x0.0 +; run: %floor_f32(-0x0.800002p-126) == -0x1.0 + +; F32 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %floor_is_nan_f32(f32) -> i32 { +block0(v0: f32): + v1 = floor v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %floor_is_nan_f32(+NaN) == 1 +; run: %floor_is_nan_f32(-NaN) == 1 +; run: %floor_is_nan_f32(+NaN:0x0) == 1 +; run: %floor_is_nan_f32(+NaN:0x1) == 1 +; run: %floor_is_nan_f32(+NaN:0x300001) == 1 +; run: %floor_is_nan_f32(-NaN:0x0) == 1 +; run: %floor_is_nan_f32(-NaN:0x1) == 1 +; run: %floor_is_nan_f32(-NaN:0x300001) == 1 +; run: %floor_is_nan_f32(+sNaN:0x1) == 1 +; run: %floor_is_nan_f32(-sNaN:0x1) == 1 +; run: %floor_is_nan_f32(+sNaN:0x200001) == 1 +; run: %floor_is_nan_f32(-sNaN:0x200001) == 1 + + + +function %floor_f64(f64) -> f64 { +block0(v0: f64): + v1 = floor v0 + return v1 +} +; run: %floor_f64(0x0.5) == 0x0.0 +; run: %floor_f64(0x1.0) == 0x1.0 +; run: %floor_f64(0x1.5) == 0x1.0 +; run: %floor_f64(0x2.9) == 0x1.0p1 +; run: %floor_f64(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %floor_f64(-0x0.5) == -0x1.0 +; run: %floor_f64(-0x1.0) == -0x1.0 +; run: %floor_f64(-0x1.5) == -0x1.0p1 +; run: %floor_f64(-0x2.9) == -0x1.8p1 +; run: %floor_f64(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %floor_f64(0x0.0) == 0x0.0 +; run: %floor_f64(-0x0.0) == -0x0.0 +; run: %floor_f64(+Inf) == +Inf +; run: %floor_f64(-Inf) == -Inf + +; F64 Epsilon / Max / Min Positive +; run: %floor_f64(0x1.0000000000000p-52) == 0x0.0 +; run: %floor_f64(0x1.fffffffffffffp1023) == 0x1.fffffffffffffp1023 +; run: %floor_f64(0x1.0000000000000p-1022) == 0x0.0 + +; F64 Subnormals +; run: %floor_f64(0x0.8000000000000p-1022) == 0x0.0 +; run: %floor_f64(-0x0.8000000000000p-1022) == -0x1.0 + + + +; F64 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %floor_is_nan_f64(f64) -> i32 { +block0(v0: f64): + v1 = floor v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %floor_is_nan_f64(+NaN) == 1 +; run: %floor_is_nan_f64(-NaN) == 1 +; run: %floor_is_nan_f64(+NaN:0x0) == 1 +; run: %floor_is_nan_f64(+NaN:0x1) == 1 +; run: %floor_is_nan_f64(+NaN:0x4000000000001) == 1 +; run: %floor_is_nan_f64(-NaN:0x0) == 1 +; run: %floor_is_nan_f64(-NaN:0x1) == 1 +; run: %floor_is_nan_f64(-NaN:0x4000000000001) == 1 +; run: %floor_is_nan_f64(+sNaN:0x1) == 1 +; run: %floor_is_nan_f64(-sNaN:0x1) == 1 +; run: %floor_is_nan_f64(+sNaN:0x4000000000001) == 1 +; run: %floor_is_nan_f64(-sNaN:0x4000000000001) == 1 diff --git a/cranelift/filetests/filetests/runtests/nearest.clif b/cranelift/filetests/filetests/runtests/nearest.clif new file mode 100644 index 0000000000..1268d75e72 --- /dev/null +++ b/cranelift/filetests/filetests/runtests/nearest.clif @@ -0,0 +1,119 @@ +test interpret +test run +target x86_64 +target aarch64 +target s390x + +function %nearest_f32(f32) -> f32 { +block0(v0: f32): + v1 = nearest v0 + return v1 +} +; run: %nearest_f32(0x0.5) == 0x0.0 +; run: %nearest_f32(0x1.0) == 0x1.0 +; run: %nearest_f32(0x1.5) == 0x1.0 +; run: %nearest_f32(0x2.9) == 0x1.8p1 +; run: %nearest_f32(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %nearest_f32(-0x0.5) == -0x0.0 +; run: %nearest_f32(-0x1.0) == -0x1.0 +; run: %nearest_f32(-0x1.5) == -0x1.0 +; run: %nearest_f32(-0x2.9) == -0x1.8p1 +; run: %nearest_f32(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %nearest_f32(0x0.0) == 0x0.0 +; run: %nearest_f32(-0x0.0) == -0x0.0 +; run: %nearest_f32(+Inf) == +Inf +; run: %nearest_f32(-Inf) == -Inf + +; F32 Epsilon / Max / Min Positive +; run: %nearest_f32(0x1.000000p-23) == 0x0.0 +; run: %nearest_f32(0x1.fffffep127) == 0x1.fffffep127 +; run: %nearest_f32(0x1.000000p-126) == 0x0.0 + +; F32 Subnormals +; run: %nearest_f32(0x0.800000p-126) == 0x0.0 +; run: %nearest_f32(-0x0.800002p-126) == -0x0.0 + +; F32 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %near_is_nan_f32(f32) -> i32 { +block0(v0: f32): + v1 = nearest v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %near_is_nan_f32(+NaN) == 1 +; run: %near_is_nan_f32(-NaN) == 1 +; run: %near_is_nan_f32(+NaN:0x0) == 1 +; run: %near_is_nan_f32(+NaN:0x1) == 1 +; run: %near_is_nan_f32(+NaN:0x300001) == 1 +; run: %near_is_nan_f32(-NaN:0x0) == 1 +; run: %near_is_nan_f32(-NaN:0x1) == 1 +; run: %near_is_nan_f32(-NaN:0x300001) == 1 +; run: %near_is_nan_f32(+sNaN:0x1) == 1 +; run: %near_is_nan_f32(-sNaN:0x1) == 1 +; run: %near_is_nan_f32(+sNaN:0x200001) == 1 +; run: %near_is_nan_f32(-sNaN:0x200001) == 1 + + + +function %nearest_f64(f64) -> f64 { +block0(v0: f64): + v1 = nearest v0 + return v1 +} +; run: %nearest_f64(0x0.5) == 0x0.0 +; run: %nearest_f64(0x1.0) == 0x1.0 +; run: %nearest_f64(0x1.5) == 0x1.0 +; run: %nearest_f64(0x2.9) == 0x1.8p1 +; run: %nearest_f64(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %nearest_f64(-0x0.5) == -0x0.0 +; run: %nearest_f64(-0x1.0) == -0x1.0 +; run: %nearest_f64(-0x1.5) == -0x1.0 +; run: %nearest_f64(-0x2.9) == -0x1.8p1 +; run: %nearest_f64(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %nearest_f64(0x0.0) == 0x0.0 +; run: %nearest_f64(-0x0.0) == -0x0.0 +; run: %nearest_f64(+Inf) == +Inf +; run: %nearest_f64(-Inf) == -Inf + +; F64 Epsilon / Max / Min Positive +; run: %nearest_f64(0x1.0000000000000p-52) == 0x0.0 +; run: %nearest_f64(0x1.fffffffffffffp1023) == 0x1.fffffffffffffp1023 +; run: %nearest_f64(0x1.0000000000000p-1022) == 0x0.0 + +; F64 Subnormals +; run: %nearest_f64(0x0.8000000000000p-1022) == 0x0.0 +; run: %nearest_f64(-0x0.8000000000000p-1022) == -0x0.0 + + + +; F64 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %near_is_nan_f64(f64) -> i32 { +block0(v0: f64): + v1 = nearest v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %near_is_nan_f64(+NaN) == 1 +; run: %near_is_nan_f64(-NaN) == 1 +; run: %near_is_nan_f64(+NaN:0x0) == 1 +; run: %near_is_nan_f64(+NaN:0x1) == 1 +; run: %near_is_nan_f64(+NaN:0x4000000000001) == 1 +; run: %near_is_nan_f64(-NaN:0x0) == 1 +; run: %near_is_nan_f64(-NaN:0x1) == 1 +; run: %near_is_nan_f64(-NaN:0x4000000000001) == 1 +; run: %near_is_nan_f64(+sNaN:0x1) == 1 +; run: %near_is_nan_f64(-sNaN:0x1) == 1 +; run: %near_is_nan_f64(+sNaN:0x4000000000001) == 1 +; run: %near_is_nan_f64(-sNaN:0x4000000000001) == 1 diff --git a/cranelift/filetests/filetests/runtests/trunc.clif b/cranelift/filetests/filetests/runtests/trunc.clif new file mode 100644 index 0000000000..a05ade2ec5 --- /dev/null +++ b/cranelift/filetests/filetests/runtests/trunc.clif @@ -0,0 +1,119 @@ +test interpret +test run +target x86_64 +target aarch64 +target s390x + +function %trunc_f32(f32) -> f32 { +block0(v0: f32): + v1 = trunc v0 + return v1 +} +; run: %trunc_f32(0x0.5) == 0x0.0 +; run: %trunc_f32(0x1.0) == 0x1.0 +; run: %trunc_f32(0x1.5) == 0x1.0 +; run: %trunc_f32(0x2.9) == 0x1.0p1 +; run: %trunc_f32(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %trunc_f32(-0x0.5) == -0x0.0 +; run: %trunc_f32(-0x1.0) == -0x1.0 +; run: %trunc_f32(-0x1.5) == -0x1.0 +; run: %trunc_f32(-0x2.9) == -0x1.0p1 +; run: %trunc_f32(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %trunc_f32(0x0.0) == 0x0.0 +; run: %trunc_f32(-0x0.0) == -0x0.0 +; run: %trunc_f32(+Inf) == +Inf +; run: %trunc_f32(-Inf) == -Inf + +; F32 Epsilon / Max / Min Positive +; run: %trunc_f32(0x1.000000p-23) == 0x0.0 +; run: %trunc_f32(0x1.fffffep127) == 0x1.fffffep127 +; run: %trunc_f32(0x1.000000p-126) == 0x0.0 + +; F32 Subnormals +; run: %trunc_f32(0x0.800000p-126) == 0x0.0 +; run: %trunc_f32(-0x0.800002p-126) == -0x0.0 + +; F32 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %trunc_is_nan_f32(f32) -> i32 { +block0(v0: f32): + v1 = trunc v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %trunc_is_nan_f32(+NaN) == 1 +; run: %trunc_is_nan_f32(-NaN) == 1 +; run: %trunc_is_nan_f32(+NaN:0x0) == 1 +; run: %trunc_is_nan_f32(+NaN:0x1) == 1 +; run: %trunc_is_nan_f32(+NaN:0x300001) == 1 +; run: %trunc_is_nan_f32(-NaN:0x0) == 1 +; run: %trunc_is_nan_f32(-NaN:0x1) == 1 +; run: %trunc_is_nan_f32(-NaN:0x300001) == 1 +; run: %trunc_is_nan_f32(+sNaN:0x1) == 1 +; run: %trunc_is_nan_f32(-sNaN:0x1) == 1 +; run: %trunc_is_nan_f32(+sNaN:0x200001) == 1 +; run: %trunc_is_nan_f32(-sNaN:0x200001) == 1 + + + +function %trunc_f64(f64) -> f64 { +block0(v0: f64): + v1 = trunc v0 + return v1 +} +; run: %trunc_f64(0x0.5) == 0x0.0 +; run: %trunc_f64(0x1.0) == 0x1.0 +; run: %trunc_f64(0x1.5) == 0x1.0 +; run: %trunc_f64(0x2.9) == 0x1.0p1 +; run: %trunc_f64(0x1.1p10) == 0x1.1p10 + +; Negatives +; run: %trunc_f64(-0x0.5) == -0x0.0 +; run: %trunc_f64(-0x1.0) == -0x1.0 +; run: %trunc_f64(-0x1.5) == -0x1.0 +; run: %trunc_f64(-0x2.9) == -0x1.0p1 +; run: %trunc_f64(-0x1.1p10) == -0x1.1p10 + +; Specials +; run: %trunc_f64(0x0.0) == 0x0.0 +; run: %trunc_f64(-0x0.0) == -0x0.0 +; run: %trunc_f64(+Inf) == +Inf +; run: %trunc_f64(-Inf) == -Inf + +; F64 Epsilon / Max / Min Positive +; run: %trunc_f64(0x1.0000000000000p-52) == 0x0.0 +; run: %trunc_f64(0x1.fffffffffffffp1023) == 0x1.fffffffffffffp1023 +; run: %trunc_f64(0x1.0000000000000p-1022) == 0x0.0 + +; F64 Subnormals +; run: %trunc_f64(0x0.8000000000000p-1022) == 0x0.0 +; run: %trunc_f64(-0x0.8000000000000p-1022) == -0x0.0 + + + +; F64 NaN's +; For NaN's this operation is specified as producing a value that is a NaN +function %trunc_is_nan_f64(f64) -> i32 { +block0(v0: f64): + v1 = trunc v0 + v2 = fcmp ne v1, v1 + v3 = bint.i32 v2 + return v3 +} +; run: %trunc_is_nan_f64(+NaN) == 1 +; run: %trunc_is_nan_f64(-NaN) == 1 +; run: %trunc_is_nan_f64(+NaN:0x0) == 1 +; run: %trunc_is_nan_f64(+NaN:0x1) == 1 +; run: %trunc_is_nan_f64(+NaN:0x4000000000001) == 1 +; run: %trunc_is_nan_f64(-NaN:0x0) == 1 +; run: %trunc_is_nan_f64(-NaN:0x1) == 1 +; run: %trunc_is_nan_f64(-NaN:0x4000000000001) == 1 +; run: %trunc_is_nan_f64(+sNaN:0x1) == 1 +; run: %trunc_is_nan_f64(-sNaN:0x1) == 1 +; run: %trunc_is_nan_f64(+sNaN:0x4000000000001) == 1 +; run: %trunc_is_nan_f64(-sNaN:0x4000000000001) == 1 diff --git a/cranelift/interpreter/src/step.rs b/cranelift/interpreter/src/step.rs index 84c9f50b9c..df8e25530c 100644 --- a/cranelift/interpreter/src/step.rs +++ b/cranelift/interpreter/src/step.rs @@ -728,10 +728,10 @@ where (a, b) if a.is_zero()? && b.is_zero()? => a, (a, b) => a.max(b)?, }), - Opcode::Ceil => unimplemented!("Ceil"), - Opcode::Floor => unimplemented!("Floor"), - Opcode::Trunc => unimplemented!("Trunc"), - Opcode::Nearest => unimplemented!("Nearest"), + Opcode::Ceil => assign(Value::ceil(arg(0)?)?), + Opcode::Floor => assign(Value::floor(arg(0)?)?), + Opcode::Trunc => assign(Value::trunc(arg(0)?)?), + Opcode::Nearest => assign(Value::nearest(arg(0)?)?), Opcode::IsNull => unimplemented!("IsNull"), Opcode::IsInvalid => unimplemented!("IsInvalid"), Opcode::Trueif => choose( diff --git a/cranelift/interpreter/src/value.rs b/cranelift/interpreter/src/value.rs index fde4659bda..1a5db35395 100644 --- a/cranelift/interpreter/src/value.rs +++ b/cranelift/interpreter/src/value.rs @@ -60,6 +60,10 @@ pub trait Value: Clone + From { // Float operations fn neg(self) -> ValueResult; fn copysign(self, sign: Self) -> ValueResult; + fn ceil(self) -> ValueResult; + fn floor(self) -> ValueResult; + fn trunc(self) -> ValueResult; + fn nearest(self) -> ValueResult; // Saturating arithmetic. fn add_sat(self, other: Self) -> ValueResult; @@ -517,6 +521,22 @@ impl Value for DataValue { binary_match!(copysign(&self, &sign); [F32, F64]) } + fn ceil(self) -> ValueResult { + unary_match!(ceil(&self); [F32, F64]) + } + + fn floor(self) -> ValueResult { + unary_match!(floor(&self); [F32, F64]) + } + + fn trunc(self) -> ValueResult { + unary_match!(trunc(&self); [F32, F64]) + } + + fn nearest(self) -> ValueResult { + unary_match!(nearest(&self); [F32, F64]) + } + fn add_sat(self, other: Self) -> ValueResult { binary_match!(saturating_add(self, &other); [I8, I16, I32, I64, I128, U8, U16, U32, U64, U128]) }