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}
256
257/// Compute a CRC32 checksum using the given polynomial.
258///
259/// `bit_size` is the number of relevant data bits (8, 16, 32, or 64).
260/// Only the low `bit_size` bits of `data` are used; higher bits must be zero.
261/// `polynomial` includes the leading 1 bit (e.g. `0x11EDC6F41` for CRC32C).
262///
263/// Following hardware CRC conventions, `crc` and `data` bits are assumed to be reversed,
264/// and output bits will be equally reversed.
265pub(crate) fn compute_crc32(crc: u32, data: u64, bit_size: u32, polynomial: u128) -> u32 {
266    assert!(
267        bit_size == 64 || data < 1u64.strict_shl(bit_size),
268        "crc32: `data` is larger than {bit_size} bits"
269    );
270    // Bit-reverse inputs to match hardware CRC conventions.
271    let crc = u128::from(crc.reverse_bits());
272    // Reverse all 64 bits of `data`, then shift right by `64 - bit_size`. This
273    // discards the (now-reversed) higher bits, leaving only the reversed low
274    // `bit_size` bits in the lowest positions (with zeros above).
275    let v = u128::from(data.reverse_bits() >> (64u32.strict_sub(bit_size)));
276
277    // Perform polynomial division modulo 2.
278    // The algorithm for the division is an adapted version of the
279    // schoolbook division algorithm used for normal integer or polynomial
280    // division. In this context, the quotient is not calculated, since
281    // only the remainder is needed.
282    //
283    // The algorithm works as follows:
284    // 1. Pull down digits until division can be performed. In the context of division
285    //    modulo 2 it means locating the most significant digit of the dividend and shifting
286    //    the divisor such that the position of the divisors most significand digit and the
287    //    dividends most significand digit match.
288    // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2,
289    //    this operation is a simple bitwise exclusive or.
290    // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case
291    //    once the degree of the remainder polynomial is smaller than the degree of the
292    //    divisor polynomial. In other words, the number of leading zeros of the remainder
293    //    is larger than the number of leading zeros of the divisor. It is important to
294    //    note that standard arithmetic comparison is not applicable here:
295    //    0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is
296    //    smaller than the divisor.
297    let mut dividend = (crc << bit_size) ^ (v << 32);
298    while dividend.leading_zeros() <= polynomial.leading_zeros() {
299        dividend ^= (polynomial << polynomial.leading_zeros()) >> dividend.leading_zeros();
300    }
301
302    u32::try_from(dividend).unwrap().reverse_bits()
303}