Skip to content

Commit 0d6b868

Browse files
ruudaalexcrichton
authored andcommitted
Add x86 AES-NI vendor intrinsics (#311)
* Define _mm_aes*_si128 intrinsics * 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. * Constify imm8 argument of aeskeygenassist * Do not rely on internal layout of __m128 Use _mm_set_epi64x instead to construct constants. * 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. * 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. * 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. * Reflow doc comment as proposed by rustfmt * Add module doc comment for i686::aes
1 parent 13c14a5 commit 0d6b868

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

coresimd/src/x86/i686/aes.rs

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//! AES New Instructions (AES-NI)
2+
//!
3+
//! The intrinsics here correspond to those in the `wmmintrin.h` C header.
4+
//!
5+
//! The reference is [Intel 64 and IA-32 Architectures Software Developer's
6+
//! Manual Volume 2: Instruction Set Reference, A-Z][intel64_ref].
7+
//!
8+
//! [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
9+
use x86::__m128i;
10+
11+
#[cfg(test)]
12+
use stdsimd_test::assert_instr;
13+
14+
#[allow(improper_ctypes)]
15+
extern "C" {
16+
#[link_name = "llvm.x86.aesni.aesdec"]
17+
fn aesdec(a: __m128i, round_key: __m128i) -> __m128i;
18+
#[link_name = "llvm.x86.aesni.aesdeclast"]
19+
fn aesdeclast(a: __m128i, round_key: __m128i) -> __m128i;
20+
#[link_name = "llvm.x86.aesni.aesenc"]
21+
fn aesenc(a: __m128i, round_key: __m128i) -> __m128i;
22+
#[link_name = "llvm.x86.aesni.aesenclast"]
23+
fn aesenclast(a: __m128i, round_key: __m128i) -> __m128i;
24+
#[link_name = "llvm.x86.aesni.aesimc"]
25+
fn aesimc(a: __m128i) -> __m128i;
26+
#[link_name = "llvm.x86.aesni.aeskeygenassist"]
27+
fn aeskeygenassist(a: __m128i, imm8: u8) -> __m128i;
28+
}
29+
30+
/// Perform one round of an AES decryption flow on data (state) in `a`.
31+
#[inline]
32+
#[target_feature(enable = "aes")]
33+
#[cfg_attr(test, assert_instr(aesdec))]
34+
pub unsafe fn _mm_aesdec_si128(a: __m128i, round_key: __m128i) -> __m128i {
35+
aesdec(a, round_key)
36+
}
37+
38+
/// Perform the last round of an AES decryption flow on data (state) in `a`.
39+
#[inline]
40+
#[target_feature(enable = "aes")]
41+
#[cfg_attr(test, assert_instr(aesdeclast))]
42+
pub unsafe fn _mm_aesdeclast_si128(a: __m128i, round_key: __m128i) -> __m128i {
43+
aesdeclast(a, round_key)
44+
}
45+
46+
/// Perform one round of an AES encryption flow on data (state) in `a`.
47+
#[inline]
48+
#[target_feature(enable = "aes")]
49+
#[cfg_attr(test, assert_instr(aesenc))]
50+
pub unsafe fn _mm_aesenc_si128(a: __m128i, round_key: __m128i) -> __m128i {
51+
aesenc(a, round_key)
52+
}
53+
54+
/// Perform the last round of an AES encryption flow on data (state) in `a`.
55+
#[inline]
56+
#[target_feature(enable = "aes")]
57+
#[cfg_attr(test, assert_instr(aesenclast))]
58+
pub unsafe fn _mm_aesenclast_si128(a: __m128i, round_key: __m128i) -> __m128i {
59+
aesenclast(a, round_key)
60+
}
61+
62+
/// Perform the “InvMixColumns” transformation on `a`.
63+
#[inline]
64+
#[target_feature(enable = "aes")]
65+
#[cfg_attr(test, assert_instr(aesimc))]
66+
pub unsafe fn _mm_aesimc_si128(a: __m128i) -> __m128i {
67+
aesimc(a)
68+
}
69+
70+
/// Assist in expanding the AES cipher key.
71+
///
72+
/// Assist in expanding the AES cipher key by computing steps towards
73+
/// generating a round key for encryption cipher using data from `a` and an
74+
/// 8-bit round constant `imm8`.
75+
#[inline]
76+
#[target_feature(enable = "aes")]
77+
#[cfg_attr(test, assert_instr(aeskeygenassist, imm8 = 0))]
78+
pub unsafe fn _mm_aeskeygenassist_si128(a: __m128i, imm8: i32) -> __m128i {
79+
macro_rules! call {
80+
($imm8:expr) => (aeskeygenassist(a, $imm8))
81+
}
82+
constify_imm8!(imm8, call)
83+
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
// The constants in the tests below are just bit patterns. They should not
88+
// be interpreted as integers; signedness does not make sense for them, but
89+
// __m128i happens to be defined in terms of signed integers.
90+
#![allow(overflowing_literals)]
91+
92+
use stdsimd_test::simd_test;
93+
94+
use x86::*;
95+
96+
#[simd_test = "aes"]
97+
unsafe fn test_mm_aesdec_si128() {
98+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc664949.aspx.
99+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
100+
let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee);
101+
let e = _mm_set_epi64x(0x044e4f5176fec48f, 0xb57ecfa381da39ee);
102+
let r = _mm_aesdec_si128(a, k);
103+
assert_eq_m128i(r, e);
104+
}
105+
106+
#[simd_test = "aes"]
107+
unsafe fn test_mm_aesdeclast_si128() {
108+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc714178.aspx.
109+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
110+
let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee);
111+
let e = _mm_set_epi64x(0x36cad57d9072bf9e, 0xf210dd981fa4a493);
112+
let r = _mm_aesdeclast_si128(a, k);
113+
assert_eq_m128i(r, e);
114+
}
115+
116+
#[simd_test = "aes"]
117+
unsafe fn test_mm_aesenc_si128() {
118+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc664810.aspx.
119+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
120+
let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee);
121+
let e = _mm_set_epi64x(0x16ab0e57dfc442ed, 0x28e4ee1884504333);
122+
let r = _mm_aesenc_si128(a, k);
123+
assert_eq_m128i(r, e);
124+
}
125+
126+
#[simd_test = "aes"]
127+
unsafe fn test_mm_aesenclast_si128() {
128+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc714136.aspx.
129+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
130+
let k = _mm_set_epi64x(0x1133557799bbddff, 0x0022446688aaccee);
131+
let e = _mm_set_epi64x(0xb6dd7df25d7ab320, 0x4b04f98cf4c860f8);
132+
let r = _mm_aesenclast_si128(a, k);
133+
assert_eq_m128i(r, e);
134+
}
135+
136+
#[simd_test = "aes"]
137+
unsafe fn test_mm_aesimc_si128() {
138+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc714195.aspx.
139+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
140+
let e = _mm_set_epi64x(0xc66c82284ee40aa0, 0x6633441122770055);
141+
let r = _mm_aesimc_si128(a);
142+
assert_eq_m128i(r, e);
143+
}
144+
145+
#[simd_test = "aes"]
146+
unsafe fn test_mm_aeskeygenassist_si128() {
147+
// Constants taken from https://msdn.microsoft.com/en-us/library/cc714138.aspx.
148+
let a = _mm_set_epi64x(0x0123456789abcdef, 0x8899aabbccddeeff);
149+
let e = _mm_set_epi64x(0x857c266b7c266e85, 0xeac4eea9c4eeacea);
150+
let r = _mm_aeskeygenassist_si128(a, 5);
151+
assert_eq_m128i(r, e);
152+
}
153+
}

coresimd/src/x86/i686/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! `i686` intrinsics
22
3+
mod aes;
4+
pub use self::aes::*;
5+
36
mod mmx;
47
pub use self::mmx::*;
58

0 commit comments

Comments
 (0)