Skip to main content

miri/shims/
loongarch.rs

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