Skip to content

Commit 4bf225d

Browse files
committed
Auto merge of #145043 - Zalathar:rollup-3dbvdrm, r=Zalathar
Rollup of 19 pull requests Successful merges: - rust-lang/rust#137831 (Tweak auto trait errors) - rust-lang/rust#138689 (add nvptx_target_feature) - rust-lang/rust#140267 (implement continue_ok and break_ok for ControlFlow) - rust-lang/rust#143028 (emit `StorageLive` and schedule `StorageDead` for `let`-`else`'s bindings after matching) - rust-lang/rust#143764 (lower pattern bindings in the order they're written and base drop order on primary bindings' order) - rust-lang/rust#143808 (Port `#[should_panic]` to the new attribute parsing infrastructure ) - rust-lang/rust#143906 (Miri: non-deterministic floating point operations in `foreign_items`) - rust-lang/rust#143929 (Mark all deprecation lints in name resolution as deny-by-default and report-in-deps) - rust-lang/rust#144133 (Stabilize const TypeId::of) - rust-lang/rust#144369 (Upgrade semicolon_in_expressions_from_macros from warn to deny) - rust-lang/rust#144439 (Introduce ModernIdent type to unify macro 2.0 hygiene handling) - rust-lang/rust#144473 (Address libunwind.a inconsistency issues in the bootstrap program) - rust-lang/rust#144601 (Allow `cargo fix` to partially apply `mismatched_lifetime_syntaxes`) - rust-lang/rust#144650 (Additional tce tests) - rust-lang/rust#144659 (bootstrap: refactor mingw dist and fix gnullvm) - rust-lang/rust#144682 (Stabilize `strict_overflow_ops`) - rust-lang/rust#145026 (Update books) - rust-lang/rust#145033 (Reimplement `print_region` in `type_name.rs`.) - rust-lang/rust#145040 (rustc-dev-guide subtree update) Failed merges: - rust-lang/rust#143857 (Port #[macro_export] to the new attribute parsing infrastructure) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 28f4bd4 + 5c08889 commit 4bf225d

File tree

5 files changed

+514
-313
lines changed

5 files changed

+514
-313
lines changed

src/intrinsics/mod.rs

Lines changed: 32 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,16 @@
33
mod atomic;
44
mod simd;
55

6-
use std::ops::Neg;
7-
86
use rand::Rng;
97
use rustc_abi::Size;
10-
use rustc_apfloat::ieee::{IeeeFloat, Semantics};
118
use rustc_apfloat::{self, Float, Round};
129
use rustc_middle::mir;
13-
use rustc_middle::ty::{self, FloatTy, ScalarInt};
10+
use rustc_middle::ty::{self, FloatTy};
1411
use rustc_span::{Symbol, sym};
1512

1613
use self::atomic::EvalContextExt as _;
1714
use self::helpers::{ToHost, ToSoft};
1815
use self::simd::EvalContextExt as _;
19-
use crate::math::{IeeeExt, apply_random_float_error_ulp};
2016
use crate::*;
2117

2218
/// Check that the number of args is what we expect.
@@ -209,7 +205,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
209205
let [f] = check_intrinsic_arg_count(args)?;
210206
let f = this.read_scalar(f)?.to_f32()?;
211207

212-
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
208+
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
213209
// Using host floats (but it's fine, these operations do not have
214210
// guaranteed precision).
215211
let host = f.to_host();
@@ -227,15 +223,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
227223

228224
// Apply a relative error of 4ULP to introduce some non-determinism
229225
// simulating imprecise implementations and optimizations.
230-
let res = apply_random_float_error_ulp(
226+
let res = math::apply_random_float_error_ulp(
231227
this,
232228
res,
233229
2, // log2(4)
234230
);
235231

236232
// Clamp the result to the guaranteed range of this function according to the C standard,
237233
// if any.
238-
clamp_float_value(intrinsic_name, res)
234+
math::clamp_float_value(intrinsic_name, res)
239235
});
240236
let res = this.adjust_nan(res, &[f]);
241237
this.write_scalar(res, dest)?;
@@ -253,7 +249,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
253249
let [f] = check_intrinsic_arg_count(args)?;
254250
let f = this.read_scalar(f)?.to_f64()?;
255251

256-
let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
252+
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
257253
// Using host floats (but it's fine, these operations do not have
258254
// guaranteed precision).
259255
let host = f.to_host();
@@ -271,15 +267,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
271267

