Skip to content

Commit 86198a1

Browse files
committed
make float intrinsics return non-deterministic NaN
1 parent dae48ea commit 86198a1

File tree

4 files changed

+238
-75
lines changed

4 files changed

+238
-75
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::time::Duration;
66
use log::trace;
77

88
use rustc_apfloat::ieee::{Double, Single};
9+
use rustc_apfloat::Float;
910
use rustc_hir::def::{DefKind, Namespace};
1011
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
1112
use rustc_index::IndexVec;
@@ -117,6 +118,50 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>)
117118
}
118119
}
119120

121+
/// Convert a softfloat type to its corresponding hostfloat type.
122+
pub trait ToHost {
123+
type HostFloat;
124+
fn to_host(self) -> Self::HostFloat;
125+
}
126+
127+
/// Convert a hostfloat type to its corresponding softfloat type.
128+
pub trait ToSoft {
129+
type SoftFloat;
130+
fn to_soft(self) -> Self::SoftFloat;
131+
}
132+
133+
impl ToHost for rustc_apfloat::ieee::Double {
134+
type HostFloat = f64;
135+
136+
fn to_host(self) -> Self::HostFloat {
137+
f64::from_bits(self.to_bits().try_into().unwrap())
138+
}
139+
}
140+
141+
impl ToSoft for f64 {
142+
type SoftFloat = rustc_apfloat::ieee::Double;
143+
144+
fn to_soft(self) -> Self::SoftFloat {
145+
Float::from_bits(self.to_bits().into())
146+
}
147+
}
148+
149+
impl ToHost for rustc_apfloat::ieee::Single {
150+
type HostFloat = f32;
151+
152+
fn to_host(self) -> Self::HostFloat {
153+
f32::from_bits(self.to_bits().try_into().unwrap())
154+
}
155+
}
156+
157+
impl ToSoft for f32 {
158+
type SoftFloat = rustc_apfloat::ieee::Single;
159+
160+
fn to_soft(self) -> Self::SoftFloat {
161+
Float::from_bits(self.to_bits().into())
162+
}
163+
}
164+
120165
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
121166
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
122167
/// Checks if the given crate/module exists.

src/tools/miri/src/operator.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
118118
nan
119119
}
120120
}
121+
122+
fn adjust_nan<F1: Float + FloatConvert<F2>, F2: Float>(&self, f: F2, inputs: &[F1]) -> F2 {
123+
if f.is_nan() { self.generate_nan(inputs) } else { f }
124+
}
121125
}

src/tools/miri/src/shims/intrinsics/mod.rs

Lines changed: 91 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_target::abi::Size;
1515

1616
use crate::*;
1717
use atomic::EvalContextExt as _;
18-
use helpers::check_arg_count;
18+
use helpers::{check_arg_count, ToHost, ToSoft};
1919
use simd::EvalContextExt as _;
2020

