miri/shims/x86/
bmi.rs

1use rustc_abi::CanonAbi;
2use rustc_middle::ty::Ty;
3use rustc_span::Symbol;
4use rustc_target::callconv::FnAbi;
5use rustc_target::spec::Arch;
6
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11    fn emulate_x86_bmi_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
20        // Prefix should have already been checked.
21        let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.bmi.").unwrap();
22
23        // The intrinsics are suffixed with the bit size of their operands.
24        let (is_64_bit, unprefixed_name) = if unprefixed_name.ends_with("64") {
25            (true, unprefixed_name.strip_suffix(".64").unwrap_or(""))
26        } else {
27            (false, unprefixed_name.strip_suffix(".32").unwrap_or(""))
28        };
29
30        // All intrinsics of the "bmi" namespace belong to the "bmi2" ISA extension.
31        // The exception is "bextr", which belongs to "bmi1".
32        let target_feature = if unprefixed_name == "bextr" { "bmi1" } else { "bmi2" };
33        this.expect_target_feature_for_intrinsic(link_name, target_feature)?;
34
35        if is_64_bit && this.tcx.sess.target.arch != Arch::X86_64 {
36            return interp_ok(EmulateItemResult::NotSupported);
37        }
38
39        let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
40        let left = this.read_scalar(left)?;
41        let right = this.read_scalar(right)?;
42
43        let left = if is_64_bit { left.to_u64()? } else { u64::from(left.to_u32()?) };
44        let right = if is_64_bit { right.to_u64()? } else { u64::from(right.to_u32()?) };
45
46        let result = match unprefixed_name {
47            // Extract a contigous range of bits from an unsigned integer.
48            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_bextr_u32
49            "bextr" => {
50                let start = u32::try_from(right & 0xff).unwrap();
51                let len = u32::try_from((right >> 8) & 0xff).unwrap();
52                let shifted = left.checked_shr(start).unwrap_or(0);
53                // Keep the `len` lowest bits of `shifted`, or all bits if `len` is too big.
54                if len >= 64 { shifted } else { shifted & 1u64.wrapping_shl(len).wrapping_sub(1) }
55            }
56            // Create a copy of an unsigned integer with bits above a certain index cleared.
57            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_bzhi_u32
58            "bzhi" => {
59                let index = u32::try_from(right & 0xff).unwrap();
60                // Keep the `index` lowest bits of `left`, or all bits if `index` is too big.
61                if index >= 64 { left } else { left & 1u64.wrapping_shl(index).wrapping_sub(1) }
62            }
63            // Extract bit values of an unsigned integer at positions marked by a mask.
64            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_pext_u32
65            "pext" => {
66                let mut mask = right;
67                let mut i = 0u32;
68                let mut result = 0;
69                // Iterate over the mask one 1-bit at a time, from
70                // the least significant bit to the most significant bit.
71                while mask != 0 {
72                    // Extract the bit marked by the mask's least significant set bit
73                    // and put it at position `i` of the result.
74                    result |= u64::from(left & (1 << mask.trailing_zeros()) != 0) << i;
75                    i = i.wrapping_add(1);
76                    // Clear the least significant set bit.
77                    mask &= mask.wrapping_sub(1);
78                }
79                result
80            }
81            // Deposit bit values of an unsigned integer to positions marked by a mask.
82            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_pdep_u32
83            "pdep" => {
84                let mut mask = right;
85                let mut set = left;
86                let mut result = 0;
87                // Iterate over the mask one 1-bit at a time, from
88                // the least significant bit to the most significant bit.
89                while mask != 0 {
90                    // Put rightmost bit of `set` at the position of the current `mask` bit.
91                    result |= (set & 1) << mask.trailing_zeros();
92                    // Go to next bit of `set`.
93                    set >>= 1;
94                    // Clear the least significant set bit.
95                    mask &= mask.wrapping_sub(1);
96                }
97                result
98            }
99            _ => return interp_ok(EmulateItemResult::NotSupported),
100        };
101
102        let result = if is_64_bit {
103            Scalar::from_u64(result)
104        } else {
105            Scalar::from_u32(u32::try_from(result).unwrap())
106        };
107        this.write_scalar(result, dest)?;
108
109        interp_ok(EmulateItemResult::NeedsReturn)
110    }
111}