1use rustc_apfloat::{self, Float, FloatConvert, Round};
2use rustc_middle::mir;
3use rustc_middle::ty::{self, FloatTy};
4
5use self::helpers::{ToHost, ToSoft};
6use super::check_intrinsic_arg_count;
7use crate::*;
8
9fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
10 this: &mut MiriInterpCx<'tcx>,
11 args: &[OpTy<'tcx>],
12 dest: &MPlaceTy<'tcx>,
13) -> InterpResult<'tcx> {
14 let [f] = check_intrinsic_arg_count(args)?;
15 let f = this.read_scalar(f)?;
16 let f: F = f.to_float()?;
17 let res = math::sqrt(f);
19 let res = this.adjust_nan(res, &[f]);
20 this.write_scalar(res, dest)
21}
22
23impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
24pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
25 fn emulate_math_intrinsic(
26 &mut self,
27 intrinsic_name: &str,
28 _generic_args: ty::GenericArgsRef<'tcx>,
29 args: &[OpTy<'tcx>],
30 dest: &MPlaceTy<'tcx>,
31 ) -> InterpResult<'tcx, EmulateItemResult> {
32 let this = self.eval_context_mut();
33
34 match intrinsic_name {
35 "sqrtf16" => sqrt::<rustc_apfloat::ieee::Half>(this, args, dest)?,
37 "sqrtf32" => sqrt::<rustc_apfloat::ieee::Single>(this, args, dest)?,
38 "sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
39 "sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
40
41 #[rustfmt::skip]
42 | "fadd_fast"
43 | "fsub_fast"
44 | "fmul_fast"
45 | "fdiv_fast"
46 | "frem_fast"
47 => {
48 let [a, b] = check_intrinsic_arg_count(args)?;
49 let a = this.read_immediate(a)?;
50 let b = this.read_immediate(b)?;
51 let op = match intrinsic_name {
52 "fadd_fast" => mir::BinOp::Add,
53 "fsub_fast" => mir::BinOp::Sub,
54 "fmul_fast" => mir::BinOp::Mul,
55 "fdiv_fast" => mir::BinOp::Div,
56 "frem_fast" => mir::BinOp::Rem,
57 _ => bug!(),
58 };
59 let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
60 let ty::Float(fty) = x.layout.ty.kind() else {
61 bug!("float_finite: non-float input type {}", x.layout.ty)
62 };
63 interp_ok(match fty {
64 FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
65 FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
66 FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
67 FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
68 })
69 };
70 match (float_finite(&a)?, float_finite(&b)?) {
71 (false, false) => throw_ub_format!(
72 "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
73 ),
74 (false, _) => throw_ub_format!(
75 "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
76 ),
77 (_, false) => throw_ub_format!(
78 "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
79 ),
80 _ => {}
81 }
82 let res = this.binary_op(op, &a, &b)?;
83 if !float_finite(&res)? {
86 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
87 }
88 let res = math::apply_random_float_error_to_imm(this, res, 4)?;
91 this.write_immediate(*res, dest)?;
92 }
93
94 "float_to_int_unchecked" => {
95 let [val] = check_intrinsic_arg_count(args)?;
96 let val = this.read_immediate(val)?;
97
98 let res = this
99 .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
100 .ok_or_else(|| {
101 err_ub_format!(
102 "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
103 dest.layout.ty
104 )
105 })?;
106
107 this.write_immediate(*res, dest)?;
108 }
109
110 #[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()?;
122
123 let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
124 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();
138
139 let res = math::apply_random_float_error_ulp(
142 this,
143 res,
144 4,
145 );
146
147 math::clamp_float_value(intrinsic_name, res)
150 });
151 let res = this.adjust_nan(res, &[f]);
152 this.write_scalar(res, dest)?;
153 }
154
155 #[rustfmt::skip]
156 | "sinf64"
157 | "cosf64"
158 | "expf64"
159 | "exp2f64"
160 | "logf64"
161 | "log10f64"
162 | "log2f64"
163 => {
164 let [f] = check_intrinsic_arg_count(args)?;
165 let f = this.read_scalar(f)?.to_f64()?;
166
167 let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
168 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();
182
183 let res = math::apply_random_float_error_ulp(
186 this,
187 res,
188 4,
189 );
190
191 math::clamp_float_value(intrinsic_name, res)
194 });
195 let res = this.adjust_nan(res, &[f]);
196 this.write_scalar(res, dest)?;
197 }
198
199 "powf32" => {
200 let [f1, f2] = check_intrinsic_arg_count(args)?;
201 let f1 = this.read_scalar(f1)?.to_f32()?;
202 let f2 = this.read_scalar(f2)?.to_f32()?;
203
204 let res =
205 math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
206 let res = f1.to_host().powf(f2.to_host()).to_soft();
208
209 math::apply_random_float_error_ulp(this, res, 4)
212 });
213 let res = this.adjust_nan(res, &[f1, f2]);
214 this.write_scalar(res, dest)?;
215 }
216 "powf64" => {
217 let [f1, f2] = check_intrinsic_arg_count(args)?;
218 let f1 = this.read_scalar(f1)?.to_f64()?;
219 let f2 = this.read_scalar(f2)?.to_f64()?;
220
221 let res =
222 math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
223 let res = f1.to_host().powf(f2.to_host()).to_soft();
225
226 math::apply_random_float_error_ulp(this, res, 4)
229 });
230 let res = this.adjust_nan(res, &[f1, f2]);
231 this.write_scalar(res, dest)?;
232 }
233
234 "powif32" => {
235 let [f, i] = check_intrinsic_arg_count(args)?;
236 let f = this.read_scalar(f)?.to_f32()?;
237 let i = this.read_scalar(i)?.to_i32()?;
238
239 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
240 let res = f.to_host().powi(i).to_soft();
242
243 math::apply_random_float_error_ulp(this, res, 4)
246 });
247 let res = this.adjust_nan(res, &[f]);
248 this.write_scalar(res, dest)?;
249 }
250 "powif64" => {
251 let [f, i] = check_intrinsic_arg_count(args)?;
252 let f = this.read_scalar(f)?.to_f64()?;
253 let i = this.read_scalar(i)?.to_i32()?;
254
255 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
256 let res = f.to_host().powi(i).to_soft();
258
259 math::apply_random_float_error_ulp(this, res, 4)
262 });
263 let res = this.adjust_nan(res, &[f]);
264 this.write_scalar(res, dest)?;
265 }
266
267 _ => return interp_ok(EmulateItemResult::NotSupported),
268 }
269
270 interp_ok(EmulateItemResult::NeedsReturn)
271 }
272}