miri/intrinsics/loongarch.rs
1use rustc_abi::Size;
2use rustc_span::Symbol;
3
4use crate::intrinsics::math::compute_crc32;
5use crate::*;
6
7impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
8pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9 fn emulate_loongarch_intrinsic(
10 &mut self,
11 link_name: Symbol,
12 args: &[OpTy<'tcx>],
13 dest: &MPlaceTy<'tcx>,
14 ) -> InterpResult<'tcx, EmulateItemResult> {
15 let this = self.eval_context_mut();
16 // Prefix should have already been checked.
17 let unprefixed_name = link_name.as_str().strip_prefix("llvm.loongarch.").unwrap();
18 match unprefixed_name {
19 // Used to implement the crc.w.{b,h,w,d}.w and crcc.w.{b,h,w,d}.w functions.
20 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#crc-check-instructions
21 // These are only available on LA64, not on LA32, and are part of
22 // the LA64 1.0 baseline and therefore always available and don't
23 // require a target feature to be enabled.
24 "crc.w.b.w" | "crc.w.h.w" | "crc.w.w.w" | "crc.w.d.w" | "crcc.w.b.w" | "crcc.w.h.w"
25 | "crcc.w.w.w" | "crcc.w.d.w"
26 if this.tcx.pointer_size().bits() == 64 =>
27 {
28 // The polynomial constants below include the leading 1 bit
29 // (e.g. 0x104C11DB7 instead of 0x04C11DB7) which the the
30 // polynomial division algorithm requires.
31 // Note that Loongson's documentation mentions the numbers
32 // 0xEDB88320 for IEEE802.3 and 0x82F63B78 for Castagnoli,
33 // which is because their docs put the least significant bit
34 // first.
35 let (bit_size, polynomial): (u32, u128) = match unprefixed_name {
36 "crc.w.b.w" => (8, 0x104C11DB7),
37 "crc.w.h.w" => (16, 0x104C11DB7),
38 "crc.w.w.w" => (32, 0x104C11DB7),
39 "crc.w.d.w" => (64, 0x104C11DB7),
40 "crcc.w.b.w" => (8, 0x11EDC6F41),
41 "crcc.w.h.w" => (16, 0x11EDC6F41),
42 "crcc.w.w.w" => (32, 0x11EDC6F41),
43 "crcc.w.d.w" => (64, 0x11EDC6F41),
44 _ => unreachable!(),
45 };
46
47 let [data, crc] = this.check_shim_sig_unadjusted(link_name, args)?;
48 let data = this.read_scalar(data)?;
49 let crc = this.read_scalar(crc)?;
50
51 // The CRC accumulator is always i32. The data argument is i32 for
52 // b/h/w variants and i64 for the d variant, per the LLVM intrinsic
53 // definitions.
54 // https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/IntrinsicsLoongArch.td
55 // LoongArch CRC b/h/w instructions ignore any bits above `bit_size`.
56 // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#crc-check-instructions
57 // Miri's `compute_crc32` requires all higher bits to be zero and may
58 // panic otherwise, so we explicitly mask them off here to reproduce the
59 // hardware behavior.
60 let crc = crc.to_u32()?;
61 let data = if bit_size == 64 {
62 data.to_u64()?
63 } else {
64 Size::from_bits(bit_size).truncate(data.to_u32()?.into()).try_into().unwrap()
65 };
66
67 let result = compute_crc32(crc, data, bit_size, polynomial);
68 this.write_scalar(Scalar::from_u32(result), dest)?;
69 }
70 _ => return interp_ok(EmulateItemResult::NotSupported),
71 }
72 interp_ok(EmulateItemResult::NeedsReturn)
73 }
74}