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