272268
// Apply a relative error of 4ULP to introduce some non-determinism
273269
// simulating imprecise implementations and optimizations.
274-
let res = apply_random_float_error_ulp(
270+
let res = math::apply_random_float_error_ulp(
275271
this,
276272
res,
277273
2, // log2(4)
278274
);
279275

280276
// Clamp the result to the guaranteed range of this function according to the C standard,
281277
// if any.
282-
clamp_float_value(intrinsic_name, res)
278+
math::clamp_float_value(intrinsic_name, res)
283279
});
284280
let res = this.adjust_nan(res, &[f]);
285281
this.write_scalar(res, dest)?;
@@ -330,16 +326,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
330326
let f1 = this.read_scalar(f1)?.to_f32()?;
331327
let f2 = this.read_scalar(f2)?.to_f32()?;
332328

333-
let res = 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();
329+
let res =
330+
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
331+
// Using host floats (but it's fine, this operation does not have guaranteed precision).
332+
let res = f1.to_host().powf(f2.to_host()).to_soft();
336333

337-
// Apply a relative error of 4ULP to introduce some non-determinism
338-
// simulating imprecise implementations and optimizations.
339-
apply_random_float_error_ulp(
340-
this, res, 2, // log2(4)
341-
)
342-
});
334+
// Apply a relative error of 4ULP to introduce some non-determinism
335+
// simulating imprecise implementations and optimizations.
336+
math::apply_random_float_error_ulp(
337+
this, res, 2, // log2(4)
338+
)
339+
});
343340
let res = this.adjust_nan(res, &[f1, f2]);
344341
this.write_scalar(res, dest)?;
345342
}
@@ -348,16 +345,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
348345
let f1 = this.read_scalar(f1)?.to_f64()?;
349346
let f2 = this.read_scalar(f2)?.to_f64()?;
350347

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

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

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

374372
// Apply a relative error of 4ULP to introduce some non-determinism
375373
// simulating imprecise implementations and optimizations.
376-
apply_random_float_error_ulp(
374+
math::apply_random_float_error_ulp(
377375
this, res, 2, // log2(4)
378376
)
379377
});
@@ -385,13 +383,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385383
let f = this.read_scalar(f)?.to_f64()?;
386384
let i = this.read_scalar(i)?.to_i32()?;
387385

388-
let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
386+
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
389387
// Using host floats (but it's fine, this operation does not have guaranteed precision).
390388
let res = f.to_host().powi(i).to_soft();
391389

392390
// Apply a relative error of 4ULP to introduce some non-determinism
393391
// simulating imprecise implementations and optimizations.
394-
apply_random_float_error_ulp(
392+
math::apply_random_float_error_ulp(
395393
this, res, 2, // log2(4)
396394
)
397395
});
@@ -448,7 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
448446
}
449447
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
450448
// due to optimizations.
451-
let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?;
449+
let res = math::apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?;
452450
this.write_immediate(*res, dest)?;
453451
}
454452

