miri/intrinsics/x86/sse41.rs
1use rustc_span::Symbol;
2
3use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first, test_bits_masked};
4use crate::*;
5
6impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
7pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8 fn emulate_x86_sse41_intrinsic(
9 &mut self,
10 link_name: Symbol,
11 args: &[OpTy<'tcx>],
12 dest: &MPlaceTy<'tcx>,
13 ) -> InterpResult<'tcx, EmulateItemResult> {
14 let this = self.eval_context_mut();
15 this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?;
16 // Prefix should have already been checked.
17 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse41.").unwrap();
18
19 match unprefixed_name {
20 // Used to implement the _mm_insert_ps function.
21 // Takes one element of `right` and inserts it into `left` and
22 // optionally zero some elements. Source index is specified
23 // in bits `6..=7` of `imm`, destination index is specified in
24 // bits `4..=5` if `imm`, and `i`th bit specifies whether element
25 // `i` is zeroed.
26 "insertps" => {
27 let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
28
29 let (left, left_len) = this.project_to_simd(left)?;
30 let (right, right_len) = this.project_to_simd(right)?;
31 let (dest, dest_len) = this.project_to_simd(dest)?;
32
33 assert_eq!(dest_len, left_len);
34 assert_eq!(dest_len, right_len);
35 assert!(dest_len <= 4);
36
37 let imm = this.read_scalar(imm)?.to_u8()?;
38 let src_index = u64::from((imm >> 6) & 0b11);
39 let dst_index = u64::from((imm >> 4) & 0b11);
40
41 let src_value = this.read_immediate(&this.project_index(&right, src_index)?)?;
42
43 for i in 0..dest_len {
44 let dest = this.project_index(&dest, i)?;
45
46 if imm & (1 << i) != 0 {
47 // zeroed
48 this.write_scalar(Scalar::from_u32(0), &dest)?;
49 } else if i == dst_index {
50 // copy from `right` at specified index
51 this.write_immediate(*src_value, &dest)?;
52 } else {
53 // copy from `left`
54 this.copy_op(&this.project_index(&left, i)?, &dest)?;
55 }
56 }
57 }
58 // Used to implement the _mm_packus_epi32 function.
59 // Concatenates two 32-bit signed integer vectors and converts
60 // the result to a 16-bit unsigned integer vector with saturation.
61 "packusdw" => {
62 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
63
64 packusdw(this, left, right, dest)?;
65 }
66 // Used to implement the _mm_dp_ps and _mm_dp_pd functions.
67 // Conditionally multiplies the packed floating-point elements in
68 // `left` and `right` using the high 4 bits in `imm`, sums the four
69 // products, and conditionally stores the sum in `dest` using the low
70 // 4 bits of `imm`.
71 "dpps" | "dppd" => {
72 let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
73
74 conditional_dot_product(this, left, right, imm, dest)?;
75 }
76 // Used to implement the _mm_floor_ss, _mm_ceil_ss and _mm_round_ss
77 // functions. Rounds the first element of `right` according to `rounding`
78 // and copies the remaining elements from `left`.
79 "round.ss" => {
80 let [left, right, rounding] =
81 this.check_shim_sig_llvm_intrinsic(link_name, args)?;
82
83 round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
84 }
85 // Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
86 // functions. Rounds the elements of `op` according to `rounding`.
87 "round.ps" => {
88 let [op, rounding] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
89
90 round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
91 }
92 // Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd
93 // functions. Rounds the first element of `right` according to `rounding`
94 // and copies the remaining elements from `left`.
95 "round.sd" => {
96 let [left, right, rounding] =
97 this.check_shim_sig_llvm_intrinsic(link_name, args)?;
98
99 round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
100 }
101 // Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
102 // functions. Rounds the elements of `op` according to `rounding`.
103 "round.pd" => {
104 let [op, rounding] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
105
106 round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
107 }
108 // Used to implement the _mm_minpos_epu16 function.
109 // Find the minimum unsigned 16-bit integer in `op` and
110 // returns its value and position.
111 "phminposuw" => {
112 let [op] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
113
114 let (op, op_len) = this.project_to_simd(op)?;
115 let (dest, dest_len) = this.project_to_simd(dest)?;
116
117 // Find minimum
118 let mut min_value = u16::MAX;
119 let mut min_index = 0;
120 for i in 0..op_len {
121 let op = this.read_scalar(&this.project_index(&op, i)?)?.to_u16()?;
122 if op < min_value {
123 min_value = op;
124 min_index = i;
125 }
126 }
127
128 // Write value and index
129 this.write_scalar(Scalar::from_u16(min_value), &this.project_index(&dest, 0)?)?;
130 this.write_scalar(
131 Scalar::from_u16(min_index.try_into().unwrap()),
132 &this.project_index(&dest, 1)?,
133 )?;
134 // Fill remainder with zeros
135 for i in 2..dest_len {
136 this.write_scalar(Scalar::from_u16(0), &this.project_index(&dest, i)?)?;
137 }
138 }
139 // Used to implement the _mm_mpsadbw_epu8 function.
140 // Compute the sum of absolute differences of quadruplets of unsigned
141 // 8-bit integers in `left` and `right`, and store the 16-bit results
142 // in `right`. Quadruplets are selected from `left` and `right` with
143 // offsets specified in `imm`.
144 // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
145 "mpsadbw" => {
146 let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
147
148 mpsadbw(this, left, right, imm, dest)?;
149 }
150 // Used to implement the _mm_testnzc_si128 function.
151 // Tests `(op & mask) != 0 && (op & mask) != mask`
152 "ptestnzc" => {
153 let [op, mask] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
154
155 let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
156 let res = !all_zero && !masked_set;
157
158 this.write_scalar(Scalar::from_i32(res.into()), dest)?;
159 }
160 _ => return interp_ok(EmulateItemResult::NotSupported),
161 }
162 interp_ok(EmulateItemResult::NeedsReturn)
163 }
164}