Skip to content

Commit 5f090d0

Browse files
committed
share more code across the different float types
1 parent dd0b7c4 commit 5f090d0

File tree

5 files changed

+280
-340
lines changed

5 files changed

+280
-340
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use std::{cmp, iter};
55

66
use rand::RngCore;
77
use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
8-
use rustc_apfloat::Float;
98
use rustc_data_structures::fx::{FxBuildHasher, FxHashSet};
109
use rustc_hir::Safety;
1110
use rustc_hir::def::{DefKind, Namespace};
@@ -165,66 +164,6 @@ pub fn iter_exported_symbols<'tcx>(
165164
interp_ok(())
166165
}
167166

168-
/// Convert a softfloat type to its corresponding hostfloat type.
169-
pub trait ToHost {
170-
type HostFloat;
171-
fn to_host(self) -> Self::HostFloat;
172-
}
173-
174-
/// Convert a hostfloat type to its corresponding softfloat type.
175-
pub trait ToSoft {
176-
type SoftFloat;
177-
fn to_soft(self) -> Self::SoftFloat;
178-
}
179-
180-
impl ToHost for rustc_apfloat::ieee::Double {
181-
type HostFloat = f64;
182-
183-
fn to_host(self) -> Self::HostFloat {
184-
f64::from_bits(self.to_bits().try_into().unwrap())
185-
}
186-
}
187-
188-
impl ToSoft for f64 {
189-
type SoftFloat = rustc_apfloat::ieee::Double;
190-
191-
fn to_soft(self) -> Self::SoftFloat {
192-
Float::from_bits(self.to_bits().into())
193-
}
194-
}
195-
196-
impl ToHost for rustc_apfloat::ieee::Single {
197-
type HostFloat = f32;
198-
199-
fn to_host(self) -> Self::HostFloat {
200-
f32::from_bits(self.to_bits().try_into().unwrap())
201-
}
202-
}
203-
204-
impl ToSoft for f32 {
205-
type SoftFloat = rustc_apfloat::ieee::Single;
206-
207-
fn to_soft(self) -> Self::SoftFloat {
208-
Float::from_bits(self.to_bits().into())
209-
}
210-
}
211-
212-
impl ToHost for rustc_apfloat::ieee::Half {
213-
type HostFloat = f16;
214-
215-
fn to_host(self) -> Self::HostFloat {
216-
f16::from_bits(self.to_bits().try_into().unwrap())
217-
}
218-
}
219-
220-
impl ToSoft for f16 {
221-
type SoftFloat = rustc_apfloat::ieee::Half;
222-
223-
fn to_soft(self) -> Self::SoftFloat {
224-
Float::from_bits(self.to_bits().into())
225-
}
226-
}
227-
228167
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
229168
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
230169
/// Checks if the given crate/module exists.

src/tools/miri/src/intrinsics/math.rs

Lines changed: 71 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, Semantics, SingleS};
12
use rustc_apfloat::{self, Float, FloatConvert, Round};
23
use rustc_middle::mir;
34
use rustc_middle::ty::{self, FloatTy};
45

5-
use self::helpers::{ToHost, ToSoft};
6-
use self::math::{HostFloatOperation, IeeeExt};
6+
use self::math::{HostFloatOperation, HostUnaryFloatOp, IeeeExt, host_unary_float_op};
77
use super::check_intrinsic_arg_count;
88
use crate::*;
99

@@ -13,26 +13,11 @@ fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
1313
dest: &MPlaceTy<'tcx>,
1414
) -> InterpResult<'tcx> {
1515
let [f] = check_intrinsic_arg_count(args)?;
16-
let f = this.read_scalar(f)?;
17-
let f: F = f.to_float()?;
18-
// Sqrt is specified to be fully precise.
19-
let res = math::sqrt(f);
20-
let res = this.adjust_nan(res, &[f]);
21-
this.write_scalar(res, dest)
22-
}
23-
24-
#[derive(Debug, Clone, Copy)]
25-
enum HostFloatUnaryOp {
26-
Sin,
27-
Cos,
28-
Exp,
29-
Exp2,
30-
Log,
31-
Log10,
32-
Log2,
16+
math::sqrt_op::<F>(this, f, dest)
3317
}
3418

