Browse the docs

silk.simd.x86.

x86-64 SIMD instructions that the portable silk.simd.simd operations do not express: byte table lookups, multiply-add steps, saturating packs, sums of absolute differences, AES rounds and carry-less multiplication.

When to use#

Use this module in kernels such as UTF-8 validation, base64 and hex decoding, where one of these instructions replaces several portable steps. Use silk.simd.simd for everything else.

Details#

Each function names the instruction it stands for and the CPU feature that instruction needs. When the build targets x86-64 with that feature, the function compiles to the one instruction. Otherwise it computes the same lanes with portable code, which is correct on every target but slower, so a kernel that depends on speed checks Target.hasCpuFeature with static if before choosing this path. x86-64-v2 enables SSSE3 and SSE4.1, and x86-64-v3 adds AVX2. The AES rounds need aes and carry-less multiplication needs pclmul; no microarchitecture level includes them.

Examples#

Look up sixteen nibbles at once#

silk
import silk.simd.x86
import silk.simd.u8x16

pub fn main() -> i32 {
  let digits = u8x16.fromArray([48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102])
  let nibbles = u8x16.fromArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
  let hex = x86.shuffleBytes(digits, nibbles)
  if u8x16.extract(hex, 10) != 97 {
    return 1
  }
  return 0
}

Import as x86 with import silk.simd.x86 as x86.

Public declarations: 22.

shuffleBytes#

silk
pub fn shuffleBytes(table: simd<u8, 16>, indices: simd<u8, 16>) -> simd<u8, 16>

pshufb (SSSE3): lane i is table[indices[i] & 15], or 0 when bit 7 of indices[i] is set.

When the build targets x86-64 with the ssse3 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

shuffleBytes256#

silk
pub fn shuffleBytes256(table: simd<u8, 32>, indices: simd<u8, 32>) -> simd<u8, 32>

vpshufb (AVX2): in each 128-bit half, lane i is table[indices[i] & 15] from the same half, or 0 when bit 7 of indices[i] is set. An index selects only within its own half.

When the build targets x86-64 with the avx2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

multiplyAddBytes#

silk
pub fn multiplyAddBytes(left: simd<u8, 16>, right: simd<i8, 16>) -> simd<i16, 8>

pmaddubsw (SSSE3): each unsigned byte of left times the signed byte of right, with each adjacent pair of products summed into one i16 lane with signed saturation.

When the build targets x86-64 with the ssse3 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

multiplyAddBytes256#

silk
pub fn multiplyAddBytes256(left: simd<u8, 32>, right: simd<i8, 32>) -> simd<i16, 16>

vpmaddubsw (AVX2): each unsigned byte of left times the signed byte of right, over 32 bytes, with each adjacent pair of products summed into one i16 lane with signed saturation.

When the build targets x86-64 with the avx2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

multiplyAddWords#

silk
pub fn multiplyAddWords(left: simd<i16, 8>, right: simd<i16, 8>) -> simd<i32, 4>

pmaddwd (SSE2): each i16 lane of left times that of right, with each adjacent pair of products summed into one i32 lane. The sum wraps only for four lanes of -32768.

When the build targets x86-64 with the sse2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

multiplyAddWords256#

silk
pub fn multiplyAddWords256(left: simd<i16, 16>, right: simd<i16, 16>) -> simd<i32, 8>

vpmaddwd (AVX2): each of the sixteen i16 lanes of left times that of right, with each adjacent pair of products summed into one i32 lane. The sum wraps only for four lanes of -32768.

When the build targets x86-64 with the avx2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

packUnsigned16#

silk
pub fn packUnsigned16(left: simd<i16, 8>, right: simd<i16, 8>) -> simd<u8, 16>

packuswb (SSE2): the lanes of left, then of right, each clamped to 0..=255.

When the build targets x86-64 with the sse2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

packUnsigned16x256#

silk
pub fn packUnsigned16x256(left: simd<i16, 16>, right: simd<i16, 16>) -> simd<u8, 32>

vpackuswb (AVX2): each lane clamped to 0..=255, packed on each 128-bit half, so the result is the low half of left, the low half of right, the high half of left, then the high half of right.

When the build targets x86-64 with the avx2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

packUnsigned32#

silk
pub fn packUnsigned32(left: simd<i32, 4>, right: simd<i32, 4>) -> simd<u16, 8>

packusdw (SSE4.1): the lanes of left, then of right, each clamped to 0..=65535.

When the build targets x86-64 with the sse4.1 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

packSigned16#

silk
pub fn packSigned16(left: simd<i16, 8>, right: simd<i16, 8>) -> simd<i8, 16>

