1use rustc_span::Symbol;
2
3use super::{
4 ShiftOp, packssdw, packsswb, packusdw, packuswb, pavg, permute, permute2, pmaddbw, pmaddwd,
5 psadbw, pshufb, shift_simd_by_simd,
6};
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11 fn emulate_x86_avx512_intrinsic(
12 &mut self,
13 link_name: Symbol,
14 args: &[OpTy<'tcx>],
15 dest: &MPlaceTy<'tcx>,
16 ) -> InterpResult<'tcx, EmulateItemResult> {
17 let this = self.eval_context_mut();
18 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx512.").unwrap();
20
21 match unprefixed_name {
22 "psllv.q.512" | "psrlv.q.512" => {
24 this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
25 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
26 let which = match unprefixed_name {
27 "psllv.q.512" => ShiftOp::Left,
28 "psrlv.q.512" => ShiftOp::RightLogic,
29 _ => unreachable!(),
30 };
31 shift_simd_by_simd(this, left, right, which, dest)?;
32 }
33 "pternlog.d.128" | "pternlog.d.256" | "pternlog.d.512" | "pternlog.q.128"
35 | "pternlog.q.256" | "pternlog.q.512" => {
36 this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
37 if !unprefixed_name.ends_with(".512") {
38 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
39 }
40
41 let [a, b, c, imm8] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
42
43 assert_eq!(dest.layout, a.layout);
44 assert_eq!(dest.layout, b.layout);
45 assert_eq!(dest.layout, c.layout);
46
47 let (a, _a_len) = this.project_to_simd(a)?;
63 let (b, _b_len) = this.project_to_simd(b)?;
64 let (c, _c_len) = this.project_to_simd(c)?;
65 let (dest, dest_len) = this.project_to_simd(dest)?;
66
67 let tern = |xa: u128, xb: u128, xc: u128, imm: u128, bits: u64| -> u128 {
69 let mut out = 0u128;
70 for bit in 0..bits {
72 let ia = (xa >> bit) & 1;
73 let ib = (xb >> bit) & 1;
74 let ic = (xc >> bit) & 1;
75 let idx = (ia << 2) | (ib << 1) | ic;
76 let v = (imm >> idx) & 1;
77 out |= v << bit;
78 }
79 out
80 };
81
82 let imm8 = u128::from(this.read_scalar(imm8)?.to_u32()? & 0xFF);
83 for i in 0..dest_len {
84 let a_lane = this.project_index(&a, i)?;
85 let b_lane = this.project_index(&b, i)?;
86 let c_lane = this.project_index(&c, i)?;
87 let d_lane = this.project_index(&dest, i)?;
88
89 let size = d_lane.layout.size;
90 let va = this.read_scalar(&a_lane)?.to_bits(size)?;
91 let vb = this.read_scalar(&b_lane)?.to_bits(size)?;
92 let vc = this.read_scalar(&c_lane)?.to_bits(size)?;
93
94 let r = tern(va, vb, vc, imm8, size.bits());
95 this.write_scalar(Scalar::from_uint(r, size), &d_lane)?;
96 }
97 }
98 "psad.bw.512" => {
100 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
101
102 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
103
104 psadbw(this, left, right, dest)?
105 }
106 "pmaddw.d.512" => {
108 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
109
110 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
111
112 pmaddwd(this, left, right, dest)?;
113 }
114 "pmaddubs.w.512" => {
116 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
117
118 pmaddbw(this, left, right, dest)?;
119 }
120 "permvar.si.512" | "permvar.di.512" => {
122 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
123
124 permute(this, left, right, dest)?;
125 }
126 "permvar.qi.512" | "permvar.qi.256" | "permvar.qi.128" => {
127 this.expect_target_feature_for_intrinsic(link_name, "avx512vbmi")?;
128 if !unprefixed_name.ends_with("512") {
129 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
130 }
131
132 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
133
134 permute(this, left, right, dest)?;
135 }
136 "vpermi2var.d.512" | "vpermi2var.q.512" => {
138 this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
139 let [left, indices, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
140
141 permute2(this, left, indices, right, dest)?;
142 }
143 "vpermi2var.qi.256" | "vpermi2var.qi.512" => {
145 this.expect_target_feature_for_intrinsic(link_name, "avx512vbmi")?;
146 if unprefixed_name.ends_with(".256") {
147 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
148 }
149
150 let [left, indices, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
151
152 permute2(this, left, indices, right, dest)?;
153 }
154 "pshuf.b.512" => {
156 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
157
158 pshufb(this, left, right, dest)?;
159 }
160
161 "vpdpbusd.512" | "vpdpbusd.256" | "vpdpbusd.128" => {
163 this.expect_target_feature_for_intrinsic(link_name, "avx512vnni")?;
164 if matches!(unprefixed_name, "vpdpbusd.128" | "vpdpbusd.256") {
165 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
166 }
167
168 let [src, a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
169
170 vpdpbusd(this, src, a, b, dest)?;
171 }
172 "packsswb.512" => {
174 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
175
176 let [a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
177
178 packsswb(this, a, b, dest)?;
179 }
180 "packuswb.512" => {
182 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
183
184 let [a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
185
186 packuswb(this, a, b, dest)?;
187 }
188 "packssdw.512" => {
190 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
191
192 let [a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
193
194 packssdw(this, a, b, dest)?;
195 }
196 "packusdw.512" => {
198 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
199
200 let [a, b] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
201
202 packusdw(this, a, b, dest)?;
203 }
204 "pavg.b.512" | "pavg.w.512" => {
207 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
208
209 let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
210
211 pavg(this, left, right, dest)?;
212 }
213 _ => return interp_ok(EmulateItemResult::NotSupported),
214 }
215 interp_ok(EmulateItemResult::NeedsReturn)
216 }
217}
218
219fn vpdpbusd<'tcx>(
228 ecx: &mut crate::MiriInterpCx<'tcx>,
229 src: &OpTy<'tcx>,
230 a: &OpTy<'tcx>,
231 b: &OpTy<'tcx>,
232 dest: &MPlaceTy<'tcx>,
233) -> InterpResult<'tcx, ()> {
234 let (src, src_len) = ecx.project_to_simd(src)?;
235 let (a, a_len) = ecx.project_to_simd(a)?;
236 let (b, b_len) = ecx.project_to_simd(b)?;
237 let (dest, dest_len) = ecx.project_to_simd(dest)?;
238
239 assert_eq!(src_len, dest_len);
243 assert_eq!(a_len, dest_len.strict_mul(4));
244 assert_eq!(b_len, a_len);
245
246 for i in 0..dest_len {
247 let src = ecx.read_scalar(&ecx.project_index(&src, i)?)?.to_i32()?;
248 let dest = ecx.project_index(&dest, i)?;
249
250 let mut intermediate_sum: i32 = 0;
251 for j in 0..4 {
252 let idx = i.strict_mul(4).strict_add(j);
253 let a = ecx.read_scalar(&ecx.project_index(&a, idx)?)?.to_u8()?;
254 let b = ecx.read_scalar(&ecx.project_index(&b, idx)?)?.to_i8()?;
255
256 let product = i32::from(a).strict_mul(i32::from(b));
257 intermediate_sum = intermediate_sum.strict_add(product);
258 }
259
260 let res = Scalar::from_i32(intermediate_sum.wrapping_add(src));
262 ecx.write_scalar(res, &dest)?;
263 }
264
265 interp_ok(())
266}