35-
fn is_host_unary_float_op(intrinsic_name: &str) -> Option<(FloatTy, HostFloatUnaryOp)> {
19+
/// Determine which float operation on which type this is.
20+
fn is_host_unary_float_op(intrinsic_name: &str) -> Option<(FloatTy, HostUnaryFloatOp)> {
3621
let (op, ty) = intrinsic_name.rsplit_once('f')?;
3722

3823
let float_ty = match ty {
@@ -44,19 +29,68 @@ fn is_host_unary_float_op(intrinsic_name: &str) -> Option<(FloatTy, HostFloatUna
4429
};
4530

4631
let host_float_op = match op {
47-
"sin" => HostFloatUnaryOp::Sin,
48-
"cos" => HostFloatUnaryOp::Cos,
49-
"exp" => HostFloatUnaryOp::Exp,
50-
"exp2" => HostFloatUnaryOp::Exp2,
51-
"log" => HostFloatUnaryOp::Log,
52-
"log10" => HostFloatUnaryOp::Log10,
53-
"log2" => HostFloatUnaryOp::Log2,
32+
"sin" => HostUnaryFloatOp::Sin,
33+
"cos" => HostUnaryFloatOp::Cos,
34+
"exp" => HostUnaryFloatOp::Exp,
35+
"exp2" => HostUnaryFloatOp::Exp2,
36+
"log" => HostUnaryFloatOp::Log,
37+
"log10" => HostUnaryFloatOp::Log10,
38+
"log2" => HostUnaryFloatOp::Log2,
5439
_ => return None,
5540
};
5641

5742
Some((float_ty, host_float_op))
5843
}
5944

45+
fn pow_intrinsic<'tcx, S: Semantics>(
46+
this: &mut MiriInterpCx<'tcx>,
47+
args: &[OpTy<'tcx>],
48+
dest: &MPlaceTy<'tcx>,
49+
) -> InterpResult<'tcx, ()>
50+
where
51+
IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
52+
{
53+
let [f1, f2] = check_intrinsic_arg_count(args)?;
54+
let f1: IeeeFloat<S> = this.read_scalar(f1)?.to_float()?;
55+
let f2: IeeeFloat<S> = this.read_scalar(f2)?.to_float()?;
56+
57+
let res = math::fixed_float_value(this, "pow", &[f1, f2]).unwrap_or_else(|| {
58+
// Using host floats (but it's fine, this operation does not have guaranteed precision).
59+
let res = f1.host_powf(f2);
60+
61+
// Apply a relative error of 4ULP to introduce some non-determinism
62+
// simulating imprecise implementations and optimizations.
63+
math::apply_random_float_error_ulp(this, res, 4)
64+
});
65+
let res = this.adjust_nan(res, &[f1, f2]);
66+
this.write_scalar(res, dest)?;
67+
interp_ok(())
68+
}
69+
fn powi_intrinsic<'tcx, S: Semantics>(
70+
this: &mut MiriInterpCx<'tcx>,
71+
args: &[OpTy<'tcx>],
72+
dest: &MPlaceTy<'tcx>,
73+
) -> InterpResult<'tcx, ()>
74+
where
75+
IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
76+
{
77+
let [f, i] = check_intrinsic_arg_count(args)?;
78+
let f: IeeeFloat<S> = this.read_scalar(f)?.to_float()?;
79+
let i = this.read_scalar(i)?.to_i32()?;
80+
81+
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
82+
// Using host floats (but it's fine, this operation does not have guaranteed precision).
83+
let res = f.host_powi(i);
84+
85+
// Apply a relative error of 4ULP to introduce some non-determinism
86+
// simulating imprecise implementations and optimizations.
87+
math::apply_random_float_error_ulp(this, res, 4)
88+
});
89+
let res = this.adjust_nan(res, &[f]);
90+
this.write_scalar(res, dest)?;
91+
interp_ok(())
92+
}
93+
6094
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
6195
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
6296
fn emulate_math_intrinsic(
@@ -145,167 +179,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
145179
}
146180

