1use rustc_middle::mir::BinOp;
2use rustc_span::Symbol;
3
4use crate::intrinsics::math::{compute_crc32, sha256};
5use crate::*;
6
7impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
8pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9 fn emulate_aarch64_intrinsic(
10 &mut self,
11 link_name: Symbol,
12 args: &[OpTy<'tcx>],
13 dest: &MPlaceTy<'tcx>,
14 ) -> InterpResult<'tcx, EmulateItemResult> {
15 let ecx = self.eval_context_mut();
16 let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap();
18 match unprefixed_name {
19 "neon.umaxp.v16i8" => {
24 let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
25
26 let (left, left_len) = ecx.project_to_simd(left)?;
27 let (right, right_len) = ecx.project_to_simd(right)?;
28 let (dest, lane_count) = ecx.project_to_simd(dest)?;
29 assert_eq!(left_len, right_len);
30 assert_eq!(lane_count, left_len);
31
32 for lane_idx in 0..lane_count {
33 let src = if lane_idx < (lane_count / 2) { &left } else { &right };
34 let src_idx = lane_idx.strict_rem(lane_count / 2);
35
36 let lhs_lane =
37 ecx.read_immediate(&ecx.project_index(src, src_idx.strict_mul(2))?)?;
38 let rhs_lane = ecx.read_immediate(
39 &ecx.project_index(src, src_idx.strict_mul(2).strict_add(1))?,
40 )?;
41
42 let res_lane =
44 if ecx.binary_op(BinOp::Gt, &lhs_lane, &rhs_lane)?.to_scalar().to_bool()? {
45 lhs_lane
46 } else {
47 rhs_lane
48 };
49
50 let dest = ecx.project_index(&dest, lane_idx)?;
51 ecx.write_immediate(*res_lane, &dest)?;
52 }
53 }
54
55 name if name.starts_with("neon.addp.") => {
63 let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
64
65 let (left, left_len) = ecx.project_to_simd(left)?;
66 let (right, right_len) = ecx.project_to_simd(right)?;
67 let (dest, dest_len) = ecx.project_to_simd(dest)?;
68
69 assert_eq!(left_len, right_len);
70 assert_eq!(left_len, dest_len);
71
72 assert_eq!(left.layout, right.layout);
73 assert_eq!(left.layout, dest.layout);
74
75 assert!(dest_len.is_multiple_of(2));
76 let half_len = dest_len.strict_div(2);
77
78 for lane_idx in 0..dest_len {
79 let (src, src_pair_idx) = if lane_idx < half_len {
81 (&left, lane_idx)
82 } else {
83 (&right, lane_idx.strict_sub(half_len))
84 };
85 let i = src_pair_idx.strict_mul(2);
87
88 let lhs = ecx.read_immediate(&ecx.project_index(src, i)?)?;
89 let rhs = ecx.read_immediate(&ecx.project_index(src, i.strict_add(1))?)?;
90
91 let sum = ecx.binary_op(BinOp::Add, &lhs, &rhs)?;
93
94 let dst_lane = ecx.project_index(&dest, lane_idx)?;
95 ecx.write_immediate(*sum, &dst_lane)?;
96 }
97 }
98
99 name if name.starts_with("neon.uaddlp.") => {
107 let [src] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
108
109 let (src, src_len) = ecx.project_to_simd(src)?;
110 let (dest, dest_len) = ecx.project_to_simd(dest)?;
111
112 assert_eq!(src_len, dest_len.strict_mul(2));
114
115 let src_elem_size = src.layout.field(ecx, 0).size;
116 let dest_elem_size = dest.layout.field(ecx, 0).size;
117
118 assert_eq!(dest_elem_size.bytes(), src_elem_size.bytes().strict_mul(2));
120
121 for dest_idx in 0..dest_len {
122 let src_idx = dest_idx.strict_mul(2);
123
124 let a_scalar = ecx.read_scalar(&ecx.project_index(&src, src_idx)?)?;
125 let b_scalar =
126 ecx.read_scalar(&ecx.project_index(&src, src_idx.strict_add(1))?)?;
127
128 let a_val = a_scalar.to_uint(src_elem_size)?;
129 let b_val = b_scalar.to_uint(src_elem_size)?;
130
131 let sum = a_val.strict_add(b_val);
134
135 let dst_lane = ecx.project_index(&dest, dest_idx)?;
136 ecx.write_scalar(Scalar::from_uint(sum, dest_elem_size), &dst_lane)?;
137 }
138 }
139
140 name if name.starts_with("neon.sqdmulh.") => {
150 let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
151
152 let (left, left_len) = ecx.project_to_simd(left)?;
153 let (right, right_len) = ecx.project_to_simd(right)?;
154 let (dest, dest_len) = ecx.project_to_simd(dest)?;
155 assert_eq!(left_len, right_len);
156 assert_eq!(left_len, dest_len);
157
158 let elem_size = dest.layout.field(ecx, 0).size;
159 let bits = elem_size.bits();
160 let min = elem_size.signed_int_min();
161 let max = elem_size.signed_int_max();
162
163 for i in 0..dest_len {
164 let a = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_int(elem_size)?;
165 let b = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(elem_size)?;
166
167 let doubled = a.strict_mul(b).strict_mul(2);
169 let res = (doubled >> bits).clamp(min, max);
170
171 ecx.write_scalar(
172 Scalar::from_int(res, elem_size),
173 &ecx.project_index(&dest, i)?,
174 )?;
175 }
176 }
177
178 _ if unprefixed_name.starts_with("neon.tbl") => {
192 let (table_segments, indices) = match unprefixed_name {
195 "neon.tbl1.v8i8" | "neon.tbl1.v16i8" => {
196 let [table, indices] =
197 ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
198 let (table, len) = ecx.project_to_simd(table)?;
199 assert_eq!(len, 16);
200 (vec![table], indices)
201 }
202 "neon.tbl2.v8i8" | "neon.tbl2.v16i8" => {
203 let [table0, table1, indices] =
204 ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
205 let (table0, len0) = ecx.project_to_simd(table0)?;
206 let (table1, len1) = ecx.project_to_simd(table1)?;
207 assert_eq!([len0, len1], [16; 2]);
208 (vec![table0, table1], indices)
209 }
210 "neon.tbl3.v8i8" | "neon.tbl3.v16i8" => {
211 let [table0, table1, table2, indices] =
212 ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
213 let (table0, len0) = ecx.project_to_simd(table0)?;
214 let (table1, len1) = ecx.project_to_simd(table1)?;
215 let (table2, len2) = ecx.project_to_simd(table2)?;
216 assert_eq!([len0, len1, len2], [16; 3]);
217 (vec![table0, table1, table2], indices)
218 }
219 "neon.tbl4.v8i8" | "neon.tbl4.v16i8" => {
220 let [table0, table1, table2, table3, indices] =
221 ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
222 let (table0, len0) = ecx.project_to_simd(table0)?;
223 let (table1, len1) = ecx.project_to_simd(table1)?;
224 let (table2, len2) = ecx.project_to_simd(table2)?;
225 let (table3, len3) = ecx.project_to_simd(table3)?;
226 assert_eq!([len0, len1, len2, len3], [16; 4]);
227 (vec![table0, table1, table2, table3], indices)
228 }
229 _ => unreachable!(),
230 };
231
232 let (indices, idx_len) = ecx.project_to_simd(indices)?;
233 let (dest, dest_len) = ecx.project_to_simd(dest)?;
234 assert_eq!(idx_len, dest_len);
235
236 for i in 0..dest_len {
237 let idx = ecx.read_immediate(&ecx.project_index(&indices, i)?)?;
238 let idx = idx.to_scalar().to_u8()?;
239
240 let val = if usize::from(idx) < table_segments.len().strict_mul(16) {
242 let table = &table_segments[usize::from(idx.strict_div(16))];
243 let lane = u64::from(idx.strict_rem(16));
244 let t = ecx.read_immediate(&ecx.project_index(table, lane)?)?;
245 t.to_scalar()
246 } else {
247 Scalar::from_u8(0)
248 };
249
250 ecx.write_scalar(val, &ecx.project_index(&dest, i)?)?;
251 }
252 }
253 "crc32b" | "crc32h" | "crc32w" | "crc32x" | "crc32cb" | "crc32ch" | "crc32cw"
259 | "crc32cx" => {
260 ecx.expect_target_feature_for_intrinsic(link_name, "crc")?;
261 let (bit_size, polynomial): (u32, u128) = match unprefixed_name {
265 "crc32b" => (8, 0x104C11DB7),
266 "crc32h" => (16, 0x104C11DB7),
267 "crc32w" => (32, 0x104C11DB7),
268 "crc32x" => (64, 0x104C11DB7),
269 "crc32cb" => (8, 0x11EDC6F41),
270 "crc32ch" => (16, 0x11EDC6F41),
271 "crc32cw" => (32, 0x11EDC6F41),
272 "crc32cx" => (64, 0x11EDC6F41),
273 _ => unreachable!(),
274 };
275
276 let [crc, data] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
277 let crc = ecx.read_scalar(crc)?;
278 let data = ecx.read_scalar(data)?;
279
280 let crc = crc.to_u32()?;
287 let data = if bit_size == 64 { data.to_u64()? } else { u64::from(data.to_u32()?) };
288
289 let result = compute_crc32(crc, data, bit_size, polynomial);
290 ecx.write_scalar(Scalar::from_u32(result), dest)?;
291 }
292 "neon.pmull64" => {
300 ecx.expect_target_feature_for_intrinsic(link_name, "aes")?;
303
304 let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
305 let left = ecx.read_scalar(left)?.to_u64()?;
306 let right = ecx.read_scalar(right)?.to_u64()?;
307
308 let result = left.widening_carryless_mul(right);
309
310 let dest = dest.transmute(ecx.machine.layouts.u128, ecx)?;
312 ecx.write_scalar(Scalar::from_u128(result), &dest)?;
313 }
314
315 "crypto.sha256h" => {
318 ecx.expect_target_feature_for_intrinsic(link_name, "sha2")?;
319
320 let [abcd, efgh, wk] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
321
322 let (abcd, abcd_len) = ecx.project_to_simd(abcd)?;
323 let (efgh, efgh_len) = ecx.project_to_simd(efgh)?;
324 let (wk, wk_len) = ecx.project_to_simd(wk)?;
325 let (dest, dest_len) = ecx.project_to_simd(dest)?;
326
327 assert_eq!(abcd_len, 4);
328 assert_eq!(efgh_len, 4);
329 assert_eq!(wk_len, 4);
330 assert_eq!(dest_len, 4);
331
332 let abcd: [u32; 4] = read_u32x4(ecx, &abcd)?;
333 let efgh: [u32; 4] = read_u32x4(ecx, &efgh)?;
334 let wk: [u32; 4] = read_u32x4(ecx, &wk)?;
335
336 let result = sha256h(abcd, efgh, wk);
337
338 write_u32x4(ecx, &dest, result)?;
339 }
340 "crypto.sha256h2" => {
343 ecx.expect_target_feature_for_intrinsic(link_name, "sha2")?;
344
345 let [efgh, abcd, wk] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
346
347 let (efgh, efgh_len) = ecx.project_to_simd(efgh)?;
348 let (abcd, abcd_len) = ecx.project_to_simd(abcd)?;
349 let (wk, wk_len) = ecx.project_to_simd(wk)?;
350 let (dest, dest_len) = ecx.project_to_simd(dest)?;
351
352 assert_eq!(efgh_len, 4);
353 assert_eq!(abcd_len, 4);
354 assert_eq!(wk_len, 4);
355 assert_eq!(dest_len, 4);
356
357 let efgh: [u32; 4] = read_u32x4(ecx, &efgh)?;
358 let abcd: [u32; 4] = read_u32x4(ecx, &abcd)?;
359 let wk: [u32; 4] = read_u32x4(ecx, &wk)?;
360
361 let result = sha256h2(efgh, abcd, wk);
362
363 write_u32x4(ecx, &dest, result)?;
364 }
365 "crypto.sha256su0" => {
368 ecx.expect_target_feature_for_intrinsic(link_name, "sha2")?;
369
370 let [a, b] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
371
372 let (a, a_len) = ecx.project_to_simd(a)?;
373 let (b, b_len) = ecx.project_to_simd(b)?;
374 let (dest, dest_len) = ecx.project_to_simd(dest)?;
375
376 assert_eq!(a_len, 4);
377 assert_eq!(b_len, 4);
378 assert_eq!(dest_len, 4);
379
380 let a: [u32; 4] = read_u32x4(ecx, &a)?;
381 let b: [u32; 4] = read_u32x4(ecx, &b)?;
382
383 let result = sha256su0(a, b);
384
385 write_u32x4(ecx, &dest, result)?;
386 }
387 "crypto.sha256su1" => {
390 ecx.expect_target_feature_for_intrinsic(link_name, "sha2")?;
391
392 let [a, b, c] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
393
394 let (a, a_len) = ecx.project_to_simd(a)?;
395 let (b, b_len) = ecx.project_to_simd(b)?;
396 let (c, c_len) = ecx.project_to_simd(c)?;
397 let (dest, dest_len) = ecx.project_to_simd(dest)?;
398
399 assert_eq!(a_len, 4);
400 assert_eq!(b_len, 4);
401 assert_eq!(c_len, 4);
402 assert_eq!(dest_len, 4);
403
404 let a: [u32; 4] = read_u32x4(ecx, &a)?;
405 let b: [u32; 4] = read_u32x4(ecx, &b)?;
406 let c: [u32; 4] = read_u32x4(ecx, &c)?;
407
408 let result = sha256su1(a, b, c);
409
410 write_u32x4(ecx, &dest, result)?;
411 }
412 _ => return interp_ok(EmulateItemResult::NotSupported),
413 }
414 interp_ok(EmulateItemResult::NeedsReturn)
415 }
416}
417
418fn read_u32x4<'c>(ecx: &mut MiriInterpCx<'c>, vec: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
420 let mut res = [0; 4];
421 for (i, dst) in res.iter_mut().enumerate() {
422 let projected = &ecx.project_index(vec, i.try_into().unwrap())?;
423 *dst = ecx.read_scalar(projected)?.to_u32()?;
424 }
425 interp_ok(res)
426}
427
428fn write_u32x4<'c>(
429 ecx: &mut MiriInterpCx<'c>,
430 dest: &MPlaceTy<'c>,
431 val: [u32; 4],
432) -> InterpResult<'c, ()> {
433 for (i, part) in val.into_iter().enumerate() {
434 let projected = &ecx.project_index(dest, i.to_u64())?;
435 ecx.write_scalar(Scalar::from_u32(part), projected)?;
436 }
437 interp_ok(())
438}
439
440fn sha256su0(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] {
441 [
442 v0[0].wrapping_add(sha256::sigma0(v0[1])),
443 v0[1].wrapping_add(sha256::sigma0(v0[2])),
444 v0[2].wrapping_add(sha256::sigma0(v0[3])),
445 v0[3].wrapping_add(sha256::sigma0(v1[0])),
446 ]
447}
448
449fn sha256su1(v0: [u32; 4], v1: [u32; 4], v2: [u32; 4]) -> [u32; 4] {
450 let r0 = v0[0].wrapping_add(v1[1]).wrapping_add(sha256::sigma1(v2[2]));
451 let r1 = v0[1].wrapping_add(v1[2]).wrapping_add(sha256::sigma1(v2[3]));
452 let r2 = v0[2].wrapping_add(v1[3]).wrapping_add(sha256::sigma1(r0));
453 let r3 = v0[3].wrapping_add(v2[0]).wrapping_add(sha256::sigma1(r1));
454 [r0, r1, r2, r3]
455}
456
457fn sha256hash(abcd: [u32; 4], efgh: [u32; 4], wk: [u32; 4]) -> ([u32; 4], [u32; 4]) {
460 let mut state = [abcd[0], abcd[1], abcd[2], abcd[3], efgh[0], efgh[1], efgh[2], efgh[3]];
461 for &wk_i in &wk {
462 state = sha256::round(state, wk_i);
463 }
464 ([state[0], state[1], state[2], state[3]], [state[4], state[5], state[6], state[7]])
465}
466
467fn sha256h(abcd: [u32; 4], efgh: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
468 sha256hash(abcd, efgh, wk).0
469}
470
471fn sha256h2(efgh: [u32; 4], abcd: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
473 sha256hash(abcd, efgh, wk).1
474}