Skip to content

Commit dd0b7c4

Browse files
folkertdevRalfJung
authored andcommitted
add f16 support
1 parent 0072142 commit dd0b7c4

File tree

7 files changed

+395
-121
lines changed

7 files changed

+395
-121
lines changed

src/tools/miri/src/helpers.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,22 @@ impl ToSoft for f32 {
209209
}
210210
}
211211

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+
212228
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
213229
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
214230
/// Checks if the given crate/module exists.

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

Lines changed: 123 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_middle::mir;
33
use rustc_middle::ty::{self, FloatTy};
44

55
use self::helpers::{ToHost, ToSoft};
6+
use self::math::{HostFloatOperation, IeeeExt};
67
use super::check_intrinsic_arg_count;
78
use crate::*;
89

@@ -20,6 +21,42 @@ fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
2021
this.write_scalar(res, dest)
2122
}
2223

24+
#[derive(Debug, Clone, Copy)]
25+
enum HostFloatUnaryOp {
26+
Sin,
27+
Cos,
28+
Exp,
29+
Exp2,
30+
Log,
31+
Log10,
32+
Log2,
33+
}
34+
35+
fn is_host_unary_float_op(intrinsic_name: &str) -> Option<(FloatTy, HostFloatUnaryOp)> {
36+
let (op, ty) = intrinsic_name.rsplit_once('f')?;
37+
38+
let float_ty = match ty {
39+
"16" => FloatTy::F16,
40+
"32" => FloatTy::F32,
41+
"64" => FloatTy::F64,
42+
"128" => FloatTy::F128,
43+
_ => return None,
44+
};
45+
46+
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,
54+
_ => return None,
55+
};
56+
57+
Some((float_ty, host_float_op))
58+
}
59+
2360
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
2461
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2562
fn emulate_math_intrinsic(
@@ -108,94 +145,84 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
108145
}
109146

110147
// Operations that need host floats.
111-
#[rustfmt::skip]
112-
| "sinf32"
113-
| "cosf32"
114-
| "expf32"
115-
| "exp2f32"
116-
| "logf32"
117-
| "log10f32"
118-
| "log2f32"
119-
=> {
120-
let [f] = check_intrinsic_arg_count(args)?;
121-
let f = this.read_scalar(f)?.to_f32()?;
148+
_ if let Some((float_ty, host_float_op)) = is_host_unary_float_op(intrinsic_name) => {
149+
use rustc_apfloat::ieee::{IeeeFloat, Semantics};
122150

123-
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
124-
// Using host floats (but it's fine, these operations do not have
125-
// guaranteed precision).
126-
let host = f.to_host();
127-
let res = match intrinsic_name {
128-
"sinf32" => host.sin(),
129-
"cosf32" => host.cos(),
130-
"expf32" => host.exp(),
131-
"exp2f32" => host.exp2(),
132-
"logf32" => host.ln(),
133-
"log10f32" => host.log10(),
134-
"log2f32" => host.log2(),
135-
_ => bug!(),
136-
};
137-
let res = res.to_soft();
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;
138163

139-
// Apply a relative error of 4ULP to introduce some non-determinism
140-
// simulating imprecise implementations and optimizations.
141-
let res = math::apply_random_float_error_ulp(
142-
this,
143-
res,
144-
4,
145-
);
146-
147-
// Clamp the result to the guaranteed range of this function according to the C standard,
148-
// if any.
149-
math::clamp_float_value(intrinsic_name, res)
150-
});
151-
let res = this.adjust_nan(res, &[f]);
152-
this.write_scalar(res, dest)?;
153-
}
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+
}
154190

155-
#[rustfmt::skip]
156-
| "sinf64"
157-
| "cosf64"
158-
| "expf64"
159-
| "exp2f64"
160-
| "logf64"
161-
| "log10f64"
162-
| "log2f64"
163-
=> {
164191
let [f] = check_intrinsic_arg_count(args)?;
165-
let f = this.read_scalar(f)?.to_f64()?;
192+
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+
}
205+
FloatTy::F128 => todo!("f128"), // FIXME(f128)
206+
};
207+
}
166208

167-
let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
168-
// Using host floats (but it's fine, these operations do not have
169-
// guaranteed precision).
170-
let host = f.to_host();
171-
let res = match intrinsic_name {
172-
"sinf64" => host.sin(),
173-
"cosf64" => host.cos(),
174-
"expf64" => host.exp(),
175-
"exp2f64" => host.exp2(),
176-
"logf64" => host.ln(),
177-
"log10f64" => host.log10(),
178-
"log2f64" => host.log2(),
179-
_ => bug!(),
180-
};
181-
let res = res.to_soft();
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()?;
182213

183-
// Apply a relative error of 4ULP to introduce some non-determinism
184-
// simulating imprecise implementations and optimizations.
185-
let res = math::apply_random_float_error_ulp(
186-
this,
187-
res,
188-
4,
189-
);
190-
191-
// Clamp the result to the guaranteed range of this function according to the C standard,
192-
// if any.
193-
math::clamp_float_value(intrinsic_name, res)
194-
});
195-
let res = this.adjust_nan(res, &[f]);
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]);
196224
this.write_scalar(res, dest)?;
197225
}
198-
199226
"powf32" => {
200227
let [f1, f2] = check_intrinsic_arg_count(args)?;
201228
let f1 = this.read_scalar(f1)?.to_f32()?;
@@ -231,6 +258,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
231258
this.write_scalar(res, dest)?;
232259
}
233260

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+
}
234277
"powif32" => {
235278
let [f, i] = check_intrinsic_arg_count(args)?;
236279
let f = this.read_scalar(f)?.to_f32()?;

src/tools/miri/src/intrinsics/simd.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
4242
// Using host floats except for sqrt (but it's fine, these operations do not
4343
// have guaranteed precision).
4444
let val = match float_ty {
45-
FloatTy::F16 => unimplemented!("f16_f128"),
45+
FloatTy::F16 => {
46+
let f = op.to_scalar().to_f16()?;
47+
let res = match intrinsic_name {
48+
"fsqrt" => math::sqrt(f),
49+
"fsin" => f.to_host().sin().to_soft(),
50+
"fcos" => f.to_host().cos().to_soft(),
51+
"fexp" => f.to_host().exp().to_soft(),
52+
"fexp2" => f.to_host().exp2().to_soft(),
53+
"flog" => f.to_host().ln().to_soft(),
54+
"flog2" => f.to_host().log2().to_soft(),
55+
"flog10" => f.to_host().log10().to_soft(),
56+
_ => bug!(),
57+
};
58+
let res = this.adjust_nan(res, &[f]);
59+
Scalar::from(res)
60+
}
4661
FloatTy::F32 => {
4762
let f = op.to_scalar().to_f32()?;
4863
let res = match intrinsic_name {

src/tools/miri/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
feature(abort_unwind)
1010
)]
1111
#![feature(rustc_private)]
12+
#![feature(f16)]
1213
#![feature(float_gamma)]
1314
#![feature(float_erf)]
1415
#![feature(map_try_insert)]

0 commit comments

Comments
 (0)