| 1 | //! `i586`'s `xsave` and `xsaveopt` target feature intrinsics |
| 2 | #![allow (clippy::module_name_repetitions)] |
| 3 | |
| 4 | #[cfg (test)] |
| 5 | use stdarch_test::assert_instr; |
| 6 | |
| 7 | #[allow (improper_ctypes)] |
| 8 | unsafe 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" )] |
| 41 | pub 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" )] |
| 57 | pub 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" )] |
| 65 | pub 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" )] |
| 77 | pub 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" )] |
| 89 | pub 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" )] |
| 106 | pub 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" )] |
| 122 | pub 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" )] |
| 139 | pub 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" )] |
| 158 | pub 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)] |
| 163 | pub(crate) use tests::XsaveArea; |
| 164 | |
| 165 | #[cfg (test)] |
| 166 | mod 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 | |