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}