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 sha256 {
258 pub(crate) fn sigma0(x: u32) -> u32 {
259 x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3)
260 }
261
262 pub(crate) fn sigma1(x: u32) -> u32 {
263 x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10)
264 }
265
266 pub(crate) fn round(state: [u32; 8], wk: u32) -> [u32; 8] {
268 let [a, b, c, d, e, f, g, h] = state;
269
270 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
271 let ch = (e & f) ^ ((!e) & g);
272 let t1 = s1.wrapping_add(ch).wrapping_add(wk).wrapping_add(h);
273
274 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
275 let maj = (a & b) ^ (a & c) ^ (b & c);
276 let t2 = s0.wrapping_add(maj);
277
278 [
279 t1.wrapping_add(t2), a, b, c, d.wrapping_add(t1), e, f, g, ]
288 }
289}