Skip to main content

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}