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}