Skip to content

Commit f4c4fa1

Browse files
committed
Merge ref 'a09fbe2c8372' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref: a09fbe2c8372643a27a8082236120f95ed4e6bba Filtered ref: e8da14f This merge was created using https://github.com/rust-lang/josh-sync.
2 parents 9df8ba3 + e8da14f commit f4c4fa1

File tree

4 files changed

+489
-278
lines changed

4 files changed

+489
-278
lines changed

src/intrinsics/mod.rs

Lines changed: 27 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ mod simd;
66
pub use self::atomic::AtomicRmwOp;
77

88
#[rustfmt::skip] // prevent `use` reordering
9-
use std::ops::Neg;
10-
119
use rand::Rng;
1210
use rustc_abi::Size;
13-
use rustc_apfloat::ieee::{IeeeFloat, Semantics};
1411
use rustc_apfloat::{self, Float, Round};
1512
use rustc_middle::mir;
1613
use rustc_middle::ty::{self, FloatTy};
@@ -19,7 +16,6 @@ use rustc_span::{Symbol, sym};
1916
use self::atomic::EvalContextExt as _;
2017
use self::helpers::{ToHost, ToSoft};
2118
use self::simd::EvalContextExt as _;
22-
use crate::math::{IeeeExt, apply_random_float_error_ulp};
2319
use crate::*;
2420

2521
/// Check that the number of args is what we expect.
@@ -212,7 +208,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
212208
let [f] = check_intrinsic_arg_count(args)?;
213209
let f = this.read_scalar(f)?.to_f32()?;
214210

215-
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
211+
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
216212
// Using host floats (but it's fine, these operations do not have
217213
// guaranteed precision).
218214
let host = f.to_host();
@@ -230,15 +226,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
230226

231227
// Apply a relative error of 4ULP to introduce some non-determinism
232228
// simulating imprecise implementations and optimizations.
233-
let res = apply_random_float_error_ulp(
229+
let res = math::apply_random_float_error_ulp(
234230
this,
235231
res,
236232
4,
237233
);
238234

239235
// Clamp the result to the guaranteed range of this function according to the C standard,
240236
// if any.
241-
clamp_float_value(intrinsic_name, res)
237+
math::clamp_float_value(intrinsic_name, res)
242238
});
243239
let res = this.adjust_nan(res, &[f]);
244240
this.write_scalar(res, dest)?;
@@ -256,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
256252
let [f] = check_intrinsic_arg_count(args)?;
257253
let f = this.read_scalar(f)?.to_f64()?;
258254

259-
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
255+
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
260256
// Using host floats (but it's fine, these operations do not have
261257
// guaranteed precision).
262258
let host = f.to_host();
@@ -274,15 +270,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
274270

275271
// Apply a relative error of 4ULP to introduce some non-determinism
276272
// simulating imprecise implementations and optimizations.
277-
let res = apply_random_float_error_ulp(
273+
let res = math::apply_random_float_error_ulp(
278274
this,
279275
res,
280276
4,
281277
);
282278

283279
// Clamp the result to the guaranteed range of this function according to the C standard,
284280
// if any.
285-
clamp_float_value(intrinsic_name, res)
281+
math::clamp_float_value(intrinsic_name, res)
286282
});
287283
let res = this.adjust_nan(res, &[f]);
288284
this.write_scalar(res, dest)?;
@@ -333,14 +329,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
333329
let f1 = this.read_scalar(f1)?.to_f32()?;
334330
let f2 = this.read_scalar(f2)?.to_f32()?;
335331

336-
let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
337-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
338-
let res = f1.to_host().powf(f2.to_host()).to_soft();
332+
let res =
333+
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
334+
// Using host floats (but it's fine, this operation does not have guaranteed precision).
335+
let res = f1.to_host().powf(f2.to_host()).to_soft();
339336

340-
// Apply a relative error of 4ULP to introduce some non-determinism
341-
// simulating imprecise implementations and optimizations.
342-
apply_random_float_error_ulp(this, res, 4)
343-
});
337+
// Apply a relative error of 4ULP to introduce some non-determinism
338+
// simulating imprecise implementations and optimizations.
339+
math::apply_random_float_error_ulp(this, res, 4)
340+
});
344341
let res = this.adjust_nan(res, &[f1, f2]);
345342
this.write_scalar(res, dest)?;
346343
}
@@ -349,14 +346,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
349346
let f1 = this.read_scalar(f1)?.to_f64()?;
350347
let f2 = this.read_scalar(f2)?.to_f64()?;
351348

352-
let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
353-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
354-
let res = f1.to_host().powf(f2.to_host()).to_soft();
349+
let res =
350+
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
351+
// Using host floats (but it's fine, this operation does not have guaranteed precision).
352+
let res = f1.to_host().powf(f2.to_host()).to_soft();
355353

356-
// Apply a relative error of 4ULP to introduce some non-determinism
357-
// simulating imprecise implementations and optimizations.
358-
apply_random_float_error_ulp(this, res, 4)
359-
});
354+
// Apply a relative error of 4ULP to introduce some non-determinism
355+
// simulating imprecise implementations and optimizations.
356+
math::apply_random_float_error_ulp(this, res, 4)
357+
});
360358
let res = this.adjust_nan(res, &[f1, f2]);
361359
this.write_scalar(res, dest)?;
362360
}
@@ -366,13 +364,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
366364
let f = this.read_scalar(f)?.to_f32()?;
367365
let i = this.read_scalar(i)?.to_i32()?;
368366

