Skip to main content

miri/intrinsics/
math.rs

1use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, Semantics, SingleS};
2use rustc_apfloat::{self, Float, FloatConvert, Round};
3use rustc_middle::mir;
4use rustc_middle::ty::{self, FloatTy};
5
6use self::math::{HostFloatOperation, HostUnaryFloatOp, IeeeExt, host_unary_float_op};
7use super::check_intrinsic_arg_count;
8use crate::*;
9
10fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
11    ecx: &mut MiriInterpCx<'tcx>,
12    args: &[OpTy<'tcx>],
13    dest: &PlaceTy<'tcx>,
14) -> InterpResult<'tcx> {
15    let [f] = check_intrinsic_arg_count(args)?;
16    math::sqrt_op::<F>(ecx, f, dest)
17}
18
19/// Determine which float operation on which type this is.
20fn is_host_unary_float_op(
21    intrinsic_name: &str,
22    generic_args: ty::GenericArgs<'_>,
23) -> Option<(FloatTy, HostUnaryFloatOp)> {
24    let host_float_op = match intrinsic_name {
25        "sin" => HostUnaryFloatOp::Sin,
26        "cos" => HostUnaryFloatOp::Cos,
27        "exp" => HostUnaryFloatOp::Exp,
28        "exp2" => HostUnaryFloatOp::Exp2,
29        "log" => HostUnaryFloatOp::Log,
30        "log10" => HostUnaryFloatOp::Log10,
31        "log2" => HostUnaryFloatOp::Log2,
32        _ => return None,
33    };
34
35    let ty::Float(float_ty) = *generic_args.type_at(0).kind() else {
36        bug!("`{intrinsic_name}` intrinsic called on non-float type");
37    };
38    Some((float_ty, host_float_op))
39}
40
41fn pow_intrinsic<'tcx, S: Semantics>(
42    ecx: &mut MiriInterpCx<'tcx>,
43    args: &[OpTy<'tcx>],
44    dest: &PlaceTy<'tcx>,
45) -> InterpResult<'tcx, ()>
46where
47    IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
48{
49    let [f1, f2] = check_intrinsic_arg_count(args)?;
50    let f1: IeeeFloat<S> = ecx.read_scalar(f1)?.to_float()?;
51    let f2: IeeeFloat<S> = ecx.read_scalar(f2)?.to_float()?;
52
53    let res = math::fixed_float_value(ecx, "pow", &[f1, f2]).unwrap_or_else(|| {
54        // Using host floats (but it's fine, this operation does not have guaranteed precision).
55        let res = f1.host_powf(f2);
56
57        // Apply a relative error of 4ULP to introduce some non-determinism
58        // simulating imprecise implementations and optimizations.
59        math::apply_random_float_error_ulp(ecx, res, 4)
60    });
61    let res = ecx.adjust_nan(res, &[f1, f2]);
62    ecx.write_scalar(res, dest)?;
63    interp_ok(())
64}
65fn powi_intrinsic<'tcx, S: Semantics>(
66    ecx: &mut MiriInterpCx<'tcx>,
67    args: &[OpTy<'tcx>],
68    dest: &PlaceTy<'tcx>,
69) -> InterpResult<'tcx, ()>
70where
71    IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
72{
73    let [f, i] = check_intrinsic_arg_count(args)?;
74    let f: IeeeFloat<S> = ecx.read_scalar(f)?.to_float()?;
75    let i = ecx.read_scalar(i)?.to_i32()?;
76
77    let res = math::fixed_powi_value(ecx, f, i).unwrap_or_else(|| {
78        // Using host floats (but it's fine, this operation does not have guaranteed precision).
79        let res = f.host_powi(i);
80
81        // Apply a relative error of 4ULP to introduce some non-determinism
82        // simulating imprecise implementations and optimizations.
83        math::apply_random_float_error_ulp(ecx, res, 4)
84    });
85    let res = ecx.adjust_nan(res, &[f]);
86    ecx.write_scalar(res, dest)?;
87    interp_ok(())
88}
89
90impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
91pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
92    fn emulate_math_intrinsic(
93        &mut self,
94        intrinsic_name: &str,
95        generic_args: ty::GenericArgs<'tcx>,
96        args: &[OpTy<'tcx>],
97        dest: &PlaceTy<'tcx>,
98    ) -> InterpResult<'tcx, EmulateItemResult> {
99        let ecx = self.eval_context_mut();
100
101        match intrinsic_name {
102            // Operations we can do with soft-floats.
103            "sqrt" => {
104                let ty::Float(float_ty) = *generic_args.type_at(0).kind() else {
105                    bug!("`sqrt` intrinsic called on non-float type");
106                };
107                match float_ty {
108                    FloatTy::F16 => sqrt::<rustc_apfloat::ieee::Half>(ecx, args, dest)?,
109                    FloatTy::F32 => sqrt::<rustc_apfloat::ieee::Single>(ecx, args, dest)?,
110                    FloatTy::F64 => sqrt::<rustc_apfloat::ieee::Double>(ecx, args, dest)?,
111                    FloatTy::F128 => sqrt::<rustc_apfloat::ieee::Quad>(ecx, args, dest)?,
112                }
113            }
114
115            #[rustfmt::skip]
116            | "fadd_fast"
117            | "fsub_fast"
118            | "fmul_fast"
119            | "fdiv_fast"
120            | "frem_fast"
121            => {
122                let [a, b] = check_intrinsic_arg_count(args)?;
123                let a = ecx.read_immediate(a)?;
124                let b = ecx.read_immediate(b)?;
125                let op = match intrinsic_name {
126                    "fadd_fast" => mir::BinOp::Add,
127                    "fsub_fast" => mir::BinOp::Sub,
128                    "fmul_fast" => mir::BinOp::Mul,
129                    "fdiv_fast" => mir::BinOp::Div,
130                    "frem_fast" => mir::BinOp::Rem,
131                    _ => bug!(),
132                };
133                let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
134                    let ty::Float(fty) = x.layout.ty.kind() else {
135                        bug!("float_finite: non-float input type {}", x.layout.ty)
136                    };
137                    interp_ok(match fty {
138                        FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
139                        FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
140                        FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
141                        FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
142                    })
143                };
144                match (float_finite(&a)?, float_finite(&b)?) {
145                    (false, false) => throw_ub_format!(
146                        "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
147                    ),
148                    (false, _) => throw_ub_format!(
149                        "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
150                    ),
151                    (_, false) => throw_ub_format!(
152                        "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
153                    ),
154                    _ => {}
155                }
156                let res = ecx.binary_op(op, &a, &b)?;
157                // This cannot be a NaN so we also don't have to apply any non-determinism.
158                // (Also, `binary_op` already called `generate_nan` if needed.)
159                if !float_finite(&res)? {
160                    throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
161                }
162                // Apply a relative error of 4ULP to simulate non-deterministic precision loss
163                // due to optimizations.
164                let res = math::apply_random_float_error_to_imm(ecx, res, 4)?;
165                ecx.write_immediate(*res, dest)?;
166            }
167
168            "float_to_int_unchecked" => {
169                let [val] = check_intrinsic_arg_count(args)?;
170                let val = ecx.read_immediate(val)?;
171
172                let res = ecx
173                    .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
174                    .ok_or_else(|| {
175                        err_ub_format!(
176                            "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
177                            dest.layout.ty
178                        )
179                    })?;
180
181                ecx.write_immediate(*res, dest)?;
182            }
183
184            // Operations that need host floats.
185            _ if let Some((float_ty, op)) =
186                is_host_unary_float_op(intrinsic_name, generic_args) =>
187            {
188                let [f] = check_intrinsic_arg_count(args)?;
189                match float_ty {
190                    FloatTy::F16 => host_unary_float_op::<HalfS>(ecx, f, op, dest)?,
191                    FloatTy::F32 => host_unary_float_op::<SingleS>(ecx, f, op, dest)?,
192                    FloatTy::F64 => host_unary_float_op::<DoubleS>(ecx, f, op, dest)?,
193                    FloatTy::F128 => todo!("f128"), // FIXME(f128)
194                };
195            }
196
197            "powf" => {
198                let ty::Float(float_ty) = *generic_args.type_at(0).kind() else {
199                    bug!("`powf` intrinsic called on non-float type");
200                };
201                match float_ty {
202                    FloatTy::F16 => pow_intrinsic::<HalfS>(ecx, args, dest)?,
203                    FloatTy::F32 => pow_intrinsic::<SingleS>(ecx, args, dest)?,
204                    FloatTy::F64 => pow_intrinsic::<DoubleS>(ecx, args, dest)?,
205                    FloatTy::F128 => todo!("f128"), // FIXME(f128)
206                }
207            }
208
209            "powi" => {
210                let ty::Float(float_ty) = *generic_args.type_at(0).kind() else {
211                    bug!("`powf` intrinsic called on non-float type");
212                };
213                match float_ty {
214                    FloatTy::F16 => powi_intrinsic::<HalfS>(ecx, args, dest)?,
215                    FloatTy::F32 => powi_intrinsic::<SingleS>(ecx, args, dest)?,
216                    FloatTy::F64 => powi_intrinsic::<DoubleS>(ecx, args, dest)?,
217                    FloatTy::F128 => todo!("f128"), // FIXME(f128)
218                }
219            }
220
221            _ => return interp_ok(EmulateItemResult::NotSupported),
222        }
223
224        interp_ok(EmulateItemResult::NeedsReturn)
225    }
226}
227
228/// Compute a CRC32 checksum using the given polynomial.
229///
230/// `bit_size` is the number of relevant data bits (8, 16, 32, or 64).
231/// Only the low `bit_size` bits of `data` are used; higher bits must be zero.
232/// `polynomial` includes the leading 1 bit (e.g. `0x11EDC6F41` for CRC32C).
233///
234/// Following hardware CRC conventions, `crc` and `data` bits are assumed to be reversed,
235/// and output bits will be equally reversed.
236pub(crate) fn compute_crc32(crc: u32, data: u64, bit_size: u32, polynomial: u128) -> u32 {
237    assert!(
238        bit_size == 64 || data < 1u64.strict_shl(bit_size),
239        "crc32: `data` is larger than {bit_size} bits"
240    );
241    // Bit-reverse inputs to match hardware CRC conventions.
242    let crc = u128::from(crc.reverse_bits());
243    // Reverse all 64 bits of `data`, then shift right by `64 - bit_size`. This
244    // discards the (now-reversed) higher bits, leaving only the reversed low
245    // `bit_size` bits in the lowest positions (with zeros above).
246    let v = u128::from(data.reverse_bits() >> (64u32.strict_sub(bit_size)));
247
248    // Perform polynomial division modulo 2.
249    // The algorithm for the division is an adapted version of the
250    // schoolbook division algorithm used for normal integer or polynomial
251    // division. In this context, the quotient is not calculated, since
252    // only the remainder is needed.
253    //
254    // The algorithm works as follows:
255    // 1. Pull down digits until division can be performed. In the context of division
256    //    modulo 2 it means locating the most significant digit of the dividend and shifting
257    //    the divisor such that the position of the divisors most significand digit and the
258    //    dividends most significand digit match.
259    // 2. Perform a division and determine the remainder. Since it is arithmetic modulo 2,
260    //    this operation is a simple bitwise exclusive or.
261    // 3. Repeat steps 1. and 2. until the full remainder is calculated. This is the case
262    //    once the degree of the remainder polynomial is smaller than the degree of the
263    //    divisor polynomial. In other words, the number of leading zeros of the remainder
264    //    is larger than the number of leading zeros of the divisor. It is important to
265    //    note that standard arithmetic comparison is not applicable here:
266    //    0b10011 / 0b11111 = 0b01100 is a valid division, even though the dividend is
267    //    smaller than the divisor.
268    let mut dividend = (crc << bit_size) ^ (v << 32);
269    while dividend.leading_zeros() <= polynomial.leading_zeros() {
270        dividend ^= (polynomial << polynomial.leading_zeros()) >> dividend.leading_zeros();
271    }
272
273    u32::try_from(dividend).unwrap().reverse_bits()
274}
275
276/// AES primitives
277pub(crate) mod aes {
278    /// AES S-box
279    ///
280    /// Source: [NIST Advanced Encryption Standar][1], Figure 7 (page 16)
281    ///
282    /// [1]: https://tsapps.nist.gov/publication/get_pdf.cfm?pub_id=901427
283    const SBOX: [u8; 256] = [
284        0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab,
285        0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4,
286        0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71,
287        0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
288        0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6,
289        0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb,
290        0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45,
291        0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
292        0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44,
293        0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a,
294        0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
295        0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
296        0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
297        0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e,
298        0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1,
299        0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
300        0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb,
301        0x16,
302    ];
303
304    /// Applies S-box substitution to each byte of a 32-bit word
305    pub(crate) fn sub_word(word: u32) -> u32 {
306        let bytes = word.to_ne_bytes().map(|b| SBOX[usize::from(b)]);
307        u32::from_ne_bytes(bytes)
308    }
309}
310
311// sha256 primitives shared by the x86 and aarch64 intrinsics. Math helpers adapted from RustCrypto soft impl:
312// https://github.com/RustCrypto/hashes/blob/3d2bc57db40fd6aeb25d6c6da98d67e2784c2985/sha2/src/sha256/soft/compact.rs
313pub(crate) mod sha256 {
314    pub(crate) fn sigma0(x: u32) -> u32 {
315        x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
316    }
317
318    pub(crate) fn sigma1(x: u32) -> u32 {
319        x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
320    }
321
322    /// One round of the compression; `wk` is the round's `w[i] + k[i]`.
323    pub(crate) fn round(state: [u32; 8], wk: u32) -> [u32; 8] {
324        let [a, b, c, d, e, f, g, h] = state;
325
326        let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
327        let ch = (e & f) ^ ((!e) & g);
328        let t1 = s1.wrapping_add(ch).wrapping_add(wk).wrapping_add(h);
329
330        let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
331        let maj = (a & b) ^ (a & c) ^ (b & c);
332        let t2 = s0.wrapping_add(maj);
333
334        [
335            t1.wrapping_add(t2), // a
336            a,                   // b
337            b,                   // c
338            c,                   // d
339            d.wrapping_add(t1),  // e
340            e,                   // f
341            f,                   // g
342            g,                   // h
343        ]
344    }
345}