Skip to content

Commit 5d206d6

Browse files
authored
Audit for maxnumf* and minnumf* intrinsics (rust-lang#1248)
* Enable `maxnumfXX` and `minnumfXX` intrinsics * Add tests for minnum and maxnum * Assert against expected result instead of NaN check * Move expected check to `test_one_nan` in `minnumf64.rs`
1 parent c5c4d5f commit 5d206d6

File tree

5 files changed

+188
-4
lines changed

5 files changed

+188
-4
lines changed

kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,12 @@ impl<'tcx> GotocCtx<'tcx> {
548548
"log2f64" => unstable_codegen!(codegen_simple_intrinsic!(Log2)),
549549
"logf32" => unstable_codegen!(codegen_simple_intrinsic!(Logf)),
550550
"logf64" => unstable_codegen!(codegen_simple_intrinsic!(Log)),
551-
"maxnumf32" => unstable_codegen!(codegen_simple_intrinsic!(Fmaxf)),
552-
"maxnumf64" => unstable_codegen!(codegen_simple_intrinsic!(Fmax)),
551+
"maxnumf32" => codegen_simple_intrinsic!(Fmaxf),
552+
"maxnumf64" => codegen_simple_intrinsic!(Fmax),
553553
"min_align_of" => codegen_intrinsic_const!(),
554554
"min_align_of_val" => codegen_size_align!(align),
555-
"minnumf32" => unstable_codegen!(codegen_simple_intrinsic!(Fminf)),
556-
"minnumf64" => unstable_codegen!(codegen_simple_intrinsic!(Fmin)),
555+
"minnumf32" => codegen_simple_intrinsic!(Fminf),
556+
"minnumf64" => codegen_simple_intrinsic!(Fmin),
557557
"mul_with_overflow" => codegen_op_with_overflow!(mul_overflow),
558558
"nearbyintf32" => codegen_unimplemented_intrinsic!(
559559
"https://github.com/model-checking/kani/issues/1025"
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that `maxnumf32` returns the minimum of two values, except in the
5+
// following cases:
6+
// * If one of the arguments is NaN, the other arguments is returned.
7+
// * If both arguments are NaN, NaN is returned.
8+
#![feature(core_intrinsics)]
9+
10+
// Note: All test cases are failing with the following error:
11+
// ```
12+
// Check X: fmaxf.assertion.1
13+
// - Status: FAILURE
14+
// - Description: "Function with missing definition is unreachable"
15+
// - Location: <builtin-library-fmaxf> in function fmaxf
16+
// ```
17+
// This is because the `fmaxf` definition is not being found, either because
18+
// Kani does not produce the right expression (which is strange, because it's
19+
// doing the same for similar expressions and they work) or CBMC is not picking
20+
// it for some reason.
21+
// Tracked in https://github.com/model-checking/kani/issues/1025
22+
#[kani::proof]
23+
fn test_general() {
24+
let x: f32 = kani::any();
25+
let y: f32 = kani::any();
26+
kani::assume(!x.is_nan() && !y.is_nan());
27+
let res = std::intrinsics::maxnumf32(x, y);
28+
if x > y {
29+
assert!(res == x);
30+
} else {
31+
assert!(res == y);
32+
}
33+
}
34+
35+
#[kani::proof]
36+
fn test_one_nan() {
37+
let x: f32 = kani::any();
38+
let y: f32 = kani::any();
39+
kani::assume((x.is_nan() && !y.is_nan()) || (!x.is_nan() && y.is_nan()));
40+
let res = std::intrinsics::maxnumf32(x, y);
41+
if x.is_nan() {
42+
assert!(res == y);
43+
} else {
44+
assert!(res == x);
45+
}
46+
}
47+
48+
#[kani::proof]
49+
fn test_both_nan() {
50+
let x: f32 = kani::any();
51+
let y: f32 = kani::any();
52+
kani::assume(x.is_nan() && y.is_nan());
53+
let res = std::intrinsics::maxnumf32(x, y);
54+
assert!(res.is_nan());
55+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that `maxnumf64` returns the minimum of two values, except in the
5+
// following cases:
6+
// * If one of the arguments is NaN, the other arguments is returned.
7+
// * If both arguments are NaN, NaN is returned.
8+
#![feature(core_intrinsics)]
9+
10+
#[kani::proof]
11+
fn test_general() {
12+
let x: f64 = kani::any();
13+
let y: f64 = kani::any();
14+
kani::assume(!x.is_nan() && !y.is_nan());
15+
let res = std::intrinsics::maxnumf64(x, y);
16+
if x > y {
17+
assert!(res == x);
18+
} else {
19+
assert!(res == y);
20+
}
21+
}
22+
23+
#[kani::proof]
24+
fn test_one_nan() {
25+
let x: f64 = kani::any();
26+
let y: f64 = kani::any();
27+
kani::assume((x.is_nan() && !y.is_nan()) || (!x.is_nan() && y.is_nan()));
28+
let res = std::intrinsics::maxnumf64(x, y);
29+
if x.is_nan() {
30+
assert!(res == y);
31+
} else {
32+
assert!(res == x);
33+
}
34+
}
35+
36+
#[kani::proof]
37+
fn test_both_nan() {
38+
let x: f64 = kani::any();
39+
let y: f64 = kani::any();
40+
kani::assume(x.is_nan() && y.is_nan());
41+
let res = std::intrinsics::maxnumf64(x, y);
42+
assert!(res.is_nan());
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that `minnumf32` returns the minimum of two values, except in the
5+
// following cases:
6+
// * If one of the arguments is NaN, the other arguments is returned.
7+
// * If both arguments are NaN, NaN is returned.
8+
#![feature(core_intrinsics)]
9+
10+
#[kani::proof]
11+
fn test_general() {
12+
let x: f32 = kani::any();
13+
let y: f32 = kani::any();
14+
kani::assume(!x.is_nan() && !y.is_nan());
15+
let res = std::intrinsics::minnumf32(x, y);
16+
if x < y {
17+
assert!(res == x);
18+
} else {
19+
assert!(res == y);
20+
}
21+
}
22+
23+
#[kani::proof]
24+
fn test_one_nan() {
25+
let x: f32 = kani::any();
26+
let y: f32 = kani::any();
27+
kani::assume((x.is_nan() && !y.is_nan()) || (!x.is_nan() && y.is_nan()));
28+
let res = std::intrinsics::minnumf32(x, y);
29+
if x.is_nan() {
30+
assert!(res == y);
31+
} else {
32+
assert!(res == x);
33+
}
34+
}
35+
36+
#[kani::proof]
37+
fn test_both_nan() {
38+
let x: f32 = kani::any();
39+
let y: f32 = kani::any();
40+
kani::assume(x.is_nan() && y.is_nan());
41+
let res = std::intrinsics::minnumf32(x, y);
42+
assert!(res.is_nan());
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Check that `minnumf64` returns the minimum of two values, except in the
5+
// following cases:
6+
// * If one of the arguments is NaN, the other arguments is returned.
7+
// * If both arguments are NaN, NaN is returned.
8+
#![feature(core_intrinsics)]
9+
10+
#[kani::proof]
11+
fn test_general() {
12+
let x: f64 = kani::any();
13+
let y: f64 = kani::any();
14+
kani::assume(!x.is_nan() && !y.is_nan());
15+
let res = std::intrinsics::minnumf64(x, y);
16+
if x < y {
17+
assert!(res == x);
18+
} else {
19+
assert!(res == y);
20+
}
21+
}
22+
23+
#[kani::proof]
24+
fn test_one_nan() {
25+
let x: f64 = kani::any();
26+
let y: f64 = kani::any();
27+
kani::assume((x.is_nan() && !y.is_nan()) || (!x.is_nan() && y.is_nan()));
28+
let res = std::intrinsics::minnumf64(x, y);
29+
if x.is_nan() {
30+
assert!(res == y);
31+
} else {
32+
assert!(res == x);
33+
}
34+
}
35+
36+
#[kani::proof]
37+
fn test_both_nan() {
38+
let x: f64 = kani::any();
39+
let y: f64 = kani::any();
40+
kani::assume(x.is_nan() && y.is_nan());
41+
let res = std::intrinsics::minnumf64(x, y);
42+
assert!(res.is_nan());
43+
}

0 commit comments

Comments
 (0)