packsswb (SSE2): the lanes of left, then of right, each clamped to the i8 range.

When the build targets x86-64 with the sse2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

packSigned32#

silk
pub fn packSigned32(left: simd<i32, 4>, right: simd<i32, 4>) -> simd<i16, 8>

packssdw (SSE2): the lanes of left, then of right, each clamped to the i16 range.

When the build targets x86-64 with the sse2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

sumAbsoluteDifferences#

silk
pub fn sumAbsoluteDifferences(left: simd<u8, 16>, right: simd<u8, 16>) -> simd<u64, 2>

psadbw (SSE2): for each group of eight bytes, the sum of the absolute differences of the lanes of left and right.

When the build targets x86-64 with the sse2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

sumAbsoluteDifferences256#

silk
pub fn sumAbsoluteDifferences256(left: simd<u8, 32>, right: simd<u8, 32>) -> simd<u64, 4>

vpsadbw (AVX2): for each group of eight bytes of 32, the sum of the absolute differences of the lanes of left and right.

When the build targets x86-64 with the avx2 feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

aesEncrypt#

silk
pub fn aesEncrypt(state: simd<u8, 16>, key: simd<u8, 16>) -> simd<u8, 16>

aesenc (AES): one AES encryption round of state with the round key key: ShiftRows, SubBytes and MixColumns, then key added.

When the build targets x86-64 with the aes feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

aesEncryptLast#

silk
pub fn aesEncryptLast(state: simd<u8, 16>, key: simd<u8, 16>) -> simd<u8, 16>

aesenclast (AES): the last AES encryption round of state with the round key key: ShiftRows and SubBytes, then key added. It skips MixColumns.

When the build targets x86-64 with the aes feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

aesDecrypt#

silk
pub fn aesDecrypt(state: simd<u8, 16>, key: simd<u8, 16>) -> simd<u8, 16>

aesdec (AES): one round of the AES equivalent inverse cipher: InvShiftRows, InvSubBytes and InvMixColumns, then key added. Its round keys are the encryption keys passed through aesInverseMixColumns.

When the build targets x86-64 with the aes feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

aesDecryptLast#

silk
pub fn aesDecryptLast(state: simd<u8, 16>, key: simd<u8, 16>) -> simd<u8, 16>

aesdeclast (AES): the last AES decryption round of state with the round key key: InvShiftRows and InvSubBytes, then key added. It skips InvMixColumns.

When the build targets x86-64 with the aes feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

aesInverseMixColumns#

silk
pub fn aesInverseMixColumns(key: simd<u8, 16>) -> simd<u8, 16>

aesimc (AES): InvMixColumns of one round key, for the decryption key schedule.

When the build targets x86-64 with the aes feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

carrylessMultiply#

silk
pub fn carrylessMultiply(left: simd<u64, 2>, right: simd<u64, 2>) -> simd<u64, 2>

pclmulqdq with selector 0 (PCLMUL): the 128-bit carry-less product of lane 0 of left and lane 0 of right, with its low 64 bits in lane 0. Lane 1 of each operand is ignored.

When the build targets x86-64 with the pclmul feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

sha256Rounds2#

silk
pub fn sha256Rounds2(cdgh: simd<u32, 4>, abef: simd<u32, 4>, wk: simd<u32, 4>) -> simd<u32, 4>

sha256rnds2 (SHA): two SHA-256 rounds. cdgh holds C, D, G and H in lanes 3, 2, 1 and 0, abef holds A, B, E and F the same way, and lanes 0 and 1 of wk hold the next two message words plus their round constants. Returns the new A, B, E and F; the new C, D, G and H are the old abef.

When the build targets x86-64 with the sha feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

sha256Message1#

silk
pub fn sha256Message1(left: simd<u32, 4>, right: simd<u32, 4>) -> simd<u32, 4>

sha256msg1 (SHA): the first step of the next four SHA-256 message words. With W[0..3] in left and W[4] in lane 0 of right, lane i is W[i] + sigma0(W[i + 1]).

When the build targets x86-64 with the sha feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

sha256Message2#

silk
pub fn sha256Message2(left: simd<u32, 4>, right: simd<u32, 4>) -> simd<u32, 4>

sha256msg2 (SHA): the last step of the next four SHA-256 message words W[16..19]. left holds the partial sums, and lanes 2 and 3 of right hold W[14] and W[15]; lane i is left[i] + sigma1(W[i + 14]).

When the build targets x86-64 with the sha feature, it compiles to this one instruction. Otherwise it computes the same lanes with portable code, which is correct but slower. Check Target.hasCpuFeature with static if before you select this path for speed.

Category: SIMD.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page