1use rustc_span::Symbol;
2
3use super::{
4 packssdw, packsswb, packusdw, packuswb, permute, permute2, pmaddbw, pmaddwd, psadbw, pshufb,
5};
6use crate::*;
7
8impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
9pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10 fn emulate_x86_avx512_intrinsic(
11 &mut self,
12 link_name: Symbol,
13 args: &[OpTy<'tcx>],
14 dest: &MPlaceTy<'tcx>,
15 ) -> InterpResult<'tcx, EmulateItemResult> {
16 let this = self.eval_context_mut();
17 let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx512.").unwrap();
19
20 match unprefixed_name {
21 "pternlog.d.128" | "pternlog.d.256" | "pternlog.d.512" => {
23 this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
24 if matches!(unprefixed_name, "pternlog.d.128" | "pternlog.d.256") {
25 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
26 }
27
28 let [a, b, c, imm8] = this.check_shim_sig_unadjusted(link_name, args)?;
29
30 assert_eq!(dest.layout, a.layout);
31 assert_eq!(dest.layout, b.layout);
32 assert_eq!(dest.layout, c.layout);
33
34 let (a, _a_len) = this.project_to_simd(a)?;
45 let (b, _b_len) = this.project_to_simd(b)?;
46 let (c, _c_len) = this.project_to_simd(c)?;
47 let (dest, dest_len) = this.project_to_simd(dest)?;
48
49 let tern = |xa: u32, xb: u32, xc: u32, imm: u32| -> u32 {
51 let mut out = 0u32;
52 for bit in 0..32 {
54 let ia = (xa >> bit) & 1;
55 let ib = (xb >> bit) & 1;
56 let ic = (xc >> bit) & 1;
57 let idx = (ia << 2) | (ib << 1) | ic;
58 let v = (imm >> idx) & 1;
59 out |= v << bit;
60 }
61 out
62 };
63
64 let imm8 = this.read_scalar(imm8)?.to_u32()? & 0xFF;
65 for i in 0..dest_len {
66 let a_lane = this.project_index(&a, i)?;
67 let b_lane = this.project_index(&b, i)?;
68 let c_lane = this.project_index(&c, i)?;
69 let d_lane = this.project_index(&dest, i)?;
70
71 let va = this.read_scalar(&a_lane)?.to_u32()?;
72 let vb = this.read_scalar(&b_lane)?.to_u32()?;
73 let vc = this.read_scalar(&c_lane)?.to_u32()?;
74
75 let r = tern(va, vb, vc, imm8);
76 this.write_scalar(Scalar::from_u32(r), &d_lane)?;
77 }
78 }
79 "psad.bw.512" => {
81 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
82
83 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
84
85 psadbw(this, left, right, dest)?
86 }
87 "pmaddw.d.512" => {
89 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
90
91 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
92
93 pmaddwd(this, left, right, dest)?;
94 }
95 "pmaddubs.w.512" => {
97 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
98
99 pmaddbw(this, left, right, dest)?;
100 }
101 "permvar.si.512" | "permvar.di.512" => {
103 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
104
105 permute(this, left, right, dest)?;
106 }
107 "permvar.qi.512" | "permvar.qi.256" | "permvar.qi.128" => {
108 this.expect_target_feature_for_intrinsic(link_name, "avx512vbmi")?;
109 if !unprefixed_name.ends_with("512") {
110 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
111 }
112
113 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
114
115 permute(this, left, right, dest)?;
116 }
117 "vpermi2var.q.512" => {
119 let [left, indices, right] = this.check_shim_sig_unadjusted(link_name, args)?;
120
121 permute2(this, left, indices, right, dest)?;
122 }
123 "vpermi2var.qi.512" => {
125 this.expect_target_feature_for_intrinsic(link_name, "avx512vbmi")?;
126
127 let [left, indices, right] = this.check_shim_sig_unadjusted(link_name, args)?;
128
129 permute2(this, left, indices, right, dest)?;
130 }
131 "pshuf.b.512" => {
133 let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?;
134
135 pshufb(this, left, right, dest)?;
136 }
137
138 "vpdpbusd.512" | "vpdpbusd.256" | "vpdpbusd.128" => {
140 this.expect_target_feature_for_intrinsic(link_name, "avx512vnni")?;
141 if matches!(unprefixed_name, "vpdpbusd.128" | "vpdpbusd.256") {
142 this.expect_target_feature_for_intrinsic(link_name, "avx512vl")?;
143 }
144
145 let [src, a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
146
147 vpdpbusd(this, src, a, b, dest)?;
148 }
149 "packsswb.512" => {
151 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
152
153 let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
154
155 packsswb(this, a, b, dest)?;
156 }
157 "packuswb.512" => {
159 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
160
161 let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
162
163 packuswb(this, a, b, dest)?;
164 }
165 "packssdw.512" => {
167 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
168
169 let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
170
171 packssdw(this, a, b, dest)?;
172 }
173 "packusdw.512" => {
175 this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?;
176
177 let [a, b] = this.check_shim_sig_unadjusted(link_name, args)?;
178
179 packusdw(this, a, b, dest)?;
180 }
181 _ => return interp_ok(EmulateItemResult::NotSupported),
182 }
183 interp_ok(EmulateItemResult::NeedsReturn)
184 }
185}
186
187fn vpdpbusd<'tcx>(
196 ecx: &mut crate::MiriInterpCx<'tcx>,
197 src: &OpTy<'tcx>,
198 a: &OpTy<'tcx>,
199 b: &OpTy<'tcx>,
200 dest: &MPlaceTy<'tcx>,
201) -> InterpResult<'tcx, ()> {
202 let (src, src_len) = ecx.project_to_simd(src)?;
203 let (a, a_len) = ecx.project_to_simd(a)?;
204 let (b, b_len) = ecx.project_to_simd(b)?;
205 let (dest, dest_len) = ecx.project_to_simd(dest)?;
206
207 assert_eq!(src_len, dest_len);
211 assert_eq!(a_len, dest_len.strict_mul(4));
212 assert_eq!(b_len, a_len);
213
214 for i in 0..dest_len {
215 let src = ecx.read_scalar(&ecx.project_index(&src, i)?)?.to_i32()?;
216 let dest = ecx.project_index(&dest, i)?;
217
218 let mut intermediate_sum: i32 = 0;
219 for j in 0..4 {
220 let idx = i.strict_mul(4).strict_add(j);
221 let a = ecx.read_scalar(&ecx.project_index(&a, idx)?)?.to_u8()?;
222 let b = ecx.read_scalar(&ecx.project_index(&b, idx)?)?.to_i8()?;
223
224 let product = i32::from(a).strict_mul(i32::from(b));
225 intermediate_sum = intermediate_sum.strict_add(product);
226 }
227
228 let res = Scalar::from_i32(intermediate_sum.wrapping_add(src));
230 ecx.write_scalar(res, &dest)?;
231 }
232
233 interp_ok(())
234}