Skip to main content

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}