2121
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
@@ -146,12 +146,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
146146
let [f] = check_arg_count(args)?;
147147
let f = this.read_scalar(f)?.to_f32()?;
148148
// Can be implemented in soft-floats.
149+
// This is a "bitwise" operation, so there's no NaN non-determinism.
149150
this.write_scalar(Scalar::from_f32(f.abs()), dest)?;
150151
}
151152
"fabsf64" => {
152153
let [f] = check_arg_count(args)?;
153154
let f = this.read_scalar(f)?.to_f64()?;
154155
// Can be implemented in soft-floats.
156+
// This is a "bitwise" operation, so there's no NaN non-determinism.
155157
this.write_scalar(Scalar::from_f64(f.abs()), dest)?;
156158
}
157159
#[rustfmt::skip]
@@ -170,25 +172,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
170172
| "rintf32"
171173
=> {
172174
let [f] = check_arg_count(args)?;
175+
let f = this.read_scalar(f)?.to_f32()?;
173176
// FIXME: Using host floats.
174-
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
175-
let f = match intrinsic_name {
176-
"sinf32" => f.sin(),
177-
"cosf32" => f.cos(),
178-
"sqrtf32" => f.sqrt(),
179-
"expf32" => f.exp(),
180-
"exp2f32" => f.exp2(),
181-
"logf32" => f.ln(),
182-
"log10f32" => f.log10(),
183-
"log2f32" => f.log2(),
184-
"floorf32" => f.floor(),
185-
"ceilf32" => f.ceil(),
186-
"truncf32" => f.trunc(),
187-
"roundf32" => f.round(),
188-
"rintf32" => f.round_ties_even(),
177+
let f_host = f.to_host();
178+
let res = match intrinsic_name {
179+
"sinf32" => f_host.sin(),
180+
"cosf32" => f_host.cos(),
181+
"sqrtf32" => f_host.sqrt(),
182+
"expf32" => f_host.exp(),
183+
"exp2f32" => f_host.exp2(),
184+
"logf32" => f_host.ln(),
185+
"log10f32" => f_host.log10(),
186+
"log2f32" => f_host.log2(),
187+
"floorf32" => f_host.floor(),
188+
"ceilf32" => f_host.ceil(),
189+
"truncf32" => f_host.trunc(),
190+
"roundf32" => f_host.round(),
191+
"rintf32" => f_host.round_ties_even(),
189192
_ => bug!(),
190193
};
191-
this.write_scalar(Scalar::from_u32(f.to_bits()), dest)?;
194+
let res = res.to_soft();
195+
let res = this.adjust_nan(res, &[f]);
196+
this.write_scalar(res, dest)?;
192197
}
193198

194199
#[rustfmt::skip]
@@ -207,25 +212,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
207212
| "rintf64"
208213
=> {
209214
let [f] = check_arg_count(args)?;
215+
let f = this.read_scalar(f)?.to_f64()?;
210216
// FIXME: Using host floats.
211-
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
212-
let f = match intrinsic_name {
213-
"sinf64" => f.sin(),
214-
"cosf64" => f.cos(),
215-
"sqrtf64" => f.sqrt(),
216-
"expf64" => f.exp(),
217-
"exp2f64" => f.exp2(),
218-
"logf64" => f.ln(),
219-
"log10f64" => f.log10(),
220-
"log2f64" => f.log2(),
221-
"floorf64" => f.floor(),
222-
"ceilf64" => f.ceil(),
223-
"truncf64" => f.trunc(),
224-
"roundf64" => f.round(),
225-
"rintf64" => f.round_ties_even(),
217+
let f_host = f.to_host();
218+
let res = match intrinsic_name {
219+
"sinf64" => f_host.sin(),
220+
"cosf64" => f_host.cos(),
221+
"sqrtf64" => f_host.sqrt(),
222+
"expf64" => f_host.exp(),
223+
"exp2f64" => f_host.exp2(),
224+
"logf64" => f_host.ln(),
225+
"log10f64" => f_host.log10(),
226+
"log2f64" => f_host.log2(),
227+
"floorf64" => f_host.floor(),
228+
"ceilf64" => f_host.ceil(),
229+
"truncf64" => f_host.trunc(),
230+
"roundf64" => f_host.round(),
231+
"rintf64" => f_host.round_ties_even(),
226232
_ => bug!(),
227233
};
228-
this.write_scalar(Scalar::from_u64(f.to_bits()), dest)?;
234+
let res = res.to_soft();
235+
let res = this.adjust_nan(res, &[f]);
236+
this.write_scalar(res, dest)?;
229237
}
230238

231239
#[rustfmt::skip]
@@ -272,6 +280,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
272280
if !float_finite(&res)? {
273281
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
274282
}
283+
// This cannot be a NaN so we also don't have to apply any non-determinism.
284+
// (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
275285
this.write_immediate(*res, dest)?;
276286
}
277287

@@ -284,9 +294,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
284294
let a = this.read_scalar(a)?.to_f32()?;
285295
let b = this.read_scalar(b)?.to_f32()?;
286296
let res = match intrinsic_name {
287-
"minnumf32" => a.min(b),
288-
"maxnumf32" => a.max(b),
289-
"copysignf32" => a.copy_sign(b),
297+
"minnumf32" => this.adjust_nan(a.min(b), &[a, b]),
298+
"maxnumf32" => this.adjust_nan(a.max(b), &[a, b]),
299+
"copysignf32" => a.copy_sign(b), // bitwise, no NaN adjustments
290300
_ => bug!(),
291301
};
292302
this.write_scalar(Scalar::from_f32(res), dest)?;
@@ -301,68 +311,74 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
301311
let a = this.read_scalar(a)?.to_f64()?;
302312
let b = this.read_scalar(b)?.to_f64()?;
303313
let res = match intrinsic_name {
304-
"minnumf64" => a.min(b),
305-
"maxnumf64" => a.max(b),
306-
"copysignf64" => a.copy_sign(b),
314+
"minnumf64" => this.adjust_nan(a.min(b), &[a, b]),
315+
"maxnumf64" => this.adjust_nan(a.max(b), &[a, b]),
316+
"copysignf64" => a.copy_sign(b), // bitwise, no NaN adjustments
307317
_ => bug!(),
308318
};
309319
this.write_scalar(Scalar::from_f64(res), dest)?;
310320
}
311321

