Skip to content

Commit 586a092

Browse files
move float nondet helpers to math.rs
1 parent e826837 commit 586a092

File tree

4 files changed

+292
-311
lines changed

4 files changed

+292
-311
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 3 additions & 250 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::num::NonZero;
2-
use std::ops::Neg;
32
use std::time::Duration;
43
use std::{cmp, iter};
54

6-
use rand::{Rng, RngCore};
5+
use rand::RngCore;
76
use rustc_abi::{Align, CanonAbi, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
87
use rustc_apfloat::Float;
9-
use rustc_apfloat::ieee::{Double, Half, IeeeFloat, Quad, Semantics, Single};
8+
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
109
use rustc_hir::Safety;
1110
use rustc_hir::def::{DefKind, Namespace};
1211
use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
@@ -15,13 +14,12 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1514
use rustc_middle::middle::dependency_format::Linkage;
1615
use rustc_middle::middle::exported_symbols::ExportedSymbol;
1716
use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
18-
use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, ScalarInt, Ty, TyCtxt, UintTy};
17+
use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy};
1918
use rustc_session::config::CrateType;
2019
use rustc_span::{Span, Symbol};
2120
use rustc_symbol_mangling::mangle_internal_symbol;
2221
use rustc_target::callconv::FnAbi;
2322

24-
use crate::math::{IeeeExt, apply_random_float_error_ulp};
2523
use crate::*;
2624

2725
/// Indicates which kind of access is being performed.
@@ -217,57 +215,6 @@ impl ToSoft for f32 {
217215
}
218216
}
219217

220-
/// Given a floating-point operation and a floating-point value, clamps the result to the output
221-
/// range of the given operation according to the C standard, if any.
222-
pub fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S>
223-
where
224-
IeeeFloat<S>: IeeeExt,
225-
{
226-
let zero = IeeeFloat::<S>::ZERO;
227-
let one = IeeeFloat::<S>::one();
228-
let two = IeeeFloat::<S>::two();
229-
let pi = IeeeFloat::<S>::pi();
230-
let pi_over_2 = (pi / two).value;
231-
232-
match intrinsic_name {
233-
// sin, cos, tanh: [-1, 1]
234-
#[rustfmt::skip]
235-
| "sinf32"
236-
| "sinf64"
237-
| "cosf32"
238-
| "cosf64"
239-
| "tanhf"
240-
| "tanh"
241-
=> val.clamp(one.neg(), one),
242-
243-
// exp: [0, +INF)
244-
"expf32" | "exp2f32" | "expf64" | "exp2f64" => val.maximum(zero),
245-
246-
// cosh: [1, +INF)
247-
"coshf" | "cosh" => val.maximum(one),
248-
249-
// acos: [0, π]
250-
"acosf" | "acos" => val.clamp(zero, pi),
251-
252-
// asin: [-π, +π]
253-
"asinf" | "asin" => val.clamp(pi.neg(), pi),
254-
255-
// atan: (-π/2, +π/2)
256-
"atanf" | "atan" => val.clamp(pi_over_2.neg(), pi_over_2),
257-
258-
// erfc: (-1, 1)
259-
"erff" | "erf" => val.clamp(one.neg(), one),
260-
261-
// erfc: (0, 2)
262-
"erfcf" | "erfc" => val.clamp(zero, two),
263-
264-
// atan2(y, x): arctan(y/x) in [−π, +π]
265-
"atan2f" | "atan2" => val.clamp(pi.neg(), pi),
266-
267-
_ => val,
268-
}
269-
}
270-
271218
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
272219
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
273220
/// Checks if the given crate/module exists.
@@ -1252,200 +1199,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
12521199
}
12531200
}
12541201