147181
// Operations that need host floats.
148-
_ if let Some((float_ty, host_float_op)) = is_host_unary_float_op(intrinsic_name) => {
149-
use rustc_apfloat::ieee::{IeeeFloat, Semantics};
150-
151-
fn eval_host_float_operation<'tcx, S: Semantics>(
152-
this: &mut MiriInterpCx<'tcx>,
153-
f: IeeeFloat<S>,
154-
intrinsic_name: &str,
155-
host_float_op: HostFloatUnaryOp,
156-
dest: &MPlaceTy<'tcx>,
157-
) -> InterpResult<'tcx, ()>
158-
where
159-
IeeeFloat<S>: HostFloatOperation + IeeeExt + Float,
160-
IeeeFloat<S>: Into<rustc_const_eval::interpret::Scalar<machine::Provenance>>,
161-
{
162-
use HostFloatOperation;
163-
164-
let res =
165-
math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
166-
// Using host floats (but it's fine, these operations do not have
167-
// guaranteed precision).
168-
let res = match host_float_op {
169-
HostFloatUnaryOp::Sin => HostFloatOperation::host_sin(f),
170-
HostFloatUnaryOp::Cos => HostFloatOperation::host_cos(f),
171-
HostFloatUnaryOp::Exp => HostFloatOperation::host_exp(f),
172-
HostFloatUnaryOp::Exp2 => HostFloatOperation::host_exp2(f),
173-
HostFloatUnaryOp::Log => HostFloatOperation::host_log(f),
174-
HostFloatUnaryOp::Log10 => HostFloatOperation::host_log10(f),
175-
HostFloatUnaryOp::Log2 => HostFloatOperation::host_log2(f),
176-
};
177-
178-
// Apply a relative error of 4ULP to introduce some non-determinism
179-
// simulating imprecise implementations and optimizations.
180-
let res = math::apply_random_float_error_ulp(this, res, 4);
181-
182-
// Clamp the result to the guaranteed range of this function according to the C standard,
183-
// if any.
184-
math::clamp_float_value(intrinsic_name, res)
185-
});
186-
187-
let res = this.adjust_nan(res, &[f]);
188-
this.write_scalar(res, dest)
189-
}
190-
182+
_ if let Some((float_ty, op)) = is_host_unary_float_op(intrinsic_name) => {
191183
let [f] = check_intrinsic_arg_count(args)?;
192184
match float_ty {
193-
FloatTy::F16 => {
194-
let f = this.read_scalar(f)?.to_f16()?;
195-
eval_host_float_operation(this, f, intrinsic_name, host_float_op, dest)?;
196-
}
197-
FloatTy::F32 => {
198-
let f = this.read_scalar(f)?.to_f32()?;
199-
eval_host_float_operation(this, f, intrinsic_name, host_float_op, dest)?;
200-
}
201-
FloatTy::F64 => {
202-
let f = this.read_scalar(f)?.to_f64()?;
203-
eval_host_float_operation(this, f, intrinsic_name, host_float_op, dest)?;
204-
}
185+
FloatTy::F16 => host_unary_float_op::<HalfS>(this, f, op, dest)?,
186+
FloatTy::F32 => host_unary_float_op::<SingleS>(this, f, op, dest)?,
187+
FloatTy::F64 => host_unary_float_op::<DoubleS>(this, f, op, dest)?,
205188
FloatTy::F128 => todo!("f128"), // FIXME(f128)
206189
};
207190
}
208191

