summaryrefslogtreecommitdiff
path: root/src/neon/gf_w8_neon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/neon/gf_w8_neon.c')
-rw-r--r--src/neon/gf_w8_neon.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/neon/gf_w8_neon.c b/src/neon/gf_w8_neon.c
new file mode 100644
index 0000000..930a916
--- /dev/null
+++ b/src/neon/gf_w8_neon.c
@@ -0,0 +1,302 @@
+/*
+ * GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic
+ * James S. Plank, Ethan L. Miller, Kevin M. Greenan,
+ * Benjamin A. Arnold, John A. Burnum, Adam W. Disney, Allen C. McBride.
+ *
+ * Copyright (c) 2014: Janne Grunau <j@jannau.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * - Neither the name of the University of Tennessee nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+ * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * gf_w8_neon.c
+ *
+ * Neon optimized routines for 8-bit Galois fields
+ *
+ */
+
+#include "gf_int.h"
+#include "gf_w8.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/* ARM NEON reducing macro for the carry free multiplication
+ * vmull_p8 is the carryless multiply operation. Here vshrn_n_u16 shifts
+ * the result to the right by 1 byte. This allows us to multiply
+ * the prim_poly by the leading bits of the result. We then xor the result
+ * of that operation back with the result. */
+#define NEON_CFM_REDUCE(v, w, result, prim_poly, initial) \
+ do { \
+ if (initial) \
+ v = vshrn_n_u16 (vreinterpretq_u16_p16(result), 8); \
+ else \
+ v = veor_u8 (v, vshrn_n_u16 (vreinterpretq_u16_p16(result), 8)); \
+ w = vmull_p8 (prim_poly, vreinterpret_p8_u8(v)); \
+ result = vreinterpretq_p16_u16 (veorq_u16 (vreinterpretq_u16_p16(result), vreinterpretq_u16_p16(w))); \
+ } while (0)
+
+static
+inline
+gf_val_32_t
+gf_w8_neon_clm_multiply_x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8, int x)
+{
+ gf_val_32_t rv = 0;
+ poly8x8_t a, b;
+ uint8x8_t v;
+ poly16x8_t result;
+ poly8x8_t prim_poly;
+ poly16x8_t w;
+ gf_internal_t * h = gf->scratch;
+
+ a = vdup_n_p8 (a8);
+ b = vdup_n_p8 (b8);
+
+ prim_poly = vdup_n_p8 ((uint32_t)(h->prim_poly & 0x1ffULL));
+
+ /* Do the initial multiply */
+ result = vmull_p8 (a, b);
+
+ /* Ben: Do prim_poly reduction twice. We are guaranteed that we will only
+ have to do the reduction at most twice, because (w-2)/z == 2. Where
+ z is equal to the number of zeros after the leading 1 */
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 1);
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ if (x >= 3) {
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ }
+ if (x >= 4) {
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ }
+ /* Extracts 32 bit value from result. */
+ rv = (gf_val_32_t)vget_lane_u8 (vmovn_u16 (vreinterpretq_u16_p16 (result)), 0);
+
+ return rv;
+}
+
+#define CLM_MULTIPLY(x) \
+static gf_val_32_t gf_w8_neon_clm_multiply_ ## x (gf_t *gf, gf_val_32_t a8, gf_val_32_t b8) \
+{\
+ return gf_w8_neon_clm_multiply_x (gf, a8, b8, x);\
+}
+
+CLM_MULTIPLY(2)
+CLM_MULTIPLY(3)
+CLM_MULTIPLY(4)
+
+static inline void
+neon_clm_multiply_region_from_single_x(gf_t *gf, uint8_t *s8, uint8_t *d8,
+ gf_val_32_t val, uint8_t *d_end,
+ int xor, int x)
+{
+ gf_internal_t * h = gf->scratch;
+ poly8x8_t a, b;
+ uint8x8_t c, v;
+ poly16x8_t result;
+ poly8x8_t prim_poly;
+ poly16x8_t w;
+
+ a = vdup_n_p8 (val);
+ prim_poly = vdup_n_p8 ((uint8_t)(h->prim_poly & 0xffULL));
+
+ while (d8 < d_end) {
+ b = vld1_p8 ((poly8_t *) s8);
+
+ if (xor)
+ c = vld1_u8 (d8);
+
+ result = vmull_p8 (a, b);
+
+ NEON_CFM_REDUCE(v, w, result, prim_poly, 1);
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ if (x >= 3) {
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ }
+ if (x >= 4) {
+ NEON_CFM_REDUCE (v, w, result, prim_poly, 0);
+ }
+ v = vmovn_u16 (vreinterpretq_u16_p16 (result));
+ if (xor)
+ v = veor_u8 (c, v);
+
+ vst1_u8 (d8, v);
+
+ d8 += 8;
+ s8 += 8;
+ }
+}
+
+#define CLM_MULT_REGION(x) \
+static void \
+gf_w8_neon_clm_multiply_region_from_single_ ## x (gf_t *gf, void *src, \
+ void *dest, \
+ gf_val_32_t val, int bytes, \
+ int xor) \
+{ \
+ gf_region_data rd; \
+ uint8_t *s8; \
+ uint8_t *d8; \
+ \
+ if (val == 0) { gf_multby_zero(dest, bytes, xor); return; } \
+ if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; } \
+ \
+ gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16); \
+ gf_do_initial_region_alignment(&rd); \
+ s8 = (uint8_t *) rd.s_start; \
+ d8 = (uint8_t *) rd.d_start; \
+ \
+ if (xor) \
+ neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 1, x); \
+ else \
+ neon_clm_multiply_region_from_single_x (gf, s8, d8, val, rd.d_top, 0, x);\
+ gf_do_final_region_alignment(&rd); \
+}
+
+CLM_MULT_REGION(2)
+CLM_MULT_REGION(3)
+CLM_MULT_REGION(4)
+
+
+int gf_w8_neon_cfm_init(gf_t *gf)
+{
+ gf_internal_t *h;
+
+ h = (gf_internal_t *) gf->scratch;
+
+ if ((0xe0 & h->prim_poly) == 0){
+ gf->multiply.w32 = gf_w8_neon_clm_multiply_2;
+ gf->multiply_region.w32 = gf_w8_neon_clm_multiply_region_from_single_2;
+ }else if ((0xc0 & h->prim_poly) == 0){
+ gf->multiply.w32 = gf_w8_neon_clm_multiply_3;
+ gf->multiply_region.w32 = gf_w8_neon_clm_multiply_region_from_single_3;
+ }else if ((0x80 & h->prim_poly) == 0){
+ gf->multiply.w32 = gf_w8_neon_clm_multiply_4;
+ gf->multiply_region.w32 = gf_w8_neon_clm_multiply_region_from_single_4;
+ }else{
+ return 0;
+ }
+ return 1;
+}
+
+#ifndef ARCH_AARCH64
+#define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \
+ vtbl2_u8(tbl, vget_high_u8(v)))
+#endif
+
+static
+void
+gf_w8_split_multiply_region_neon(gf_t *gf, void *src, void *dest, gf_val_32_t val, int bytes, int xor)
+{
+ uint8_t *bh, *bl, *sptr, *dptr;
+ uint8x16_t r, va, vh, vl, loset;
+#ifdef ARCH_AARCH64
+ uint8x16_t mth, mtl;
+#else
+ uint8x8x2_t mth, mtl;
+#endif
+ struct gf_w8_half_table_data *htd;
+ gf_region_data rd;
+
+ if (val == 0) { gf_multby_zero(dest, bytes, xor); return; }
+ if (val == 1) { gf_multby_one(src, dest, bytes, xor); return; }
+
+ htd = (struct gf_w8_half_table_data *) ((gf_internal_t *) (gf->scratch))->private;
+
+ gf_set_region_data(&rd, gf, src, dest, bytes, val, xor, 16);
+ gf_do_initial_region_alignment(&rd);
+
+ bh = (uint8_t *) htd->high;
+ bh += (val << 4);
+ bl = (uint8_t *) htd->low;
+ bl += (val << 4);
+
+ sptr = rd.s_start;
+ dptr = rd.d_start;
+
+#ifdef ARCH_AARCH64
+ mth = vld1q_u8 (bh);
+ mtl = vld1q_u8 (bl);
+#else
+ mth.val[0] = vld1_u8 (bh);
+ mtl.val[0] = vld1_u8 (bl);
+ mth.val[1] = vld1_u8 (bh + 8);
+ mtl.val[1] = vld1_u8 (bl + 8);
+#endif
+
+ loset = vdupq_n_u8(0xf);
+
+ if (xor) {
+ while (sptr < (uint8_t *) rd.s_top) {
+ va = vld1q_u8 (sptr);
+
+ vh = vshrq_n_u8 (va, 4);
+ vl = vandq_u8 (va, loset);
+ va = vld1q_u8 (dptr);
+
+ vh = vqtbl1q_u8 (mth, vh);
+ vl = vqtbl1q_u8 (mtl, vl);
+
+ r = veorq_u8 (vh, vl);
+
+ vst1q_u8 (dptr, veorq_u8 (va, r));
+
+ dptr += 16;
+ sptr += 16;
+ }
+ } else {
+ while (sptr < (uint8_t *) rd.s_top) {
+ va = vld1q_u8 (sptr);
+
+ vh = vshrq_n_u8 (va, 4);
+ vl = vandq_u8 (va, loset);
+#ifdef ARCH_AARCH64
+ vh = vqtbl1q_u8 (mth, vh);
+ vl = vqtbl1q_u8 (mtl, vl);
+#else
+ vh = vcombine_u8 (vtbl2_u8 (mth, vget_low_u8 (vh)),
+ vtbl2_u8 (mth, vget_high_u8 (vh)));
+ vl = vcombine_u8 (vtbl2_u8 (mtl, vget_low_u8 (vl)),
+ vtbl2_u8 (mtl, vget_high_u8 (vl)));
+#endif
+
+ r = veorq_u8 (vh, vl);
+
+ vst1q_u8(dptr, r);
+
+ dptr += 16;
+ sptr += 16;
+ }
+ }
+
+ gf_do_final_region_alignment(&rd);
+}
+
+
+void gf_w8_neon_split_init(gf_t *gf)
+{
+ gf->multiply_region.w32 = gf_w8_split_multiply_region_neon;
+}