Skip to main content

miri/shims/
math.rs

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