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
19fn 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 let res = f1.host_powf(f2);
56
57 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 let res = f.host_powi(i);
80
81 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 "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 if !float_finite(&res)? {
160 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
161 }
162 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 _ 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"), };
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"), }
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"), }
219 }
220
221 _ => return interp_ok(EmulateItemResult::NotSupported),
222 }
223
224 interp_ok(EmulateItemResult::NeedsReturn)
225 }
226}
227
228pub(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 let crc = u128::from(crc.reverse_bits());
243 let v = u128::from(data.reverse_bits() >> (64u32.strict_sub(bit_size)));
247
248 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
276pub(crate) mod aes {
278 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 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
311pub(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 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, b, c, d.wrapping_add(t1), e, f, g, ]
344 }
345}