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 this: &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>(this, f, dest)
17}
18
19fn is_host_unary_float_op(
21 intrinsic_name: &str,
22 generic_args: ty::GenericArgsRef<'_>,
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 this: &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> = this.read_scalar(f1)?.to_float()?;
51 let f2: IeeeFloat<S> = this.read_scalar(f2)?.to_float()?;
52
53 let res = math::fixed_float_value(this, "pow", &[f1, f2]).unwrap_or_else(|| {
54 let res = f1.host_powf(f2);
56
57 math::apply_random_float_error_ulp(this, res, 4)
60 });
61 let res = this.adjust_nan(res, &[f1, f2]);
62 this.write_scalar(res, dest)?;
63 interp_ok(())
64}
65fn powi_intrinsic<'tcx, S: Semantics>(
66 this: &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> = this.read_scalar(f)?.to_float()?;
75 let i = this.read_scalar(i)?.to_i32()?;
76
77 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
78 let res = f.host_powi(i);
80
81 math::apply_random_float_error_ulp(this, res, 4)
84 });
85 let res = this.adjust_nan(res, &[f]);
86 this.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::GenericArgsRef<'tcx>,
96 args: &[OpTy<'tcx>],
97 dest: &PlaceTy<'tcx>,
98 ) -> InterpResult<'tcx, EmulateItemResult> {
99 let this = self.eval_context_mut();
100
101 match intrinsic_name {
102 "sqrtf16" => sqrt::<rustc_apfloat::ieee::Half>(this, args, dest)?,
104 "sqrtf32" => sqrt::<rustc_apfloat::ieee::Single>(this, args, dest)?,
105 "sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
106 "sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
107
108 #[rustfmt::skip]
109 | "fadd_fast"
110 | "fsub_fast"
111 | "fmul_fast"
112 | "fdiv_fast"
113 | "frem_fast"
114 => {
115 let [a, b] = check_intrinsic_arg_count(args)?;
116 let a = this.read_immediate(a)?;
117 let b = this.read_immediate(b)?;
118 let op = match intrinsic_name {
119 "fadd_fast" => mir::BinOp::Add,
120 "fsub_fast" => mir::BinOp::Sub,
121 "fmul_fast" => mir::BinOp::Mul,
122 "fdiv_fast" => mir::BinOp::Div,
123 "frem_fast" => mir::BinOp::Rem,
124 _ => bug!(),
125 };
126 let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
127 let ty::Float(fty) = x.layout.ty.kind() else {
128 bug!("float_finite: non-float input type {}", x.layout.ty)
129 };
130 interp_ok(match fty {
131 FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
132 FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
133 FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
134 FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
135 })
136 };
137 match (float_finite(&a)?, float_finite(&b)?) {
138 (false, false) => throw_ub_format!(
139 "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
140 ),
141 (false, _) => throw_ub_format!(
142 "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
143 ),
144 (_, false) => throw_ub_format!(
145 "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
146 ),
147 _ => {}
148 }
149 let res = this.binary_op(op, &a, &b)?;
150 if !float_finite(&res)? {
153 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
154 }
155 let res = math::apply_random_float_error_to_imm(this, res, 4)?;
158 this.write_immediate(*res, dest)?;
159 }
160
161 "float_to_int_unchecked" => {
162 let [val] = check_intrinsic_arg_count(args)?;
163 let val = this.read_immediate(val)?;
164
165 let res = this
166 .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
167 .ok_or_else(|| {
168 err_ub_format!(
169 "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
170 dest.layout.ty
171 )
172 })?;
173
174 this.write_immediate(*res, dest)?;
175 }
176
177 _ if let Some((float_ty, op)) =
179 is_host_unary_float_op(intrinsic_name, generic_args) =>
180 {
181 let [f] = check_intrinsic_arg_count(args)?;
182 match float_ty {
183 FloatTy::F16 => host_unary_float_op::<HalfS>(this, f, op, dest)?,
184 FloatTy::F32 => host_unary_float_op::<SingleS>(this, f, op, dest)?,
185 FloatTy::F64 => host_unary_float_op::<DoubleS>(this, f, op, dest)?,
186 FloatTy::F128 => todo!("f128"), };
188 }
189
190 "powf16" => pow_intrinsic::<HalfS>(this, args, dest)?,
191 "powf32" => pow_intrinsic::<SingleS>(this, args, dest)?,
192 "powf64" => pow_intrinsic::<DoubleS>(this, args, dest)?,
193 "powf128" => todo!("f128"), "powif16" => powi_intrinsic::<HalfS>(this, args, dest)?,
196 "powif32" => powi_intrinsic::<SingleS>(this, args, dest)?,
197 "powif64" => powi_intrinsic::<DoubleS>(this, args, dest)?,
198 "powif128" => todo!("f128"), _ => return interp_ok(EmulateItemResult::NotSupported),
201 }
202
203 interp_ok(EmulateItemResult::NeedsReturn)
204 }
205}
206
207pub(crate) fn compute_crc32(crc: u32, data: u64, bit_size: u32, polynomial: u128) -> u32 {
216 assert!(
217 bit_size == 64 || data < 1u64.strict_shl(bit_size),
218 "crc32: `data` is larger than {bit_size} bits"
219 );
220 let crc = u128::from(crc.reverse_bits());
222 let v = u128::from(data.reverse_bits() >> (64u32.strict_sub(bit_size)));
226
227 let mut dividend = (crc << bit_size) ^ (v << 32);
248 while dividend.leading_zeros() <= polynomial.leading_zeros() {
249 dividend ^= (polynomial << polynomial.leading_zeros()) >> dividend.leading_zeros();
250 }
251
252 u32::try_from(dividend).unwrap().reverse_bits()
253}
254
255pub(crate) mod aes {
257 const SBOX: [u8; 256] = [
263 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab,
264 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4,
265 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71,
266 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2,
267 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6,
268 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb,
269 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45,
270 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
271 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44,
272 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a,
273 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49,
274 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d,
275 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25,
276 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e,
277 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1,
278 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
279 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb,
280 0x16,
281 ];
282
283 pub(crate) fn sub_word(word: u32) -> u32 {
285 let bytes = word.to_ne_bytes().map(|b| SBOX[usize::from(b)]);
286 u32::from_ne_bytes(bytes)
287 }
288}
289
290pub(crate) mod sha256 {
293 pub(crate) fn sigma0(x: u32) -> u32 {
294 x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
295 }
296
297 pub(crate) fn sigma1(x: u32) -> u32 {
298 x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
299 }
300
301 pub(crate) fn round(state: [u32; 8], wk: u32) -> [u32; 8] {
303 let [a, b, c, d, e, f, g, h] = state;
304
305 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
306 let ch = (e & f) ^ ((!e) & g);
307 let t1 = s1.wrapping_add(ch).wrapping_add(wk).wrapping_add(h);
308
309 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
310 let maj = (a & b) ^ (a & c) ^ (b & c);
311 let t2 = s0.wrapping_add(maj);
312
313 [
314 t1.wrapping_add(t2), a, b, c, d.wrapping_add(t1), e, f, g, ]
323 }
324}