369-
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
367+
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
370368
// Using host floats (but it's fine, this operation does not have guaranteed precision).
371369
let res = f.to_host().powi(i).to_soft();
372370

373371
// Apply a relative error of 4ULP to introduce some non-determinism
374372
// simulating imprecise implementations and optimizations.
375-
apply_random_float_error_ulp(this, res, 4)
373+
math::apply_random_float_error_ulp(this, res, 4)
376374
});
377375
let res = this.adjust_nan(res, &[f]);
378376
this.write_scalar(res, dest)?;
@@ -382,13 +380,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
382380
let f = this.read_scalar(f)?.to_f64()?;
383381
let i = this.read_scalar(i)?.to_i32()?;
384382

385-
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
383+
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
386384
// Using host floats (but it's fine, this operation does not have guaranteed precision).
387385
let res = f.to_host().powi(i).to_soft();
388386

389387
// Apply a relative error of 4ULP to introduce some non-determinism
390388
// simulating imprecise implementations and optimizations.
391-
apply_random_float_error_ulp(this, res, 4)
389+
math::apply_random_float_error_ulp(this, res, 4)
392390
});
393391
let res = this.adjust_nan(res, &[f]);
394392
this.write_scalar(res, dest)?;
@@ -443,7 +441,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
443441
}
444442
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
445443
// due to optimizations.
446-
let res = crate::math::apply_random_float_error_to_imm(this, res, 4)?;
444+
let res = math::apply_random_float_error_to_imm(this, res, 4)?;
447445
this.write_immediate(*res, dest)?;
448446
}
449447

@@ -480,108 +478,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
480478
interp_ok(EmulateItemResult::NeedsReturn)
481479
}
482480
}
483-
484-
/// For the intrinsics:
485-
/// - sinf32, sinf64
486-
/// - cosf32, cosf64
487-
/// - expf32, expf64, exp2f32, exp2f64
488-
/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
489-
/// - powf32, powf64
490-
///
491-
/// # Return
492-
///
493-
/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
494-
/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
495-
/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
496-
/// implementation. Returns `None` if no specific value is guaranteed.
497-
///
498-
/// # Note
499-
///
500-
/// For `powf*` operations of the form:
501-
///
502-
/// - `(SNaN)^(±0)`
503-
/// - `1^(SNaN)`
504-
///
505-
/// The result is implementation-defined:
506-
/// - musl returns for both `1.0`
507-
/// - glibc returns for both `NaN`
508-
///
509-
/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
510-
/// and the C standard leaves behavior for SNaNs unspecified.
511-
///
512-
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
513-
fn fixed_float_value<S: Semantics>(
514-
ecx: &mut MiriInterpCx<'_>,
515-
intrinsic_name: &str,
516-
args: &[IeeeFloat<S>],
517-
) -> Option<IeeeFloat<S>> {
518-
let one = IeeeFloat::<S>::one();
519-
Some(match (intrinsic_name, args) {
520-
// cos(+- 0) = 1
521-
("cosf32" | "cosf64", [input]) if input.is_zero() => one,
522-
523-
// e^0 = 1
524-
("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
525-
526-
// (-1)^(±INF) = 1
527-
("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
528-
529-
// 1^y = 1 for any y, even a NaN
530-
("powf32" | "powf64", [base, exp]) if *base == one => {
531-
let rng = ecx.machine.rng.get_mut();
532-
// SNaN exponents get special treatment: they might return 1, or a NaN.
533-
let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random();
534-
// Handle both the musl and glibc cases non-deterministically.
535-
if return_nan { ecx.generate_nan(args) } else { one }
536-
}
537-
538-
// x^(±0) = 1 for any x, even a NaN
539-
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
540-
let rng = ecx.machine.rng.get_mut();
541-
// SNaN bases get special treatment: they might return 1, or a NaN.
542-
let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
543-
// Handle both the musl and glibc cases non-deterministically.
544-
if return_nan { ecx.generate_nan(args) } else { one }
545-
}
546-
547-
// There are a lot of cases for fixed outputs according to the C Standard, but these are
548-
// mainly INF or zero which are not affected by the applied error.
549-
_ => return None,
550-
})
551-
}
552-
553-
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
554-
/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
555-
fn fixed_powi_float_value<S: Semantics>(
556-
ecx: &mut MiriInterpCx<'_>,
557-
base: IeeeFloat<S>,
558-
exp: i32,
559-
) -> Option<IeeeFloat<S>> {
560-
Some(match exp {
561-
0 => {
562-
let one = IeeeFloat::<S>::one();
563-
let rng = ecx.machine.rng.get_mut();
564-
let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
565-
// For SNaN treatment, we are consistent with `powf`above.
566-
// (We wouldn't have two, unlike powf all implementations seem to agree for powi,
567-
// but for now we are maximally conservative.)
568-
if return_nan { ecx.generate_nan(&[base]) } else { one }
569-
}
570-
571-
_ => return None,
572-
})
573-
}
574-
575-
/// Given an floating-point operation and a floating-point value, clamps the result to the output
576-
/// range of the given operation.
577-
fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> {
578-
match intrinsic_name {
579-
// sin and cos: [-1, 1]
580-
"sinf32" | "cosf32" | "sinf64" | "cosf64" =>
581-
val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()),
582-
// exp: [0, +INF]
583-
"expf32" | "exp2f32" | "expf64" | "exp2f64" =>
584-
IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO),
585-
_ => val,
586-
}
587-
}

0 commit comments

Comments
 (0)