312-
"powf32" => {
313-
let [f, f2] = check_arg_count(args)?;
314-
// FIXME: Using host floats.
315-
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
316-
let f2 = f32::from_bits(this.read_scalar(f2)?.to_u32()?);
317-
let res = f.powf(f2);
318-
this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?;
319-
}
320-
321-
"powf64" => {
322-
let [f, f2] = check_arg_count(args)?;
323-
// FIXME: Using host floats.
324-
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
325-
let f2 = f64::from_bits(this.read_scalar(f2)?.to_u64()?);
326-
let res = f.powf(f2);
327-
this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?;
328-
}
329-
330322
"fmaf32" => {
331323
let [a, b, c] = check_arg_count(args)?;
324+
let a = this.read_scalar(a)?.to_f32()?;
325+
let b = this.read_scalar(b)?.to_f32()?;
326+
let c = this.read_scalar(c)?.to_f32()?;
332327
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
333-
let a = f32::from_bits(this.read_scalar(a)?.to_u32()?);
334-
let b = f32::from_bits(this.read_scalar(b)?.to_u32()?);
335-
let c = f32::from_bits(this.read_scalar(c)?.to_u32()?);
336-
let res = a.mul_add(b, c);
337-
this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?;
328+
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
329+
let res = this.adjust_nan(res, &[a, b, c]);
330+
this.write_scalar(res, dest)?;
338331
}
339332

340333
"fmaf64" => {
341334
let [a, b, c] = check_arg_count(args)?;
335+
let a = this.read_scalar(a)?.to_f64()?;
336+
let b = this.read_scalar(b)?.to_f64()?;
337+
let c = this.read_scalar(c)?.to_f64()?;
342338
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
343-
let a = f64::from_bits(this.read_scalar(a)?.to_u64()?);
344-
let b = f64::from_bits(this.read_scalar(b)?.to_u64()?);
345-
let c = f64::from_bits(this.read_scalar(c)?.to_u64()?);
346-
let res = a.mul_add(b, c);
347-
this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?;
339+
let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
340+
let res = this.adjust_nan(res, &[a, b, c]);
341+
this.write_scalar(res, dest)?;
342+
}
343+
344+
"powf32" => {
345+
let [f1, f2] = check_arg_count(args)?;
346+
let f1 = this.read_scalar(f1)?.to_f32()?;
347+
let f2 = this.read_scalar(f2)?.to_f32()?;
348+
// FIXME: Using host floats.
349+
let res = f1.to_host().powf(f2.to_host()).to_soft();
350+
let res = this.adjust_nan(res, &[f1, f2]);
351+
this.write_scalar(res, dest)?;
352+
}
353+
354+
"powf64" => {
355+
let [f1, f2] = check_arg_count(args)?;
356+
let f1 = this.read_scalar(f1)?.to_f64()?;
357+
let f2 = this.read_scalar(f2)?.to_f64()?;
358+
// FIXME: Using host floats.
359+
let res = f1.to_host().powf(f2.to_host()).to_soft();
360+
let res = this.adjust_nan(res, &[f1, f2]);
361+
this.write_scalar(res, dest)?;
348362
}
349363

350364
"powif32" => {
351365
let [f, i] = check_arg_count(args)?;
352-
// FIXME: Using host floats.
353-
let f = f32::from_bits(this.read_scalar(f)?.to_u32()?);
366+
let f = this.read_scalar(f)?.to_f32()?;
354367
let i = this.read_scalar(i)?.to_i32()?;
355-
let res = f.powi(i);
356-
this.write_scalar(Scalar::from_u32(res.to_bits()), dest)?;
368+
// FIXME: Using host floats.
369+
let res = f.to_host().powi(i).to_soft();
370+
let res = this.adjust_nan(res, &[f]);
371+
this.write_scalar(res, dest)?;
357372
}
358373

359374
"powif64" => {
360375
let [f, i] = check_arg_count(args)?;
361-
// FIXME: Using host floats.
362-
let f = f64::from_bits(this.read_scalar(f)?.to_u64()?);
376+
let f = this.read_scalar(f)?.to_f64()?;
363377
let i = this.read_scalar(i)?.to_i32()?;
364-
let res = f.powi(i);
365-
this.write_scalar(Scalar::from_u64(res.to_bits()), dest)?;
378+
// FIXME: Using host floats.
379+
let res = f.to_host().powi(i).to_soft();
380+
let res = this.adjust_nan(res, &[f]);
381+
this.write_scalar(res, dest)?;
366382
}
367383

368384
"float_to_int_unchecked" => {

0 commit comments

Comments
 (0)