2 * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic
3 * James S. Plank, Ethan L. Miller, Kevin M. Greenan,
4 * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride.
6 * Copyright (c) 2014: Janne Grunau <j@jannau.net>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * - Neither the name of the University of Tennessee nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
31 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
34 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * Neon optimized routines for 8-bit Galois fields
48 /* ARM NEON reducing macro for the carry free multiplication
49 * vmull_p8 is the carryless multiply operation. Here vshrn_n_u16 shifts
50 * the result to the right by 1 byte. This allows us to multiply
51 * the prim_poly by the leading bits of the result. We then xor the result
52 * of that operation back with the result. */
53 #define NEON_CFM_REDUCE(v, w, result, prim_poly, initial) \
56 v = vshrn_n_u16 (vreinterpretq_u16_p16(result), 8); \
58 v = veor_u8 (v, vshrn_n_u16 (vreinterpretq_u16_p16(result), 8)); \
59 w = vmull_p8 (prim_poly, vreinterpret_p8_u8(v)); \
60 result = vreinterpretq_p16_u16 (veorq_u16 (vreinterpretq_u16_p16(result), vreinterpretq_u16_p16(w))); \
66 gf_w8_neon_clm_multiply_x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8, int x)
74 gf_internal_t * h = gf->scratch;
79 prim_poly = vdup_n_p8 ((uint32_t)(h->prim_poly & 0x1ffULL));
81 /* Do the initial multiply */
82 result = vmull_p8 (a, b);
84 /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only
85 have to do the reduction at most twice, because (w-2)/z == 2. Where
86 z is equal to the number of zeros after the leading 1 */
87 NEON_CFM_REDUCE (v, w, result, prim_poly, 1);
88 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
90 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
93 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
95 /* Extracts 32 bit value from result. */
96 rv = (gf_val_32_t)vget_lane_u8 (vmovn_u16 (vreinterpretq_u16_p16 (result)), 0);
101 #define CLM_MULTIPLY(x) \
102 static gf_val_32_t gf_w8_neon_clm_multiply_ ## x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) \
104 return gf_w8_neon_clm_multiply_x (gf, a8, b8, x);\
112 neon_clm_multiply_region_from_single_x(gf_t *gf, uint8_t *s8, uint8_t *d8,
113 gf_val_32_t val, uint8_t *d_end,
116 gf_internal_t * h = gf->scratch;
124 prim_poly = vdup_n_p8 ((uint8_t)(h->prim_poly & 0xffULL));
127 b = vld1_p8 ((poly8_t *) s8);
132 result = vmull_p8 (a, b);
134 NEON_CFM_REDUCE(v, w, result, prim_poly, 1);
135 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
137 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
140 NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
142 v = vmovn_u16 (vreinterpretq_u16_p16 (result));
153 #define CLM_MULT_REGION(x) \
155 gf_w8_neon_clm_multiply_region_from_single_ ## x (gf_t *gf, void *src, \
157 gf_val_32_t val, int bytes, \
164 if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } \
165 if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } \
167 gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); \
168 gf_do_initial_region_alignment(&rd); \
169 s8 = (uint8_t *) rd.s_start; \
170 d8 = (uint8_t *) rd.d_start; \
173 neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 1, x); \
175 neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 0, x);\
176 gf_do_final_region_alignment(&rd); \
184 int gf_w8_neon_cfm_init(gf_t *gf)
188 h = (gf_internal_t *) gf->scratch;
190 if ((0xe0 & h->prim_poly) == 0){
191 SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_2)
192 SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_2)
193 }else if ((0xc0 & h->prim_poly) == 0){
194 SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_3)
195 SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_3)
196 }else if ((0x80 & h->prim_poly) == 0){
197 SET_FUNCTION(gf,multiply,w32,gf_w8_neon_clm_multiply_4)
198 SET_FUNCTION(gf,multiply_region,w32,gf_w8_neon_clm_multiply_region_from_single_4)
206 #define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \
207 vtbl2_u8(tbl, vget_high_u8(v)))
212 gf_w8_split_multiply_region_neon(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor)
214 uint8_t *bh, *bl, *sptr, *dptr;
215 uint8x16_t r, va, vh, vl, loset;
219 uint8x8x2_t mth, mtl;
221 struct gf_w8_half_table_data *htd;
224 if (val == 0) { gf_multby_zero(dest, bytes, xor); return; }
225 if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; }
227 htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) (gf->scratch))->private;
229 gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16);
230 gf_do_initial_region_alignment(&rd);
232 bh = (uint8_t *) htd->high;
234 bl = (uint8_t *) htd->low;
244 mth.val[0] = vld1_u8 (bh);
245 mtl.val[0] = vld1_u8 (bl);
246 mth.val[1] = vld1_u8 (bh + 8);
247 mtl.val[1] = vld1_u8 (bl + 8);
250 loset = vdupq_n_u8(0xf);
253 while (sptr < (uint8_t *) rd.s_top) {
254 va = vld1q_u8 (sptr);
256 vh = vshrq_n_u8 (va, 4);
257 vl = vandq_u8 (va, loset);
258 va = vld1q_u8 (dptr);
260 vh = vqtbl1q_u8 (mth, vh);
261 vl = vqtbl1q_u8 (mtl, vl);
263 r = veorq_u8 (vh, vl);
265 vst1q_u8 (dptr, veorq_u8 (va, r));
271 while (sptr < (uint8_t *) rd.s_top) {
272 va = vld1q_u8 (sptr);
274 vh = vshrq_n_u8 (va, 4);
275 vl = vandq_u8 (va, loset);
277 vh = vqtbl1q_u8 (mth, vh);
278 vl = vqtbl1q_u8 (mtl, vl);
280 vh = vcombine_u8 (vtbl2_u8 (mth, vget_low_u8 (vh)),
281 vtbl2_u8 (mth, vget_high_u8 (vh)));
282 vl = vcombine_u8 (vtbl2_u8 (mtl, vget_low_u8 (vl)),
283 vtbl2_u8 (mtl, vget_high_u8 (vl)));
286 r = veorq_u8 (vh, vl);
295 gf_do_final_region_alignment(&rd);
299 void gf_w8_neon_split_init(gf_t *gf)
301 SET_FUNCTION(gf,multiply_region,w32,gf_w8_split_multiply_region_neon)