miri/shims/math.rs
1use rustc_apfloat::Float;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5
6use self::math::{ToHost, ToSoft};
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11 fn emulate_foreign_item_inner(
12 &mut self,
13 link_name: Symbol,
14 abi: &FnAbi<'tcx, Ty<'tcx>>,
15 args: &[OpTy<'tcx>],
16 dest: &MPlaceTy<'tcx>,
17 ) -> InterpResult<'tcx, EmulateItemResult> {
18 let ecx = self.eval_context_mut();
19
20 // math functions (note that there are also intrinsics for some other functions)
21 match link_name.as_str() {
22 // math functions (note that there are also intrinsics for some other functions)
23 #[rustfmt::skip]
24 | "cbrtf"
25 | "coshf"
26 | "sinhf"
27 | "tanf"
28 | "tanhf"
29 | "acosf"
30 | "asinf"
31 | "atanf"
32 | "acoshf"
33 | "asinhf"
34 | "log1pf"
35 | "expm1f"
36 | "tgammaf"
37 | "erff"
38 | "erfcf"
39 => {
40 let [f] = &ecx.check_shim_sig(
41 shim_sig!(extern "C" fn(f32) -> f32),
42 (link_name, abi, args),
43 )?;
44 let f = ecx.read_scalar(f)?.to_f32()?;
45
46 let res = math::fixed_float_value(ecx, link_name.as_str(), &[f]).unwrap_or_else(|| {
47 // Using host floats (but it's fine, these operations do not have
48 // guaranteed precision).
49 let f_host = f.to_host();
50 let res = match link_name.as_str() {
51 "cbrtf" => f_host.cbrt(),
52 "coshf" => f_host.cosh(),
53 "sinhf" => f_host.sinh(),
54 "tanf" => f_host.tan(),
55 "tanhf" => f_host.tanh(),
56 "acosf" => f_host.acos(),
57 "asinf" => f_host.asin(),
58 "atanf" => f_host.atan(),
59 "acoshf" => f_host.acosh(),
60 "asinhf" => f_host.asinh(),
61 "log1pf" => f_host.ln_1p(),
62 "expm1f" => f_host.exp_m1(),
63 "tgammaf" => f_host.gamma(),
64 "erff" => f_host.erf(),
65 "erfcf" => f_host.erfc(),
66 _ => bug!(),
67 };
68 let res = res.to_soft();
69 // Apply a relative error of 4ULP to introduce some non-determinism
70 // simulating imprecise implementations and optimizations.
71 let res = math::apply_random_float_error_ulp(ecx, res, 4);
72
73 // Clamp the result to the guaranteed range of this function according to the C standard,
74 // if any.
75 math::clamp_float_value(link_name.as_str(), res)
76 });
77 let res = ecx.adjust_nan(res, &[f]);
78 ecx.write_scalar(res, dest)?;
79 }
80 #[rustfmt::skip]
81 | "_hypotf"
82 | "hypotf"
83 | "atan2f"
84 | "fdimf"
85 => {
86 let [f1, f2] = &ecx.check_shim_sig(
87 shim_sig!(extern "C" fn(f32, f32) -> f32),
88 (link_name, abi, args),
89 )?;
90 let f1 = ecx.read_scalar(f1)?.to_f32()?;
91 let f2 = ecx.read_scalar(f2)?.to_f32()?;
92
93 let res = math::fixed_float_value(ecx, link_name.as_str(), &[f1, f2])
94 .unwrap_or_else(|| {
95 let res = match link_name.as_str() {
96 // underscore case for windows, here and below
97 // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
98 // Using host floats (but it's fine, these operations do not have guaranteed precision).
99 "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(),
100 "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(),
101 #[allow(deprecated)]
102 "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
103 _ => bug!(),
104 };
105 // Apply a relative error of 4ULP to introduce some non-determinism
106 // simulating imprecise implementations and optimizations.
107 let res = math::apply_random_float_error_ulp(ecx, res, 4);
108
109 // Clamp the result to the guaranteed range of this function according to the C standard,
110 // if any.
111 math::clamp_float_value(link_name.as_str(), res)
112 });
113 let res = ecx.adjust_nan(res, &[f1, f2]);
114 ecx.write_scalar(res, dest)?;
115 }
116 #[rustfmt::skip]
117 | "cbrt"
118 | "cosh"
119 | "sinh"
120 | "tan"
121 | "tanh"
122 | "acos"
123 | "asin"
124 | "atan"
125 | "acosh"
126 | "asinh"
127 | "log1p"
128 | "expm1"
129 | "tgamma"
130 | "erf"
131 | "erfc"
132 => {
133 let [f] = &ecx.check_shim_sig(
134 shim_sig!(extern "C" fn(f64) -> f64),
135 (link_name, abi, args),
136 )?;
137 let f = ecx.read_scalar(f)?.to_f64()?;
138
139 let res = math::fixed_float_value(ecx, link_name.as_str(), &[f]).unwrap_or_else(|| {
140 // Using host floats (but it's fine, these operations do not have
141 // guaranteed precision).
142 let f_host = f.to_host();
143 let res = match link_name.as_str() {
144 "cbrt" => f_host.cbrt(),
145 "cosh" => f_host.cosh(),
146 "sinh" => f_host.sinh(),
147 "tan" => f_host.tan(),
148 "tanh" => f_host.tanh(),
149 "acos" => f_host.acos(),
150 "asin" => f_host.asin(),
151 "atan" => f_host.atan(),
152 "acosh" => f_host.acosh(),
153 "asinh" => f_host.asinh(),
154 "log1p" => f_host.ln_1p(),
155 "expm1" => f_host.exp_m1(),
156 "tgamma" => f_host.gamma(),
157 "erf" => f_host.erf(),
158 "erfc" => f_host.erfc(),
159 _ => bug!(),
160 };
161 let res = res.to_soft();
162 // Apply a relative error of 4ULP to introduce some non-determinism
163 // simulating imprecise implementations and optimizations.
164 let res = math::apply_random_float_error_ulp(ecx, res, 4);
165
166 // Clamp the result to the guaranteed range of this function according to the C standard,
167 // if any.
168 math::clamp_float_value(link_name.as_str(), res)
169 });
170 let res = ecx.adjust_nan(res, &[f]);
171 ecx.write_scalar(res, dest)?;
172 }
173 #[rustfmt::skip]
174 | "_hypot"
175 | "hypot"
176 | "atan2"
177 | "fdim"
178 => {
179 let [f1, f2] = &ecx.check_shim_sig(
180 shim_sig!(extern "C" fn(f64, f64) -> f64),
181 (link_name, abi, args),
182 )?;
183 let f1 = ecx.read_scalar(f1)?.to_f64()?;
184 let f2 = ecx.read_scalar(f2)?.to_f64()?;
185
186 let res = math::fixed_float_value(ecx, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| {
187 let res = match link_name.as_str() {
188 // underscore case for windows, here and below
189 // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
190 // Using host floats (but it's fine, these operations do not have guaranteed precision).
191 "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(),
192 "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(),
193 #[allow(deprecated)]
194 "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
195 _ => bug!(),
196 };
197 // Apply a relative error of 4ULP to introduce some non-determinism
198 // simulating imprecise implementations and optimizations.
199 let res = math::apply_random_float_error_ulp(ecx, res, 4);
200
201 // Clamp the result to the guaranteed range of this function according to the C standard,
202 // if any.
203 math::clamp_float_value(link_name.as_str(), res)
204 });
205 let res = ecx.adjust_nan(res, &[f1, f2]);
206 ecx.write_scalar(res, dest)?;
207 }
208 #[rustfmt::skip]
209 | "_ldexp"
210 | "ldexp"
211 | "scalbn"
212 => {
213 let [x, exp] = &ecx.check_shim_sig(
214 shim_sig!(extern "C" fn(f64, i32) -> f64),
215 (link_name, abi, args),
216 )?;
217 // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
218 let x = ecx.read_scalar(x)?.to_f64()?;
219 let exp = ecx.read_scalar(exp)?.to_i32()?;
220
221 let res = x.scalbn(exp);
222 let res = ecx.adjust_nan(res, &[x]);
223 ecx.write_scalar(res, dest)?;
224 }
225 "lgammaf_r" => {
226 let [x, signp] = &ecx.check_shim_sig(
227 shim_sig!(extern "C" fn(f32, *i32) -> f32),
228 (link_name, abi, args),
229 )?;
230 let x = ecx.read_scalar(x)?.to_f32()?;
231 let signp = ecx.deref_pointer(signp)?;
232
233 // Using host floats (but it's fine, these operations do not have guaranteed precision).
234 let (res, sign) = x.to_host().ln_gamma();
235 ecx.write_int(sign, &signp)?;
236
237 let res = res.to_soft();
238 // Apply a relative error of 4ULP to introduce some non-determinism
239 // simulating imprecise implementations and optimizations.
240 let res = math::apply_random_float_error_ulp(ecx, res, 4);
241 // Clamp the result to the guaranteed range of this function according to the C standard,
242 // if any.
243 let res = math::clamp_float_value(link_name.as_str(), res);
244 let res = ecx.adjust_nan(res, &[x]);
245 ecx.write_scalar(res, dest)?;
246 }
247 "lgamma_r" => {
248 let [x, signp] = &ecx.check_shim_sig(
249 shim_sig!(extern "C" fn(f64, *i32) -> f64),
250 (link_name, abi, args),
251 )?;
252 let x = ecx.read_scalar(x)?.to_f64()?;
253 let signp = ecx.deref_pointer(signp)?;
254
255 // Using host floats (but it's fine, these operations do not have guaranteed precision).
256 let (res, sign) = x.to_host().ln_gamma();
257 ecx.write_int(sign, &signp)?;
258
259 let res = res.to_soft();
260 // Apply a relative error of 4ULP to introduce some non-determinism
261 // simulating imprecise implementations and optimizations.
262 let res = math::apply_random_float_error_ulp(ecx, res, 4);
263 // Clamp the result to the guaranteed range of this function according to the C standard,
264 // if any.
265 let res = math::clamp_float_value(link_name.as_str(), res);
266 let res = ecx.adjust_nan(res, &[x]);
267 ecx.write_scalar(res, dest)?;
268 }
269
270 _ => return interp_ok(EmulateItemResult::NotSupported),
271 }
272
273 interp_ok(EmulateItemResult::NeedsReturn)
274 }
275}