From b286a6bdf3ce99e9fd8f0986dd2a3e18ffec016f Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 29 Jan 2018 19:32:03 +0100 Subject: [PATCH 1/9] Define _mm_aes*_si128 intrinsics --- coresimd/src/x86/x86_64/aes.rs | 68 ++++++++++++++++++++++++++++++++++ coresimd/src/x86/x86_64/mod.rs | 3 ++ 2 files changed, 71 insertions(+) create mode 100644 coresimd/src/x86/x86_64/aes.rs diff --git a/coresimd/src/x86/x86_64/aes.rs b/coresimd/src/x86/x86_64/aes.rs new file mode 100644 index 0000000000..225f4f0209 --- /dev/null +++ b/coresimd/src/x86/x86_64/aes.rs @@ -0,0 +1,68 @@ +use x86::__m128i; + +#[allow(improper_ctypes)] +extern "C" { + #[link_name = "llvm.x86.aesni.aesdec"] + fn aesdec(a: __m128i, round_key: __m128i) -> __m128i; + #[link_name = "llvm.x86.aesni.aesdeclast"] + fn aesdeclast(a: __m128i, round_key: __m128i) -> __m128i; + #[link_name = "llvm.x86.aesni.aesenc"] + fn aesenc(a: __m128i, round_key: __m128i) -> __m128i; + #[link_name = "llvm.x86.aesni.aesenclast"] + fn aesenclast(a: __m128i, round_key: __m128i) -> __m128i; + #[link_name = "llvm.x86.aesni.aesimc"] + fn aesimc(a: __m128i) -> __m128i; + #[link_name = "llvm.x86.aesni.aeskeygenassist"] + fn aeskeygenassist(a: __m128i, imm8: u8) -> __m128i; +} + +/// Perform one round of an AES decryption flow on data (state) in `a`. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aesdec))] +pub unsafe fn _mm_aesdec_si128(a: __m128i, round_key: __m128i) -> __m128i { + aesdec(a, round_key) +} + +/// Perform the last round of an AES decryption flow on data (state) in `a`. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aesdeclast))] +pub unsafe fn _mm_aesdeclast_si128(a: __m128i, round_key: __m128i) -> __m128i { + aesdeclast(a, round_key) +} + +/// Perform one round of an AES encryption flow on data (state) in `a`. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aesenc))] +pub unsafe fn _mm_aesenc_si128(a: __m128i, round_key: __m128i) -> __m128i { + aesenc(a, round_key) +} + +/// Perform the last round of an AES encryption flow on data (state) in `a`. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aesenclast))] +pub unsafe fn _mm_aesenclast_si128(a: __m128i, round_key: __m128i) -> __m128i { + aesenclast(a, round_key) +} + +/// Perform the “InvMixColumns” transformation on `a`. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aesimc))] +pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { + aesimc(a) +} + +/// Assist in expanding the AES cipher key. +/// +/// Assist in expanding the AES cipher key by computing steps towards generating +/// a round key for encryption cipher using data from `a` and an 8-bit round constant. +#[inline] +#[target_feature(enable = "aes")] +#[cfg_attr(test, assert_instr(aeskeygenassist))] +pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: u8) -> __m128i { + aeskeygenassist(a, imm8) +} diff --git a/coresimd/src/x86/x86_64/mod.rs b/coresimd/src/x86/x86_64/mod.rs index 3b21197aa8..76131ecf41 100644 --- a/coresimd/src/x86/x86_64/mod.rs +++ b/coresimd/src/x86/x86_64/mod.rs @@ -23,6 +23,9 @@ pub use self::xsave::*; mod abm; pub use self::abm::*; +mod aes; +pub use self::aes::*; + mod avx; pub use self::avx::*; From 2d4b38e31916f3aeab815cc5feb9118ec01882fc Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Tue, 30 Jan 2018 17:22:54 +0100 Subject: [PATCH 2/9] Add tests for _mm_aes*_si128 intrinsics These tests are based on the examples in Microsoft's documentation. Same input should result in the same output in any case. --- coresimd/src/x86/x86_64/aes.rs | 73 ++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/coresimd/src/x86/x86_64/aes.rs b/coresimd/src/x86/x86_64/aes.rs index 225f4f0209..6c64b430e5 100644 --- a/coresimd/src/x86/x86_64/aes.rs +++ b/coresimd/src/x86/x86_64/aes.rs @@ -1,5 +1,8 @@ use x86::__m128i; +#[cfg(test)] +use stdsimd_test::assert_instr; + #[allow(improper_ctypes)] extern "C" { #[link_name = "llvm.x86.aesni.aesdec"] @@ -66,3 +69,73 @@ pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: u8) -> __m128i { aeskeygenassist(a, imm8) } + +#[cfg(test)] +mod tests { + // The constants in the tests below are just bit patterns. They should not + // be interpreted as integers; signedness does not make sense for them, but + // __m128i happens to be defined in terms of signed integers. + #![allow(overflowing_literals)] + + use stdsimd_test::simd_test; + + use x86::*; + + #[simd_test = "aes"] + unsafe fn test_mm_aesdec_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc664949.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); + let e = __m128i(0x044e4f5176fec48f, 0xb57ecfa381da39ee); + let r = _mm_aesdec_si128(a, k); + assert_eq_m128i(r, e); + } + + #[simd_test = "aes"] + unsafe fn test_mm_aesdeclast_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc714178.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); + let e = __m128i(0x36cad57d9072bf9e, 0xf210dd981fa4a493); + let r = _mm_aesdeclast_si128(a, k); + assert_eq_m128i(r, e); + } + + #[simd_test = "aes"] + unsafe fn test_mm_aesenc_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc664810.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); + let e = __m128i(0x16ab0e57dfc442ed, 0x28e4ee1884504333); + let r = _mm_aesenc_si128(a, k); + assert_eq_m128i(r, e); + } + + #[simd_test = "aes"] + unsafe fn test_mm_aesenclast_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc714136.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); + let e = __m128i(0xb6dd7df25d7ab320, 0x4b04f98cf4c860f8); + let r = _mm_aesenclast_si128(a, k); + assert_eq_m128i(r, e); + } + + #[simd_test = "aes"] + unsafe fn test_mm_aesimc_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc714195.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let e = __m128i(0xc66c82284ee40aa0, 0x6633441122770055); + let r = _mm_aesimc_si128(a); + assert_eq_m128i(r, e); + } + + #[simd_test = "aes"] + unsafe fn test_mm_aeskeygenassist_si128() { + // Constants taken from https://msdn.microsoft.com/en-us/library/cc714138.aspx. + let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); + let e = __m128i(0x857c266b7c266e85, 0xeac4eea9c4eeacea); + let r = _mm_aeskeygenassist_si128(a, 5); + assert_eq_m128i(r, e); + } +} From 4a14c05b21b210efe8984ccf99d1bea402444614 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Wed, 31 Jan 2018 19:56:35 +0100 Subject: [PATCH 3/9] Constify imm8 argument of aeskeygenassist --- coresimd/src/x86/x86_64/aes.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/coresimd/src/x86/x86_64/aes.rs b/coresimd/src/x86/x86_64/aes.rs index 6c64b430e5..80bc3a65c7 100644 --- a/coresimd/src/x86/x86_64/aes.rs +++ b/coresimd/src/x86/x86_64/aes.rs @@ -67,7 +67,10 @@ pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { #[target_feature(enable = "aes")] #[cfg_attr(test, assert_instr(aeskeygenassist))] pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: u8) -> __m128i { - aeskeygenassist(a, imm8) + macro_rules! call { + ($imm8:expr) => (aeskeygenassist(a, $imm8)) + } + constify_imm8!(imm8, call) } #[cfg(test)] From 36746a694ce6996cd5779cd8d9b538e5605a6efb Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Thu, 1 Feb 2018 09:51:02 +0100 Subject: [PATCH 4/9] Do not rely on internal layout of __m128 Use _mm_set_epi64x instead to construct constants. --- coresimd/src/x86/x86_64/aes.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/coresimd/src/x86/x86_64/aes.rs b/coresimd/src/x86/x86_64/aes.rs index 80bc3a65c7..73f9781956 100644 --- a/coresimd/src/x86/x86_64/aes.rs +++ b/coresimd/src/x86/x86_64/aes.rs @@ -87,9 +87,9 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aesdec_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc664949.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); - let e = __m128i(0x044e4f5176fec48f, 0xb57ecfa381da39ee); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee); + let e = _mm_set_epi64x(0x044e4f5176fec48f, 0xb57ecfa381da39ee); let r = _mm_aesdec_si128(a, k); assert_eq_m128i(r, e); } @@ -97,9 +97,9 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aesdeclast_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc714178.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); - let e = __m128i(0x36cad57d9072bf9e, 0xf210dd981fa4a493); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee); + let e = _mm_set_epi64x(0x36cad57d9072bf9e, 0xf210dd981fa4a493); let r = _mm_aesdeclast_si128(a, k); assert_eq_m128i(r, e); } @@ -107,9 +107,9 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aesenc_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc664810.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); - let e = __m128i(0x16ab0e57dfc442ed, 0x28e4ee1884504333); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee); + let e = _mm_set_epi64x(0x16ab0e57dfc442ed, 0x28e4ee1884504333); let r = _mm_aesenc_si128(a, k); assert_eq_m128i(r, e); } @@ -117,9 +117,9 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aesenclast_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc714136.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let k = __m128i(0x1133557799bbddff, 0x0022446688aaccee); - let e = __m128i(0xb6dd7df25d7ab320, 0x4b04f98cf4c860f8); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee); + let e = _mm_set_epi64x(0xb6dd7df25d7ab320, 0x4b04f98cf4c860f8); let r = _mm_aesenclast_si128(a, k); assert_eq_m128i(r, e); } @@ -127,8 +127,8 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aesimc_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc714195.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let e = __m128i(0xc66c82284ee40aa0, 0x6633441122770055); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let e = _mm_set_epi64x(0xc66c82284ee40aa0, 0x6633441122770055); let r = _mm_aesimc_si128(a); assert_eq_m128i(r, e); } @@ -136,8 +136,8 @@ mod tests { #[simd_test = "aes"] unsafe fn test_mm_aeskeygenassist_si128() { // Constants taken from https://msdn.microsoft.com/en-us/library/cc714138.aspx. - let a = __m128i(0x0123456789abcdef, 0x8899aabbccddeeff); - let e = __m128i(0x857c266b7c266e85, 0xeac4eea9c4eeacea); + let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff); + let e = _mm_set_epi64x(0x857c266b7c266e85, 0xeac4eea9c4eeacea); let r = _mm_aeskeygenassist_si128(a, 5); assert_eq_m128i(r, e); } From 3bcb7010ab25a8841b2bd7f48903e456132df9d1 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Thu, 1 Feb 2018 09:57:12 +0100 Subject: [PATCH 5/9] Move AES vendor intrinsics from x86_64 to i686 Although i686 does not have the AES New Instructions, making code compatible across x86 and x64_64 tends to be easier if the intrinsics are available everywhere. --- coresimd/src/x86/{x86_64 => i686}/aes.rs | 0 coresimd/src/x86/i686/mod.rs | 3 +++ coresimd/src/x86/x86_64/mod.rs | 3 --- 3 files changed, 3 insertions(+), 3 deletions(-) rename coresimd/src/x86/{x86_64 => i686}/aes.rs (100%) diff --git a/coresimd/src/x86/x86_64/aes.rs b/coresimd/src/x86/i686/aes.rs similarity index 100% rename from coresimd/src/x86/x86_64/aes.rs rename to coresimd/src/x86/i686/aes.rs diff --git a/coresimd/src/x86/i686/mod.rs b/coresimd/src/x86/i686/mod.rs index 1c4430d078..ad40c73c6d 100644 --- a/coresimd/src/x86/i686/mod.rs +++ b/coresimd/src/x86/i686/mod.rs @@ -1,5 +1,8 @@ //! `i686` intrinsics +mod aes; +pub use self::aes::*; + mod mmx; pub use self::mmx::*; diff --git a/coresimd/src/x86/x86_64/mod.rs b/coresimd/src/x86/x86_64/mod.rs index 76131ecf41..3b21197aa8 100644 --- a/coresimd/src/x86/x86_64/mod.rs +++ b/coresimd/src/x86/x86_64/mod.rs @@ -23,9 +23,6 @@ pub use self::xsave::*; mod abm; pub use self::abm::*; -mod aes; -pub use self::aes::*; - mod avx; pub use self::avx::*; From 8665db24a378b750381b9db100e5db2753f60220 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 5 Feb 2018 10:54:03 +0100 Subject: [PATCH 6/9] Pass constant for assert_instr(aeskeygenassist) Pass a particular value for the disassembly test, so we end up with one instruction, instead of the match arm that matches on all 256 values. --- coresimd/src/x86/i686/aes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coresimd/src/x86/i686/aes.rs b/coresimd/src/x86/i686/aes.rs index 73f9781956..e52dd8e672 100644 --- a/coresimd/src/x86/i686/aes.rs +++ b/coresimd/src/x86/i686/aes.rs @@ -65,7 +65,7 @@ pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { /// a round key for encryption cipher using data from `a` and an 8-bit round constant. #[inline] #[target_feature(enable = "aes")] -#[cfg_attr(test, assert_instr(aeskeygenassist))] +#[cfg_attr(test, assert_instr(aeskeygenassist, imm8 = 0))] pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: u8) -> __m128i { macro_rules! call { ($imm8:expr) => (aeskeygenassist(a, $imm8)) From 53332f652d9874066ac8cd9064e95b193b81ad35 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 5 Feb 2018 16:51:16 +0100 Subject: [PATCH 7/9] Make aeskeygenassist imm8 argument i32, not u8 Intel documentation specifies it as an "8-bit round constant", but then goes on to give it a type "const int", which translates to i32 in Rust. The test that verifies the Rust signatures against Intel documentation failed on this. For now we will replicate the C API verbatim. Even when Rust could have a more accurate type signature that makes passing values more than 8 bits impossible, rather than silently mapping out-of-range values to 255. --- coresimd/src/x86/i686/aes.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coresimd/src/x86/i686/aes.rs b/coresimd/src/x86/i686/aes.rs index e52dd8e672..a31f6ac24c 100644 --- a/coresimd/src/x86/i686/aes.rs +++ b/coresimd/src/x86/i686/aes.rs @@ -62,11 +62,12 @@ pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { /// Assist in expanding the AES cipher key. /// /// Assist in expanding the AES cipher key by computing steps towards generating -/// a round key for encryption cipher using data from `a` and an 8-bit round constant. +/// a round key for encryption cipher using data from `a` and an 8-bit round +/// constant `imm8`. #[inline] #[target_feature(enable = "aes")] #[cfg_attr(test, assert_instr(aeskeygenassist, imm8 = 0))] -pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: u8) -> __m128i { +pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: i32) -> __m128i { macro_rules! call { ($imm8:expr) => (aeskeygenassist(a, $imm8)) } From e21c51198ec7b7c42c99db4cb2dfdc15ffd15e9c Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 5 Feb 2018 17:38:38 +0100 Subject: [PATCH 8/9] Reflow doc comment as proposed by rustfmt --- coresimd/src/x86/i686/aes.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coresimd/src/x86/i686/aes.rs b/coresimd/src/x86/i686/aes.rs index a31f6ac24c..f69031aea1 100644 --- a/coresimd/src/x86/i686/aes.rs +++ b/coresimd/src/x86/i686/aes.rs @@ -61,9 +61,9 @@ pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i { /// Assist in expanding the AES cipher key. /// -/// Assist in expanding the AES cipher key by computing steps towards generating -/// a round key for encryption cipher using data from `a` and an 8-bit round -/// constant `imm8`. +/// Assist in expanding the AES cipher key by computing steps towards +/// generating a round key for encryption cipher using data from `a` and an +/// 8-bit round constant `imm8`. #[inline] #[target_feature(enable = "aes")] #[cfg_attr(test, assert_instr(aeskeygenassist, imm8 = 0))] From 9425fe722e4bc53220f81b412598dcfede7ff755 Mon Sep 17 00:00:00 2001 From: Ruud van Asseldonk Date: Mon, 5 Feb 2018 17:49:02 +0100 Subject: [PATCH 9/9] Add module doc comment for i686::aes --- coresimd/src/x86/i686/aes.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/coresimd/src/x86/i686/aes.rs b/coresimd/src/x86/i686/aes.rs index f69031aea1..ed2251aa81 100644 --- a/coresimd/src/x86/i686/aes.rs +++ b/coresimd/src/x86/i686/aes.rs @@ -1,3 +1,11 @@ +//! AES New Instructions (AES-NI) +//! +//! The intrinsics here correspond to those in the `wmmintrin.h` C header. +//! +//! The reference is [Intel 64 and IA-32 Architectures Software Developer's +//! Manual Volume 2: Instruction Set Reference, A-Z][intel64_ref]. +//! +//! [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf use x86::__m128i; #[cfg(test)]