1255-
/// Applies a random ULP floating point error to `val` and returns the new value.
1256-
/// So if you want an X ULP error, `ulp_exponent` should be log2(X).
1257-
///
1258-
/// Will fail if `val` is not a floating point number.
1259-
fn apply_random_float_error_to_imm(
1260-
&mut self,
1261-
val: ImmTy<'tcx>,
1262-
ulp_exponent: u32,
1263-
) -> InterpResult<'tcx, ImmTy<'tcx>> {
1264-
let this = self.eval_context_mut();
1265-
let scalar = val.to_scalar_int()?;
1266-
let res: ScalarInt = match val.layout.ty.kind() {
1267-
ty::Float(FloatTy::F16) =>
1268-
apply_random_float_error_ulp(this, scalar.to_f16(), ulp_exponent).into(),
1269-
ty::Float(FloatTy::F32) =>
1270-
apply_random_float_error_ulp(this, scalar.to_f32(), ulp_exponent).into(),
1271-
ty::Float(FloatTy::F64) =>
1272-
apply_random_float_error_ulp(this, scalar.to_f64(), ulp_exponent).into(),
1273-
ty::Float(FloatTy::F128) =>
1274-
apply_random_float_error_ulp(this, scalar.to_f128(), ulp_exponent).into(),
1275-
_ => bug!("intrinsic called with non-float input type"),
1276-
};
1277-
1278-
interp_ok(ImmTy::from_scalar_int(res, val.layout))
1279-
}
1280-
1281-
/// For the intrinsics:
1282-
/// - sinf32, sinf64, sinhf, sinh
1283-
/// - cosf32, cosf64, coshf, cosh
1284-
/// - tanhf, tanh, atanf, atan, atan2f, atan2
1285-
/// - expf32, expf64, exp2f32, exp2f64
1286-
/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
1287-
/// - powf32, powf64
1288-
/// - erff, erf, erfcf, erfc
1289-
/// - hypotf, hypot
1290-
///
1291-
/// # Return
1292-
///
1293-
/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
1294-
/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
1295-
/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
1296-
/// implementation. Returns `None` if no specific value is guaranteed.
1297-
///
1298-
/// # Note
1299-
///
1300-
/// For `powf*` operations of the form:
1301-
///
1302-
/// - `(SNaN)^(±0)`
1303-
/// - `1^(SNaN)`
1304-
///
1305-
/// The result is implementation-defined:
1306-
/// - musl returns for both `1.0`
1307-
/// - glibc returns for both `NaN`
1308-
///
1309-
/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
1310-
/// and the C standard leaves behavior for SNaNs unspecified.
1311-
///
1312-
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
1313-
fn fixed_float_value<S: Semantics>(
1314-
&mut self,
1315-
intrinsic_name: &str,
1316-
args: &[IeeeFloat<S>],
1317-
) -> Option<IeeeFloat<S>>
1318-
where
1319-
IeeeFloat<S>: IeeeExt,
1320-
{
1321-
let this = self.eval_context_mut();
1322-
let one = IeeeFloat::<S>::one();
1323-
let two = IeeeFloat::<S>::two();
1324-
let three = IeeeFloat::<S>::three();
1325-
let pi = IeeeFloat::<S>::pi();
1326-
let pi_over_2 = (pi / two).value;
1327-
let pi_over_4 = (pi_over_2 / two).value;
1328-
1329-
Some(match (intrinsic_name, args) {
1330-
// cos(±0) and cosh(±0)= 1
1331-
("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one,
1332-
1333-
// e^0 = 1
1334-
("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
1335-
1336-
// tanh(±INF) = ±1
1337-
("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input),
1338-
1339-
// atan(±INF) = ±π/2
1340-
("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input),
1341-
1342-
// erf(±INF) = ±1
1343-
("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input),
1344-
1345-
// erfc(-INF) = 2
1346-
("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value,
1347-
1348-
// hypot(x, ±0) = abs(x), if x is not a NaN.
1349-
("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() =>
1350-
x.abs(),
1351-
1352-
// atan2(±0,−0) = ±π.
1353-
// atan2(±0, y) = ±π for y < 0.
1354-
// Must check for non NaN because `y.is_negative()` also applies to NaN.
1355-
("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) =>
1356-
pi.copy_sign(*x),
1357-
1358-
// atan2(±x,−∞) = ±π for finite x > 0.
1359-
("atan2f" | "atan2", [x, y])
1360-
if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() =>
1361-
pi.copy_sign(*x),
1362-
1363-
// atan2(x, ±0) = −π/2 for x < 0.
1364-
// atan2(x, ±0) = π/2 for x > 0.
1365-
// REVIEW: Above is from C23, do we simplify this to the following?
1366-
// atan2(±x, 0) = ±π/2 if x is non-zero
1367-
("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x),
1368-
1369-
//atan2(±∞, −∞) = ±3π/4
1370-
("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() =>
1371-
(pi_over_4 * three).value.copy_sign(*x),
1372-
1373-
//atan2(±∞, +∞) = ±π/4
1374-
("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() =>
1375-
pi_over_4.copy_sign(*x),
1376-
1377-
// atan2(±∞, y) returns ±π/2 for finite y.
1378-
("atan2f" | "atan2", [x, y])
1379-
if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) =>
1380-
pi_over_2.copy_sign(*x),
1381-
1382-
// (-1)^(±INF) = 1
1383-
("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
1384-
1385-
// 1^y = 1 for any y, even a NaN
1386-
("powf32" | "powf64", [base, exp]) if *base == one => {
1387-
let rng = this.machine.rng.get_mut();
1388-
// SNaN exponents get special treatment: they might return 1, or a NaN.
1389-
let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random();
1390-
// Handle both the musl and glibc cases non-deterministically.
1391-
if return_nan { this.generate_nan(args) } else { one }
1392-
}
1393-
1394-
// x^(±0) = 1 for any x, even a NaN
1395-
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
1396-
let rng = this.machine.rng.get_mut();
1397-
// SNaN bases get special treatment: they might return 1, or a NaN.
1398-
let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random();
1399-
// Handle both the musl and glibc cases non-deterministically.
1400-
if return_nan { this.generate_nan(args) } else { one }
1401-
}
1402-
1403-
// There are a lot of cases for fixed outputs according to the C Standard, but these are
1404-
// mainly INF or zero which are not affected by the applied error.
1405-
_ => return None,
1406-
})
1407-
}
1408-
1409-
/// # Return
1410-
///
1411-
/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
1412-
/// (specifically, C23 annex F.10) when given f (as a float) and int (as a i32) as arguments. Outputs that are
1413-
/// unaffected by a relative error (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
1414-
/// implementation. Returns `None` if no specific value is guaranteed.
1415-
///
1416-
/// # Note
1417-
///
1418-
/// For `powif*` operations of the form `(SNaN)^(±0)` we follow the same behaviour as `powf*`, see [`fixed_float_value`].
1419-
fn fixed_float_int_value<S: Semantics>(
1420-
&mut self,
1421-
operation: &str,
1422-
f: IeeeFloat<S>,
1423-
int: i32,
1424-
) -> Option<IeeeFloat<S>>
1425-
where
1426-
IeeeFloat<S>: IeeeExt,
1427-
{
1428-
let this = self.eval_context_mut();
1429-
1430-
Some(match (operation, f, int) {
1431-
("powif32" | "powif64", x, 0) => {
1432-
let one = IeeeFloat::<S>::one();
1433-
let rng = this.machine.rng.get_mut();
1434-
let return_nan = this.machine.float_nondet && rng.random() && x.is_signaling();
1435-
// For SNaN treatment, we are consistent with `powf`above.
1436-
// (We wouldn't have two, unlike powf all implementations seem to agree for powi,
1437-
// but for now we are maximally conservative.)
1438-
if return_nan { this.generate_nan(&[x]) } else { one }
1439-
}
1440-
1441-
// For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
1442-
// scalbn(x, 0) = x.
1443-
("_ldexp" | "ldexp" | "scalbn", x, 0) => x,
1444-
1445-
_ => return None,
1446-
})
1447-
}
1448-
14491202
/// Returns an integer type that is twice wide as `ty`
14501203
fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
14511204
let this = self.eval_context_ref();

0 commit comments

Comments
 (0)