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