@@ -485,133 +483,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
485483
interp_ok(EmulateItemResult::NeedsReturn)
486484
}
487485
}
488-
489-
/// Applies a random ULP floating point error to `val` and returns the new value.
490-
/// So if you want an X ULP error, `ulp_exponent` should be log2(X).
491-
///
492-
/// Will fail if `val` is not a floating point number.
493-
fn apply_random_float_error_to_imm<'tcx>(
494-
ecx: &mut MiriInterpCx<'tcx>,
495-
val: ImmTy<'tcx>,
496-
ulp_exponent: u32,
497-
) -> InterpResult<'tcx, ImmTy<'tcx>> {
498-
let scalar = val.to_scalar_int()?;
499-
let res: ScalarInt = match val.layout.ty.kind() {
500-
ty::Float(FloatTy::F16) =>
501-
apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
502-
ty::Float(FloatTy::F32) =>
503-
apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
504-
ty::Float(FloatTy::F64) =>
505-
apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
506-
ty::Float(FloatTy::F128) =>
507-
apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
508-
_ => bug!("intrinsic called with non-float input type"),
509-
};
510-
511-
interp_ok(ImmTy::from_scalar_int(res, val.layout))
512-
}
513-
514-
/// For the intrinsics:
515-
/// - sinf32, sinf64
516-
/// - cosf32, cosf64
517-
/// - expf32, expf64, exp2f32, exp2f64
518-
/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
519-
/// - powf32, powf64
520-
///
521-
/// # Return
522-
///
523-
/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
524-
/// (specifically, C23 annex F.10) when given `args` as arguments. Outputs that are unaffected by a relative error
525-
/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
526-
/// implementation. Returns `None` if no specific value is guaranteed.
527-
///
528-
/// # Note
529-
///
530-
/// For `powf*` operations of the form:
531-
///
532-
/// - `(SNaN)^(±0)`
533-
/// - `1^(SNaN)`
534-
///
535-
/// The result is implementation-defined:
536-
/// - musl returns for both `1.0`
537-
/// - glibc returns for both `NaN`
538-
///
539-
/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
540-
/// and the C standard leaves behavior for SNaNs unspecified.
541-
///
542-
/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
543-
fn fixed_float_value<S: Semantics>(
544-
ecx: &mut MiriInterpCx<'_>,
545-
intrinsic_name: &str,
546-
args: &[IeeeFloat<S>],
547-
) -> Option<IeeeFloat<S>> {
548-
let one = IeeeFloat::<S>::one();
549-
Some(match (intrinsic_name, args) {
550-
// cos(+- 0) = 1
551-
("cosf32" | "cosf64", [input]) if input.is_zero() => one,
552-
553-
// e^0 = 1
554-
("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
555-
556-
// (-1)^(±INF) = 1
557-
("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
558-
559-
// 1^y = 1 for any y, even a NaN
560-
("powf32" | "powf64", [base, exp]) if *base == one => {
561-
let rng = ecx.machine.rng.get_mut();
562-
// SNaN exponents get special treatment: they might return 1, or a NaN.
563-
let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random();
564-
// Handle both the musl and glibc cases non-deterministically.
565-
if return_nan { ecx.generate_nan(args) } else { one }
566-
}
567-
568-
// x^(±0) = 1 for any x, even a NaN
569-
("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
570-
let rng = ecx.machine.rng.get_mut();
571-
// SNaN bases get special treatment: they might return 1, or a NaN.
572-
let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
573-
// Handle both the musl and glibc cases non-deterministically.
574-
if return_nan { ecx.generate_nan(args) } else { one }
575-
}
576-
577-
// There are a lot of cases for fixed outputs according to the C Standard, but these are
578-
// mainly INF or zero which are not affected by the applied error.
579-
_ => return None,
580-
})
581-
}
582-
583-
/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
584-
/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
585-
fn fixed_powi_float_value<S: Semantics>(
586-
ecx: &mut MiriInterpCx<'_>,
587-
base: IeeeFloat<S>,
588-
exp: i32,
589-
) -> Option<IeeeFloat<S>> {
590-
Some(match exp {
591-
0 => {
592-
let one = IeeeFloat::<S>::one();
593-
let rng = ecx.machine.rng.get_mut();
594-
let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
595-
// For SNaN treatment, we are consistent with `powf`above.
596-
// (We wouldn't have two, unlike powf all implementations seem to agree for powi,
597-
// but for now we are maximally conservative.)
598-
if return_nan { ecx.generate_nan(&[base]) } else { one }
599-
}
600-
601-
_ => return None,
602-
})
603-
}
604-
605-
/// Given an floating-point operation and a floating-point value, clamps the result to the output
606-
/// range of the given operation.
607-
fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> {
608-
match intrinsic_name {
609-
// sin and cos: [-1, 1]
610-
"sinf32" | "cosf32" | "sinf64" | "cosf64" =>
611-
val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()),
612-
// exp: [0, +INF]
613-
"expf32" | "exp2f32" | "expf64" | "exp2f64" =>
614-
IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO),
615-
_ => val,
616-
}
617-
}

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![cfg_attr(bootstrap, feature(strict_overflow_ops))]
12
#![feature(abort_unwind)]
23
#![feature(cfg_select)]
34
#![feature(rustc_private)]
@@ -11,13 +12,14 @@
1112
#![feature(variant_count)]
1213
#![feature(yeet_expr)]
1314
#![feature(nonzero_ops)]
14-
#![feature(strict_overflow_ops)]
1515
#![feature(pointer_is_aligned_to)]
1616
#![feature(ptr_metadata)]
1717
#![feature(unqualified_local_imports)]
1818
#![feature(derive_coerce_pointee)]
1919
#![feature(arbitrary_self_types)]
2020
#![feature(iter_advance_by)]
21+
#![feature(f16)]
22+
#![feature(f128)]
2123
// Configure clippy and other lints
2224
#![allow(
2325
clippy::collapsible_else_if,

0 commit comments

Comments
 (0)