core/stdarch/crates/core_arch/src/x86/sse4a.rs
1//! `i686`'s Streaming SIMD Extensions 4a (`SSE4a`)
2
3use crate::core_arch::{simd::*, x86::*};
4
5#[cfg(test)]
6use stdarch_test::assert_instr;
7
8#[allow(improper_ctypes)]
9unsafe extern "unadjusted" {
10 #[link_name = "llvm.x86.sse4a.extrq"]
11 fn extrq(x: i64x2, y: i8x16) -> i64x2;
12 #[link_name = "llvm.x86.sse4a.extrqi"]
13 fn extrqi(x: i64x2, len: u8, idx: u8) -> i64x2;
14 #[link_name = "llvm.x86.sse4a.insertq"]
15 fn insertq(x: i64x2, y: i64x2) -> i64x2;
16 #[link_name = "llvm.x86.sse4a.insertqi"]
17 fn insertqi(x: i64x2, y: i64x2, len: u8, idx: u8) -> i64x2;
18}
19
20/// Extracts the bit range specified by `y` from the lower 64 bits of `x`.
21///
22/// The `[13:8]` bits of `y` specify the index of the bit-range to extract. The
23/// `[5:0]` bits of `y` specify the length of the bit-range to extract. All
24/// other bits are ignored.
25///
26/// If the length is zero, it is interpreted as `64`. If the length and index
27/// are zero, the lower 64 bits of `x` are extracted.
28///
29/// If `length == 0 && index > 0` or `length + index > 64` the result is
30/// undefined.
31///
32/// The extracted bits are saved in the least-significant bit positions of the lower
33/// quadword of the destination; the remaining bits in the lower quadword of the
34/// destination register are cleared to 0. The upper quadword of the destination
35/// register is undefined.
36#[inline]
37#[target_feature(enable = "sse4a")]
38#[cfg_attr(test, assert_instr(extrq))]
39#[stable(feature = "simd_x86", since = "1.27.0")]
40pub fn _mm_extract_si64(x: __m128i, y: __m128i) -> __m128i {
41 unsafe { transmute(extrq(x.as_i64x2(), y.as_i8x16())) }
42}
43
44/// Extracts the specified bits from the lower 64 bits of the 128-bit integer vector operand at the
45/// index `idx` and of the length `len`.
46///
47/// `idx` specifies the index of the LSB. `len` specifies the number of bits to extract. If length
48/// and index are both zero, bits `[63:0]` of parameter `x` are extracted. It is a compile-time error
49/// for `len + idx` to be greater than 64 or for `len` to be zero and `idx` to be non-zero.
50///
51/// The extracted bits are saved in the least-significant bit positions of the lower
52/// quadword of the destination; the remaining bits in the lower quadword of the
53/// destination register are cleared to 0. The upper quadword of the destination
54/// register is undefined.
55#[inline]
56#[target_feature(enable = "sse4a")]
57#[cfg_attr(test, assert_instr(extrq, LEN = 5, IDX = 5))]
58#[rustc_legacy_const_generics(1, 2)]
59#[stable(feature = "simd_x86_updates", since = "1.82.0")]
60pub fn _mm_extracti_si64<const LEN: i32, const IDX: i32>(x: __m128i) -> __m128i {
61 // LLVM mentions that it is UB if these are not satisfied
62 static_assert_uimm_bits!(LEN, 6);
63 static_assert_uimm_bits!(IDX, 6);
64 static_assert!((LEN == 0 && IDX == 0) || (LEN != 0 && LEN + IDX <= 64));
65 unsafe { transmute(extrqi(x.as_i64x2(), LEN as u8, IDX as u8)) }
66}
67
68/// Inserts the `[length:0]` bits of `y` into `x` at `index`.
69///
70/// The bits of `y`:
71///
72/// - `[69:64]` specify the `length`,
73/// - `[77:72]` specify the index.
74///
75/// If the `length` is zero it is interpreted as `64`. If `index + length > 64`
76/// or `index > 0 && length == 0` the result is undefined.
77///
78/// The upper 64 bits of the destination are undefined.
79#[inline]
80#[target_feature(enable = "sse4a")]
81#[cfg_attr(test, assert_instr(insertq))]
82#[stable(feature = "simd_x86", since = "1.27.0")]
83pub fn _mm_insert_si64(x: __m128i, y: __m128i) -> __m128i {
84 unsafe { transmute(insertq(x.as_i64x2(), y.as_i64x2())) }
85}
86
87/// Inserts the `len` least-significant bits from the lower 64 bits of the 128-bit integer vector operand `y` into
88/// the lower 64 bits of the 128-bit integer vector operand `x` at the index `idx` and of the length `len`.
89///
90/// `idx` specifies the index of the LSB. `len` specifies the number of bits to insert. If length and index
91/// are both zero, bits `[63:0]` of parameter `x` are replaced with bits `[63:0]` of parameter `y`. It is a
92/// compile-time error for `len + idx` to be greater than 64 or for `len` to be zero and `idx` to be non-zero.
93///
94/// The upper 64 bits of the destination are undefined.
95#[inline]
96#[target_feature(enable = "sse4a")]
97#[cfg_attr(test, assert_instr(insertq, LEN = 5, IDX = 5))]
98#[rustc_legacy_const_generics(2, 3)]
99#[stable(feature = "simd_x86_updates", since = "1.82.0")]
100pub fn _mm_inserti_si64<const LEN: i32, const IDX: i32>(x: __m128i, y: __m128i) -> __m128i {
101 // LLVM mentions that it is UB if these are not satisfied
102 static_assert_uimm_bits!(LEN, 6);
103 static_assert_uimm_bits!(IDX, 6);
104 static_assert!((LEN == 0 && IDX == 0) || (LEN != 0 && LEN + IDX <= 64));
105 unsafe { transmute(insertqi(x.as_i64x2(), y.as_i64x2(), LEN as u8, IDX as u8)) }
106}
107
108/// Non-temporal store of `a.0` into `p`.
109///
110/// Writes 64-bit data to a memory location without polluting the caches.
111///
112/// # Safety of non-temporal stores
113///
114/// After using this intrinsic, but before any other access to the memory that this intrinsic
115/// mutates, a call to [`_mm_sfence`] must be performed by the thread that used the intrinsic. In
116/// particular, functions that call this intrinsic should generally call `_mm_sfence` before they
117/// return.
118///
119/// See [`_mm_sfence`] for details.
120#[inline]
121#[target_feature(enable = "sse4a")]
122#[cfg_attr(test, assert_instr(movntsd))]
123#[stable(feature = "simd_x86", since = "1.27.0")]
124pub unsafe fn _mm_stream_sd(p: *mut f64, a: __m128d) {
125 // see #1541, we should use inline asm to be sure, because LangRef isn't clear enough
126 crate::arch::asm!(
127 vps!("movntsd", ",{a}"),
128 p = in(reg) p,
129 a = in(xmm_reg) a,
130 options(nostack, preserves_flags),
131 );
132}
133
134/// Non-temporal store of `a.0` into `p`.
135///
136/// Writes 32-bit data to a memory location without polluting the caches.
137///
138/// # Safety of non-temporal stores
139///
140/// After using this intrinsic, but before any other access to the memory that this intrinsic
141/// mutates, a call to [`_mm_sfence`] must be performed by the thread that used the intrinsic. In
142/// particular, functions that call this intrinsic should generally call `_mm_sfence` before they
143/// return.
144///
145/// See [`_mm_sfence`] for details.
146#[inline]
147#[target_feature(enable = "sse4a")]
148#[cfg_attr(test, assert_instr(movntss))]
149#[stable(feature = "simd_x86", since = "1.27.0")]
150pub unsafe fn _mm_stream_ss(p: *mut f32, a: __m128) {
151 // see #1541, we should use inline asm to be sure, because LangRef isn't clear enough
152 crate::arch::asm!(
153 vps!("movntss", ",{a}"),
154 p = in(reg) p,
155 a = in(xmm_reg) a,
156 options(nostack, preserves_flags),
157 );
158}
159
160#[cfg(test)]
161mod tests {
162 use crate::core_arch::x86::*;
163 use stdarch_test::simd_test;
164
165 // Normally this requires SSE2, but for tests it does not matter whether we use the instruction.
166 fn _mm_cvtsi128_si64(a: __m128i) -> i64 {
167 unsafe { simd_extract!(a.as_i64x2(), 0) }
168 }
169
170 #[simd_test(enable = "sse4a")]
171 fn test_mm_extract_si64() {
172 let b = 0b0110_0000_0000_i64;
173 // ^^^^ bit range extracted
174 let x = _mm_setr_epi64x(b, 0);
175 let v = 0b001000___00___000100_i64;
176 // ^idx: 2^3 = 8 ^length = 2^2 = 4
177 let y = _mm_setr_epi64x(v, 0);
178 let e = _mm_setr_epi64x(0b0110_i64, 0);
179 let r = _mm_extract_si64(x, y);
180
181 // The upper quadword of the destination register is undefined.
182 let r = _mm_cvtsi128_si64(r);
183 let e = _mm_cvtsi128_si64(e);
184 assert_eq!(r, e);
185 }
186
187 #[simd_test(enable = "sse4a")]
188 fn test_mm_extracti_si64() {
189 let a = _mm_setr_epi64x(0x0123456789abcdef, 0);
190 let r = _mm_extracti_si64::<8, 8>(a);
191 let e = _mm_setr_epi64x(0xcd, 0);
192
193 // The upper quadword of the destination register is undefined.
194 let r = _mm_cvtsi128_si64(r);
195 let e = _mm_cvtsi128_si64(e);
196 assert_eq!(r, e);
197 }
198
199 #[simd_test(enable = "sse4a")]
200 fn test_mm_insert_si64() {
201 let i = 0b0110_i64;
202 // ^^^^ bit range inserted
203 let z = 0b1010_1010_1010i64;
204 // ^^^^ bit range replaced
205 let e = 0b0110_1010_1010i64;
206 // ^^^^ replaced 1010 with 0110
207 let x = _mm_setr_epi64x(z, 0);
208 let expected = _mm_setr_epi64x(e, 0);
209 let v = 0b001000___00___000100_i64;
210 // ^idx: 2^3 = 8 ^length = 2^2 = 4
211 let y = _mm_setr_epi64x(i, v);
212 let r = _mm_insert_si64(x, y);
213
214 // The upper quadword of the destination register is undefined.
215 let r = _mm_cvtsi128_si64(r);
216 let expected = _mm_cvtsi128_si64(expected);
217 assert_eq!(r, expected);
218 }
219
220 #[simd_test(enable = "sse4a")]
221 fn test_mm_inserti_si64() {
222 let a = _mm_setr_epi64x(0x0123456789abcdef, 0);
223 let b = _mm_setr_epi64x(0x0011223344556677, 0);
224 let r = _mm_inserti_si64::<8, 8>(a, b);
225 let e = _mm_setr_epi64x(0x0123456789ab77ef, 0);
226
227 // The upper quadword of the destination register is undefined.
228 let r = _mm_cvtsi128_si64(r);
229 let e = _mm_cvtsi128_si64(e);
230 assert_eq!(r, e);
231 }
232
233 #[repr(align(16))]
234 struct MemoryF64 {
235 data: [f64; 2],
236 }
237
238 #[simd_test(enable = "sse4a")]
239 // Miri cannot support this until it is clear how it fits in the Rust memory model
240 // (non-temporal store)
241 #[cfg_attr(miri, ignore)]
242 fn test_mm_stream_sd() {
243 let mut mem = MemoryF64 {
244 data: [1.0_f64, 2.0],
245 };
246 {
247 let vals = &mut mem.data;
248 let d = vals.as_mut_ptr();
249
250 let x = _mm_setr_pd(3.0, 4.0);
251
252 unsafe {
253 _mm_stream_sd(d, x);
254 }
255 _mm_sfence();
256 }
257 assert_eq!(mem.data[0], 3.0);
258 assert_eq!(mem.data[1], 2.0);
259 }
260
261 #[repr(align(16))]
262 struct MemoryF32 {
263 data: [f32; 4],
264 }
265
266 #[simd_test(enable = "sse4a")]
267 // Miri cannot support this until it is clear how it fits in the Rust memory model
268 // (non-temporal store)
269 #[cfg_attr(miri, ignore)]
270 fn test_mm_stream_ss() {
271 let mut mem = MemoryF32 {
272 data: [1.0_f32, 2.0, 3.0, 4.0],
273 };
274 {
275 let vals = &mut mem.data;
276 let d = vals.as_mut_ptr();
277
278 let x = _mm_setr_ps(5.0, 6.0, 7.0, 8.0);
279
280 unsafe {
281 _mm_stream_ss(d, x);
282 }
283 _mm_sfence();
284 }
285 assert_eq!(mem.data[0], 5.0);
286 assert_eq!(mem.data[1], 2.0);
287 assert_eq!(mem.data[2], 3.0);
288 assert_eq!(mem.data[3], 4.0);
289 }
290}