1#![allow(unsafe_op_in_unsafe_fn, reason = "needs triage")]
4#![allow(clippy::cast_possible_truncation, reason = "needs triage")]
5#![allow(clippy::cast_possible_wrap, reason = "needs triage")]
6#![allow(clippy::cast_sign_loss, reason = "needs triage")]
7#![allow(clippy::undocumented_unsafe_blocks, reason = "TODO")]
8
9use crate::{Rounds, STATE_WORDS, Variant};
10
11#[cfg(feature = "rng")]
12use crate::ChaChaCore;
13
14#[cfg(feature = "cipher")]
15use crate::chacha::Block;
16#[cfg(feature = "cipher")]
17use cipher::{
18 BlockSizeUser, ParBlocksSizeUser, StreamCipherBackend, StreamCipherClosure,
19 consts::{U4, U64},
20};
21use core::marker::PhantomData;
22
23#[cfg(target_arch = "x86")]
24use core::arch::x86::*;
25#[cfg(target_arch = "x86_64")]
26use core::arch::x86_64::*;
27
28const PAR_BLOCKS: usize = 4;
29
30#[inline]
31#[target_feature(enable = "sse2")]
32#[cfg(feature = "cipher")]
33pub(crate) unsafe fn inner<R, F, V>(state: &mut [u32; STATE_WORDS], f: F)
34where
35 R: Rounds,
36 F: StreamCipherClosure<BlockSize = U64>,
37 V: Variant,
38{
39 let mut backend = Backend::<R, V>::new(state);
40 f.call(&mut backend);
41 backend.save_ctr(state);
42}
43
44#[inline]
45#[target_feature(enable = "sse2")]
46#[cfg(feature = "rng")]
47pub(crate) unsafe fn rng_inner<R, V>(core: &mut ChaChaCore<R, V>, buffer: &mut [u32; 64])
48where
49 R: Rounds,
50 V: Variant,
51{
52 let mut backend = Backend::<R, V>::new(&core.state);
53 backend.gen_ks_blocks(buffer);
54 backend.save_ctr(&mut core.state);
55}
56
57struct Backend<R: Rounds, V: Variant> {
58 v: [__m128i; 4],
59 _pd: PhantomData<(R, V)>,
60}
61
62impl<R: Rounds, V: Variant> Backend<R, V> {
63 unsafe fn new(state: &[u32; STATE_WORDS]) -> Self {
64 let state_ptr = state.as_ptr().cast::<__m128i>();
65 Self {
66 v: core::array::from_fn(|i| _mm_loadu_si128(state_ptr.add(i))),
67 _pd: PhantomData,
68 }
69 }
70
71 unsafe fn save_ctr(self, state: &mut [u32; STATE_WORDS]) {
72 let state_ptr = state.as_mut_ptr().cast::<__m128i>();
73 _mm_storeu_si128(state_ptr.add(3), self.v[3]);
74 }
75}
76
77#[cfg(feature = "cipher")]
78impl<R: Rounds, V: Variant> BlockSizeUser for Backend<R, V> {
79 type BlockSize = U64;
80}
81
82#[cfg(feature = "cipher")]
83impl<R: Rounds, V: Variant> ParBlocksSizeUser for Backend<R, V> {
84 type ParBlocksSize = U4;
85}
86
87#[cfg(feature = "cipher")]
88impl<R: Rounds, V: Variant> StreamCipherBackend for Backend<R, V> {
89 #[inline(always)]
90 fn gen_ks_block(&mut self, block: &mut Block) {
91 unsafe {
92 let res = rounds::<R, V>(&self.v);
93 self.v[3] = match size_of::<V::Counter>() {
94 4 => _mm_add_epi32(self.v[3], _mm_set_epi32(0, 0, 0, 1)),
95 8 => _mm_add_epi64(self.v[3], _mm_set_epi64x(0, 1)),
96 _ => unreachable!(),
97 };
98
99 let block_ptr = block.as_mut_ptr().cast::<__m128i>();
100 for i in 0..4 {
101 _mm_storeu_si128(block_ptr.add(i), res[0][i]);
102 }
103 }
104 }
105
106 #[inline(always)]
107 fn gen_par_ks_blocks(&mut self, blocks: &mut cipher::ParBlocks<Self>) {
108 unsafe {
109 let res = rounds::<R, V>(&self.v);
110 self.v[3] = match size_of::<V::Counter>() {
111 4 => _mm_add_epi32(self.v[3], _mm_set_epi32(0, 0, 0, PAR_BLOCKS as i32)),
112 8 => _mm_add_epi64(self.v[3], _mm_set_epi64x(0, PAR_BLOCKS as i64)),
113 _ => unreachable!(),
114 };
115
116 let blocks_ptr = blocks.as_mut_ptr().cast::<__m128i>();
117 for block in 0..PAR_BLOCKS {
118 for i in 0..4 {
119 _mm_storeu_si128(blocks_ptr.add(i + block * PAR_BLOCKS), res[block][i]);
120 }
121 }
122 }
123 }
124}
125
126#[cfg(feature = "rng")]
127impl<R: Rounds, V: Variant> Backend<R, V> {
128 #[inline(always)]
129 fn gen_ks_blocks(&mut self, block: &mut [u32; 64]) {
130 const _: () = assert!(4 * PAR_BLOCKS * size_of::<__m128i>() == size_of::<[u32; 64]>());
131 unsafe {
132 let res = rounds::<R, V>(&self.v);
133 self.v[3] = _mm_add_epi64(self.v[3], _mm_set_epi64x(0, PAR_BLOCKS as i64));
134
135 let blocks_ptr = block.as_mut_ptr().cast::<__m128i>();
136 for block in 0..PAR_BLOCKS {
137 for i in 0..4 {
138 _mm_storeu_si128(blocks_ptr.add(i + block * PAR_BLOCKS), res[block][i]);
139 }
140 }
141 }
142 }
143}
144
145#[inline]
146#[target_feature(enable = "sse2")]
147unsafe fn rounds<R: Rounds, V: Variant>(v: &[__m128i; 4]) -> [[__m128i; 4]; PAR_BLOCKS] {
148 let mut res = [*v; 4];
149 for block in 1..PAR_BLOCKS {
150 res[block][3] = match size_of::<V::Counter>() {
151 4 => _mm_add_epi32(res[block][3], _mm_set_epi32(0, 0, 0, block as i32)),
152 8 => _mm_add_epi64(res[block][3], _mm_set_epi64x(0, block as i64)),
153 _ => unreachable!(),
154 }
155 }
156
157 for _ in 0..R::COUNT {
158 double_quarter_round(&mut res);
159 }
160
161 for block in 0..PAR_BLOCKS {
162 for i in 0..3 {
163 res[block][i] = _mm_add_epi32(res[block][i], v[i]);
164 }
165 let ctr = match size_of::<V::Counter>() {
166 4 => _mm_add_epi32(v[3], _mm_set_epi32(0, 0, 0, block as i32)),
167 8 => _mm_add_epi64(v[3], _mm_set_epi64x(0, block as i64)),
168 _ => unreachable!(),
169 };
170 res[block][3] = _mm_add_epi32(res[block][3], ctr);
171 }
172
173 res
174}
175
176#[inline]
177#[target_feature(enable = "sse2")]
178unsafe fn double_quarter_round(v: &mut [[__m128i; 4]; PAR_BLOCKS]) {
179 add_xor_rot(v);
180 rows_to_cols(v);
181 add_xor_rot(v);
182 cols_to_rows(v);
183}
184
185#[inline]
221#[target_feature(enable = "sse2")]
222unsafe fn rows_to_cols(blocks: &mut [[__m128i; 4]; PAR_BLOCKS]) {
223 for [a, _, c, d] in blocks.iter_mut() {
224 *c = _mm_shuffle_epi32(*c, 0b_00_11_10_01); *d = _mm_shuffle_epi32(*d, 0b_01_00_11_10); *a = _mm_shuffle_epi32(*a, 0b_10_01_00_11); }
229}
230
231#[inline]
249#[target_feature(enable = "sse2")]
250unsafe fn cols_to_rows(blocks: &mut [[__m128i; 4]; PAR_BLOCKS]) {
251 for [a, _, c, d] in blocks.iter_mut() {
252 *c = _mm_shuffle_epi32(*c, 0b_10_01_00_11); *d = _mm_shuffle_epi32(*d, 0b_01_00_11_10); *a = _mm_shuffle_epi32(*a, 0b_00_11_10_01); }
257}
258
259#[inline]
260#[target_feature(enable = "sse2")]
261unsafe fn add_xor_rot(blocks: &mut [[__m128i; 4]; PAR_BLOCKS]) {
262 for [a, b, c, d] in blocks.iter_mut() {
263 *a = _mm_add_epi32(*a, *b);
265 *d = _mm_xor_si128(*d, *a);
266 *d = _mm_xor_si128(_mm_slli_epi32(*d, 16), _mm_srli_epi32(*d, 16));
267
268 *c = _mm_add_epi32(*c, *d);
270 *b = _mm_xor_si128(*b, *c);
271 *b = _mm_xor_si128(_mm_slli_epi32(*b, 12), _mm_srli_epi32(*b, 20));
272
273 *a = _mm_add_epi32(*a, *b);
275 *d = _mm_xor_si128(*d, *a);
276 *d = _mm_xor_si128(_mm_slli_epi32(*d, 8), _mm_srli_epi32(*d, 24));
277
278 *c = _mm_add_epi32(*c, *d);
280 *b = _mm_xor_si128(*b, *c);
281 *b = _mm_xor_si128(_mm_slli_epi32(*b, 7), _mm_srli_epi32(*b, 25));
282 }
283}