Skip to content

Commit c5c4d5f

Browse files
authored
Audit for copysignf* (rust-lang#1249)
* Restore `copysign*` intrinsics * Add tests
1 parent ae2de35 commit c5c4d5f

File tree

3 files changed

+120
-2
lines changed

3 files changed

+120
-2
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,8 @@ impl<'tcx> GotocCtx<'tcx> {
491491
"copy_nonoverlapping" => unreachable!(
492492
"Expected `core::intrinsics::unreachable` to be handled by `StatementKind::CopyNonOverlapping`"
493493
),
494-
"copysignf32" => unstable_codegen!(codegen_simple_intrinsic!(Copysignf)),
495-
"copysignf64" => unstable_codegen!(codegen_simple_intrinsic!(Copysign)),
494+
"copysignf32" => codegen_simple_intrinsic!(Copysignf),
495+
"copysignf64" => codegen_simple_intrinsic!(Copysign),
496496
"cosf32" => codegen_simple_intrinsic!(Cosf),
497497
"cosf64" => codegen_simple_intrinsic!(Cos),
498498
"ctlz" => codegen_count_intrinsic!(ctlz, true),
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `copysignf32` returns a floating point value with the magnitude
5+
// of `mag` and the sign of `sgn`. There are two special cases to consider:
6+
// * If `mag` is NaN, then NaN with the sign of `sgn` is returned.
7+
// * If `sgn` is -0, the result is only negative if the implementation supports
8+
// the signed zero consistenly in arithmetic operations.
9+
#![feature(core_intrinsics)]
10+
11+
// NaN values in either `mag` or `sgn` can lead to spurious failures in these
12+
// harnesses, so they are excluded when needed.
13+
#[kani::proof]
14+
fn test_copysign() {
15+
let mag: f32 = kani::any();
16+
let sig: f32 = kani::any();
17+
kani::assume(!mag.is_nan());
18+
kani::assume(!sig.is_nan());
19+
20+
// Build the expected result
21+
let abs_mag = mag.abs();
22+
let expected_res = if sig.is_sign_positive() { abs_mag } else { -abs_mag };
23+
24+
let res = unsafe { std::intrinsics::copysignf32(mag, sig) };
25+
26+
// Compare against the expected result
27+
assert!(expected_res == res);
28+
}
29+
30+
#[kani::proof]
31+
fn test_copysign_mag_nan() {
32+
let mag: f32 = kani::any();
33+
let sig: f32 = kani::any();
34+
kani::assume(mag.is_nan());
35+
kani::assume(!sig.is_nan());
36+
37+
let res = unsafe { std::intrinsics::copysignf32(mag, sig) };
38+
39+
// Check the result is NaN with the expected sign
40+
if sig.is_sign_positive() {
41+
assert!(res.is_nan());
42+
assert!(res.is_sign_positive());
43+
} else {
44+
assert!(res.is_nan());
45+
assert!(res.is_sign_negative());
46+
}
47+
}
48+
49+
#[kani::proof]
50+
fn test_copysign_sig_neg_zero() {
51+
let mag: f32 = kani::any();
52+
let sig: f32 = -0.0;
53+
54+
let res = unsafe { std::intrinsics::copysignf32(mag, sig) };
55+
56+
// Check that the result is negative. This case is already included in the
57+
// general case, but we provide it for clarity.
58+
assert!(res.is_sign_negative());
59+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
// Checks that `copysignf64` returns a floating point value with the magnitude
5+
// of `mag` and the sign of `sgn`. There are two special cases to consider:
6+
// * If `mag` is NaN, then NaN with the sign of `sgn` is returned.
7+
// * If `sgn` is -0, the result is only negative if the implementation supports
8+
// the signed zero consistenly in arithmetic operations.
9+
#![feature(core_intrinsics)]
10+
11+
// NaN values in either `mag` or `sgn` can lead to spurious failures in these
12+
// harnesses, so they are excluded when needed.
13+
#[kani::proof]
14+
fn test_copysign() {
15+
let mag: f64 = kani::any();
16+
let sig: f64 = kani::any();
17+
kani::assume(!mag.is_nan());
18+
kani::assume(!sig.is_nan());
19+
20+
// Build the expected result
21+
let abs_mag = mag.abs();
22+
let expected_res = if sig.is_sign_positive() { abs_mag } else { -abs_mag };
23+
24+
let res = unsafe { std::intrinsics::copysignf64(mag, sig) };
25+
26+
// Compare against the expected result
27+
assert!(expected_res == res);
28+
}
29+
30+
#[kani::proof]
31+
fn test_copysign_mag_nan() {
32+
let mag: f64 = kani::any();
33+
let sig: f64 = kani::any();
34+
kani::assume(mag.is_nan());
35+
kani::assume(!sig.is_nan());
36+
37+
let res = unsafe { std::intrinsics::copysignf64(mag, sig) };
38+
39+
// Check the result is NaN with the expected sign
40+
if sig.is_sign_positive() {
41+
assert!(res.is_nan());
42+
assert!(res.is_sign_positive());
43+
} else {
44+
assert!(res.is_nan());
45+
assert!(res.is_sign_negative());
46+
}
47+
}
48+
49+
#[kani::proof]
50+
fn test_copysign_sig_neg_zero() {
51+
let mag: f64 = kani::any();
52+
let sig: f64 = -0.0;
53+
54+
let res = unsafe { std::intrinsics::copysignf64(mag, sig) };
55+
56+
// Check that the result is negative. This case is already included in the
57+
// general case, but we provide it for clarity.
58+
assert!(res.is_sign_negative());
59+
}

0 commit comments

Comments
 (0)