1//! `i586`'s `xsave` and `xsaveopt` target feature intrinsics
2#![allow(clippy::module_name_repetitions)]
3
4#[cfg(test)]
5use stdarch_test::assert_instr;
6
7#[allow(improper_ctypes)]
8unsafe extern "C" {
9 #[link_name = "llvm.x86.xsave"]
10 unsafefn xsave(p: *mut u8, hi: u32, lo: u32);
11 #[link_name = "llvm.x86.xrstor"]
12 unsafefn xrstor(p: *const u8, hi: u32, lo: u32);
13 #[link_name = "llvm.x86.xsetbv"]
14 unsafefn xsetbv(v: u32, hi: u32, lo: u32);
15 #[link_name = "llvm.x86.xgetbv"]
16 unsafefn xgetbv(v: u32) -> i64;
17 #[link_name = "llvm.x86.xsaveopt"]
18 unsafefn xsaveopt(p: *mut u8, hi: u32, lo: u32);
19 #[link_name = "llvm.x86.xsavec"]
20 unsafefn xsavec(p: *mut u8, hi: u32, lo: u32);
21 #[link_name = "llvm.x86.xsaves"]
22 unsafefn xsaves(p: *mut u8, hi: u32, lo: u32);
23 #[link_name = "llvm.x86.xrstors"]
24 unsafefn xrstors(p: *const u8, hi: u32, lo: u32);
25}
26
27/// Performs a full or partial save of the enabled processor states to memory at
28/// `mem_addr`.
29///
30/// State is saved based on bits `[62:0]` in `save_mask` and XCR0.
31/// `mem_addr` must be aligned on a 64-byte boundary.
32///
33/// The format of the XSAVE area is detailed in Section 13.4, “XSAVE Area,” of
34/// Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1.
35///
36/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xsave)
37#[inline]
38#[target_feature(enable = "xsave")]
39#[cfg_attr(test, assert_instr(xsave))]
40#[stable(feature = "simd_x86", since = "1.27.0")]
41pub unsafe fn _xsave(mem_addr: *mut u8, save_mask: u64) {
42 xsave(p:mem_addr, (save_mask >> 32) as u32, lo:save_mask as u32);
43}
44
45/// Performs a full or partial restore of the enabled processor states using
46/// the state information stored in memory at `mem_addr`.
47///
48/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
49/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
50/// boundary.
51///
52/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xrstor)
53#[inline]
54#[target_feature(enable = "xsave")]
55#[cfg_attr(test, assert_instr(xrstor))]
56#[stable(feature = "simd_x86", since = "1.27.0")]
57pub unsafe fn _xrstor(mem_addr: *const u8, rs_mask: u64) {
58 xrstor(p:mem_addr, (rs_mask >> 32) as u32, lo:rs_mask as u32);
59}
60
61/// `XFEATURE_ENABLED_MASK` for `XCR`
62///
63/// This intrinsic maps to `XSETBV` instruction.
64#[stable(feature = "simd_x86", since = "1.27.0")]
65pub const _XCR_XFEATURE_ENABLED_MASK: u32 = 0;
66
67/// Copies 64-bits from `val` to the extended control register (`XCR`) specified
68/// by `a`.
69///
70/// Currently only `XFEATURE_ENABLED_MASK` `XCR` is supported.
71///
72/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xsetbv)
73#[inline]
74#[target_feature(enable = "xsave")]
75#[cfg_attr(test, assert_instr(xsetbv))]
76#[stable(feature = "simd_x86", since = "1.27.0")]
77pub unsafe fn _xsetbv(a: u32, val: u64) {
78 xsetbv(v:a, (val >> 32) as u32, lo:val as u32);
79}
80
81/// Reads the contents of the extended control register `XCR`
82/// specified in `xcr_no`.
83///
84/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xgetbv)
85#[inline]
86#[target_feature(enable = "xsave")]
87#[cfg_attr(test, assert_instr(xgetbv))]
88#[stable(feature = "simd_x86", since = "1.27.0")]
89pub unsafe fn _xgetbv(xcr_no: u32) -> u64 {
90 xgetbv(xcr_no) as u64
91}
92
93/// Performs a full or partial save of the enabled processor states to memory at
94/// `mem_addr`.
95///
96/// State is saved based on bits `[62:0]` in `save_mask` and `XCR0`.
97/// `mem_addr` must be aligned on a 64-byte boundary. The hardware may optimize
98/// the manner in which data is saved. The performance of this instruction will
99/// be equal to or better than using the `XSAVE` instruction.
100///
101/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xsaveopt)
102#[inline]
103#[target_feature(enable = "xsave,xsaveopt")]
104#[cfg_attr(test, assert_instr(xsaveopt))]
105#[stable(feature = "simd_x86", since = "1.27.0")]
106pub unsafe fn _xsaveopt(mem_addr: *mut u8, save_mask: u64) {
107 xsaveopt(p:mem_addr, (save_mask >> 32) as u32, lo:save_mask as u32);
108}
109
110/// Performs a full or partial save of the enabled processor states to memory
111/// at `mem_addr`.
112///
113/// `xsavec` differs from `xsave` in that it uses compaction and that it may
114/// use init optimization. State is saved based on bits `[62:0]` in `save_mask`
115/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
116///
117/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xsavec)
118#[inline]
119#[target_feature(enable = "xsave,xsavec")]
120#[cfg_attr(test, assert_instr(xsavec))]
121#[stable(feature = "simd_x86", since = "1.27.0")]
122pub unsafe fn _xsavec(mem_addr: *mut u8, save_mask: u64) {
123 xsavec(p:mem_addr, (save_mask >> 32) as u32, lo:save_mask as u32);
124}
125
126/// Performs a full or partial save of the enabled processor states to memory at
127/// `mem_addr`
128///
129/// `xsaves` differs from xsave in that it can save state components
130/// corresponding to bits set in `IA32_XSS` `MSR` and that it may use the
131/// modified optimization. State is saved based on bits `[62:0]` in `save_mask`
132/// and `XCR0`. `mem_addr` must be aligned on a 64-byte boundary.
133///
134/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xsaves)
135#[inline]
136#[target_feature(enable = "xsave,xsaves")]
137#[cfg_attr(test, assert_instr(xsaves))]
138#[stable(feature = "simd_x86", since = "1.27.0")]
139pub unsafe fn _xsaves(mem_addr: *mut u8, save_mask: u64) {
140 xsaves(p:mem_addr, (save_mask >> 32) as u32, lo:save_mask as u32);
141}
142
143/// Performs a full or partial restore of the enabled processor states using the
144/// state information stored in memory at `mem_addr`.
145///
146/// `xrstors` differs from `xrstor` in that it can restore state components
147/// corresponding to bits set in the `IA32_XSS` `MSR`; `xrstors` cannot restore
148/// from an `xsave` area in which the extended region is in the standard form.
149/// State is restored based on bits `[62:0]` in `rs_mask`, `XCR0`, and
150/// `mem_addr.HEADER.XSTATE_BV`. `mem_addr` must be aligned on a 64-byte
151/// boundary.
152///
153/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_xrstors)
154#[inline]
155#[target_feature(enable = "xsave,xsaves")]
156#[cfg_attr(test, assert_instr(xrstors))]
157#[stable(feature = "simd_x86", since = "1.27.0")]
158pub unsafe fn _xrstors(mem_addr: *const u8, rs_mask: u64) {
159 xrstors(p:mem_addr, (rs_mask >> 32) as u32, lo:rs_mask as u32);
160}
161
162#[cfg(test)]
163pub(crate) use tests::XsaveArea;
164
165#[cfg(test)]
166mod tests {
167 use std::boxed::Box;
168
169 use crate::core_arch::x86::*;
170 use stdarch_test::simd_test;
171
172 #[derive(Debug)]
173 pub(crate) struct XsaveArea {
174 data: Box<[AlignedArray]>,
175 }
176
177 #[repr(align(64))]
178 #[derive(Copy, Clone, Debug)]
179 struct AlignedArray([u8; 64]);
180
181 impl XsaveArea {
182 #[target_feature(enable = "xsave")]
183 pub(crate) fn new() -> XsaveArea {
184 // `CPUID.(EAX=0DH,ECX=0):ECX` contains the size required to hold all supported xsave
185 // components. `EBX` contains the size required to hold all xsave components currently
186 // enabled in `XCR0`. We are using `ECX` to ensure enough space in all scenarios
187 let CpuidResult { ecx, .. } = __cpuid(0x0d);
188
189 XsaveArea {
190 data: vec![AlignedArray([0; 64]); ecx.div_ceil(64) as usize].into_boxed_slice(),
191 }
192 }
193 pub(crate) fn ptr(&mut self) -> *mut u8 {
194 self.data.as_mut_ptr().cast()
195 }
196 }
197
198 #[simd_test(enable = "xsave")]
199 #[cfg_attr(miri, ignore)] // Register saving/restoring is not supported in Miri
200 fn test_xsave() {
201 let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
202 let mut a = XsaveArea::new();
203 let mut b = XsaveArea::new();
204
205 unsafe {
206 _xsave(a.ptr(), m);
207 _xrstor(a.ptr(), m);
208 _xsave(b.ptr(), m);
209 }
210 }
211
212 #[simd_test(enable = "xsave")]
213 #[cfg_attr(miri, ignore)] // Register saving/restoring is not supported in Miri
214 fn test_xgetbv() {
215 let xcr_n: u32 = _XCR_XFEATURE_ENABLED_MASK;
216
217 let xcr: u64 = unsafe { _xgetbv(xcr_n) };
218 let xcr_cpy: u64 = unsafe { _xgetbv(xcr_n) };
219 assert_eq!(xcr, xcr_cpy);
220 }
221
222 #[simd_test(enable = "xsave,xsaveopt")]
223 #[cfg_attr(miri, ignore)] // Register saving/restoring is not supported in Miri
224 fn test_xsaveopt() {
225 let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
226 let mut a = XsaveArea::new();
227 let mut b = XsaveArea::new();
228
229 unsafe {
230 _xsaveopt(a.ptr(), m);
231 _xrstor(a.ptr(), m);
232 _xsaveopt(b.ptr(), m);
233 }
234 }
235
236 #[simd_test(enable = "xsave,xsavec")]
237 #[cfg_attr(miri, ignore)] // Register saving/restoring is not supported in Miri
238 fn test_xsavec() {
239 let m = 0xFFFFFFFFFFFFFFFF_u64; //< all registers
240 let mut a = XsaveArea::new();
241 let mut b = XsaveArea::new();
242
243 unsafe {
244 _xsavec(a.ptr(), m);
245 _xrstor(a.ptr(), m);
246 _xsavec(b.ptr(), m);
247 }
248 }
249}
250