209-
"powf16" => {
210-
let [f1, f2] = check_intrinsic_arg_count(args)?;
211-
let f1 = this.read_scalar(f1)?.to_f16()?;
212-
let f2 = this.read_scalar(f2)?.to_f16()?;
213-
214-
let res =
215-
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
216-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
217-
let res = f1.to_host().powf(f2.to_host()).to_soft();
218-
219-
// Apply a relative error of 4ULP to introduce some non-determinism
220-
// simulating imprecise implementations and optimizations.
221-
math::apply_random_float_error_ulp(this, res, 4)
222-
});
223-
let res = this.adjust_nan(res, &[f1, f2]);
224-
this.write_scalar(res, dest)?;
225-
}
226-
"powf32" => {
227-
let [f1, f2] = check_intrinsic_arg_count(args)?;
228-
let f1 = this.read_scalar(f1)?.to_f32()?;
229-
let f2 = this.read_scalar(f2)?.to_f32()?;
230-
231-
let res =
232-
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
233-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
234-
let res = f1.to_host().powf(f2.to_host()).to_soft();
235-
236-
// Apply a relative error of 4ULP to introduce some non-determinism
237-
// simulating imprecise implementations and optimizations.
238-
math::apply_random_float_error_ulp(this, res, 4)
239-
});
240-
let res = this.adjust_nan(res, &[f1, f2]);
241-
this.write_scalar(res, dest)?;
242-
}
243-
"powf64" => {
244-
let [f1, f2] = check_intrinsic_arg_count(args)?;
245-
let f1 = this.read_scalar(f1)?.to_f64()?;
246-
let f2 = this.read_scalar(f2)?.to_f64()?;
247-
248-
let res =
249-
math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
250-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
251-
let res = f1.to_host().powf(f2.to_host()).to_soft();
252-
253-
// Apply a relative error of 4ULP to introduce some non-determinism
254-
// simulating imprecise implementations and optimizations.
255-
math::apply_random_float_error_ulp(this, res, 4)
256-
});
257-
let res = this.adjust_nan(res, &[f1, f2]);
258-
this.write_scalar(res, dest)?;
259-
}
192+
"powf16" => pow_intrinsic::<HalfS>(this, args, dest)?,
193+
"powf32" => pow_intrinsic::<SingleS>(this, args, dest)?,
194+
"powf64" => pow_intrinsic::<DoubleS>(this, args, dest)?,
260195

261-
"powif16" => {
262-
let [f, i] = check_intrinsic_arg_count(args)?;
263-
let f = this.read_scalar(f)?.to_f16()?;
264-
let i = this.read_scalar(i)?.to_i32()?;
265-
266-
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
267-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
268-
let res = f.to_host().powi(i).to_soft();
269-
270-
// Apply a relative error of 4ULP to introduce some non-determinism
271-
// simulating imprecise implementations and optimizations.
272-
math::apply_random_float_error_ulp(this, res, 4)
273-
});
274-
let res = this.adjust_nan(res, &[f]);
275-
this.write_scalar(res, dest)?;
276-
}
277-
"powif32" => {
278-
let [f, i] = check_intrinsic_arg_count(args)?;
279-
let f = this.read_scalar(f)?.to_f32()?;
280-
let i = this.read_scalar(i)?.to_i32()?;
281-
282-
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
283-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
284-
let res = f.to_host().powi(i).to_soft();
285-
286-
// Apply a relative error of 4ULP to introduce some non-determinism
287-
// simulating imprecise implementations and optimizations.
288-
math::apply_random_float_error_ulp(this, res, 4)
289-
});
290-
let res = this.adjust_nan(res, &[f]);
291-
this.write_scalar(res, dest)?;
292-
}
293-
"powif64" => {
294-
let [f, i] = check_intrinsic_arg_count(args)?;
295-
let f = this.read_scalar(f)?.to_f64()?;
296-
let i = this.read_scalar(i)?.to_i32()?;
297-
298-
let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
299-
// Using host floats (but it's fine, this operation does not have guaranteed precision).
300-
let res = f.to_host().powi(i).to_soft();
301-
302-
// Apply a relative error of 4ULP to introduce some non-determinism
303-
// simulating imprecise implementations and optimizations.
304-
math::apply_random_float_error_ulp(this, res, 4)
305-
});
306-
let res = this.adjust_nan(res, &[f]);
307-
this.write_scalar(res, dest)?;
308-
}
196+
"powif16" => powi_intrinsic::<HalfS>(this, args, dest)?,
197+
"powif32" => powi_intrinsic::<SingleS>(this, args, dest)?,
198+
"powif64" => powi_intrinsic::<DoubleS>(this, args, dest)?,
309199

310200
_ => return interp_ok(EmulateItemResult::NotSupported),
311201
}

0 commit comments

Comments
 (0)