comparison src/impl/arm/neon.c @ 28:c6c99ab1088a

*: add min/max functions and a big big refactor (again) agh, this time I added a few more implementations (and generally made the code just a little faster...)
author Paper <paper@tflc.us>
date Thu, 24 Apr 2025 00:54:02 -0400
parents d00b95f95dd1
children bf6ad516f1e6
comparison
equal deleted inserted replaced
27:d00b95f95dd1 28:c6c99ab1088a
37 }; \ 37 }; \
38 \ 38 \
39 VEC_STATIC_ASSERT(VEC_ALIGNOF(sign##int##bits##x##size##_t) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \ 39 VEC_STATIC_ASSERT(VEC_ALIGNOF(sign##int##bits##x##size##_t) <= VEC_ALIGNOF(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " alignment needs to be expanded to fit intrinsic type size"); \
40 VEC_STATIC_ASSERT(sizeof(sign##int##bits##x##size##_t) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \ 40 VEC_STATIC_ASSERT(sizeof(sign##int##bits##x##size##_t) <= sizeof(v##sign##int##bits##x##size), "vec: v" #sign "int" #bits "x" #size " needs to be expanded to fit intrinsic type size"); \
41 \ 41 \
42 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_load_aligned(const vec_##sign##int##bits in[size]) \ 42 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_load_aligned(const vec_##sign##int##bits in[size]) \
43 { \ 43 { \
44 union v##sign##int##bits##x##size##_impl_data vec; \ 44 union v##sign##int##bits##x##size##_impl_data vec; \
45 vec.neon = vld1_##sign##bits(in); \ 45 vec.neon = vld1_##sign##bits(in); \
46 return vec.vec; \ 46 return vec.vec; \
47 } \ 47 } \
48 \ 48 \
49 static void v##sign##int##bits##x##size##_neon_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \ 49 VEC_FUNC_IMPL void v##sign##int##bits##x##size##_neon_store_aligned(v##sign##int##bits##x##size vec, vec_##sign##int##bits out[size]) \
50 { \ 50 { \
51 vstore_lane_##bits(sign, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->neon, out); \ 51 vstore_lane_##bits(sign, ((union v##sign##int##bits##x##size##_impl_data *)&vec)->neon, out); \
52 } \ 52 } \
53 \ 53 \
54 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 54 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_add(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
55 { \ 55 { \
56 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 56 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
57 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 57 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
58 \ 58 \
59 vec1d->neon = vadd_##sign##bits(vec1d->neon, vec2d->neon); \ 59 vec1d->neon = vadd_##sign##bits(vec1d->neon, vec2d->neon); \
60 return vec1d->vec; \ 60 return vec1d->vec; \
61 } \ 61 } \
62 \ 62 \
63 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 63 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_sub(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
64 { \ 64 { \
65 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 65 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
66 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 66 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
67 \ 67 \
68 vec1d->neon = vsub_##sign##bits(vec1d->neon, vec2d->neon); \ 68 vec1d->neon = vsub_##sign##bits(vec1d->neon, vec2d->neon); \
69 return vec1d->vec; \ 69 return vec1d->vec; \
70 } \ 70 } \
71 \ 71 \
72 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 72 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_mul(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
73 { \ 73 { \
74 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 74 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
75 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 75 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
76 \ 76 \
77 vec1d->neon = vmul_##sign##bits(vec1d->neon, vec2d->neon); \ 77 vec1d->neon = vmul_##sign##bits(vec1d->neon, vec2d->neon); \
78 return vec1d->vec; \ 78 return vec1d->vec; \
79 } \ 79 } \
80 \ 80 \
81 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \ 81 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_lshift(v##sign##int##bits##x##size vec1, vuint##bits##x##size vec2) \
82 { \ 82 { \
83 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 83 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
84 union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \ 84 union vuint##bits##x##size##_impl_data *vec2d = (union vuint##bits##x##size##_impl_data *)&vec2; \
85 \ 85 \
86 vec1d->neon = vshl_##sign##bits(vec1d->neon, (vreinterpret_##bits##_u##bits)vec2d->neon); \ 86 vec1d->neon = vshl_##sign##bits(vec1d->neon, (vreinterpret_##bits##_u##bits)vec2d->neon); \
87 return vec1d->vec; \ 87 return vec1d->vec; \
88 } \ 88 } \
89 \ 89 \
90 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 90 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_and(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
91 { \ 91 { \
92 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 92 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
93 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 93 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
94 \ 94 \
95 vec1d->neon = vand_##sign##bits(vec1d->neon, vec2d->neon); \ 95 vec1d->neon = vand_##sign##bits(vec1d->neon, vec2d->neon); \
96 return vec1d->vec; \ 96 return vec1d->vec; \
97 } \ 97 } \
98 \ 98 \
99 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 99 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_or(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
100 { \ 100 { \
101 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 101 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
102 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 102 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
103 \ 103 \
104 vec1d->neon = vorr_##sign##bits(vec1d->neon, vec2d->neon); \ 104 vec1d->neon = vorr_##sign##bits(vec1d->neon, vec2d->neon); \
105 return vec1d->vec; \ 105 return vec1d->vec; \
106 } \ 106 } \
107 \ 107 \
108 static v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \ 108 VEC_FUNC_IMPL v##sign##int##bits##x##size v##sign##int##bits##x##size##_neon_xor(v##sign##int##bits##x##size vec1, v##sign##int##bits##x##size vec2) \
109 { \ 109 { \
110 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \ 110 union v##sign##int##bits##x##size##_impl_data *vec1d = (union v##sign##int##bits##x##size##_impl_data *)&vec1; \
111 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \ 111 union v##sign##int##bits##x##size##_impl_data *vec2d = (union v##sign##int##bits##x##size##_impl_data *)&vec2; \
112 \ 112 \
113 vec1d->neon = veor_##sign##bits(vec1d->neon, vec2d->neon); \ 113 vec1d->neon = veor_##sign##bits(vec1d->neon, vec2d->neon); \
114 return vec1d->vec; \ 114 return vec1d->vec; \
115 } \ 115 } \
116 \ 116 \
117 static v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_neon = { \ 117 const v##sign##int##bits##x##size##_impl v##sign##int##bits##x##size##_impl_neon = { \
118 v##sign##int##bits##x##size##_fallback_splat, \ 118 .load = v##sign##int##bits##x##size##_neon_load_aligned, \
119 v##sign##int##bits##x##size##_neon_load_aligned, \ 119 .load_aligned = v##sign##int##bits##x##size##_neon_load_aligned, \
120 v##sign##int##bits##x##size##_neon_load_aligned, \ 120 .store = v##sign##int##bits##x##size##_neon_store_aligned, \
121 v##sign##int##bits##x##size##_neon_store_aligned, \ 121 .store_aligned = v##sign##int##bits##x##size##_neon_store_aligned, \
122 v##sign##int##bits##x##size##_neon_store_aligned, \ 122 .add = v##sign##int##bits##x##size##_neon_add, \
123 v##sign##int##bits##x##size##_neon_add, \ 123 .sub = v##sign##int##bits##x##size##_neon_sub, \
124 v##sign##int##bits##x##size##_neon_sub, \ 124 .mul = v##sign##int##bits##x##size##_neon_mul, \
125 v##sign##int##bits##x##size##_neon_mul, \ 125 .band = v##sign##int##bits##x##size##_neon_and, \
126 v##sign##int##bits##x##size##_fallback_div, \ 126 .bor = v##sign##int##bits##x##size##_neon_or, \
127 v##sign##int##bits##x##size##_fallback_avg, \ 127 .bxor = v##sign##int##bits##x##size##_neon_xor, \
128 v##sign##int##bits##x##size##_neon_and, \ 128 .lshift = v##sign##int##bits##x##size##_neon_lshift, \
129 v##sign##int##bits##x##size##_neon_or, \
130 v##sign##int##bits##x##size##_neon_xor, \
131 v##sign##int##bits##x##size##_fallback_not, \
132 v##sign##int##bits##x##size##_neon_lshift, \
133 v##sign##int##bits##x##size##_fallback_rshift, \
134 v##sign##int##bits##x##size##_fallback_lrshift, \
135 v##sign##int##bits##x##size##_fallback_cmplt, \
136 v##sign##int##bits##x##size##_fallback_cmple, \
137 v##sign##int##bits##x##size##_fallback_cmpeq, \
138 v##sign##int##bits##x##size##_fallback_cmpge, \
139 v##sign##int##bits##x##size##_fallback_cmpgt, \
140 }; 129 };
141 130
142 #define VEC_DEFINE_OPERATIONS(bits, size) \ 131 #define VEC_DEFINE_OPERATIONS(bits, size) \
143 VEC_DEFINE_OPERATIONS_SIGN(u, U, bits, size) \ 132 VEC_DEFINE_OPERATIONS_SIGN(u, U, bits, size) \
144 VEC_DEFINE_OPERATIONS_SIGN( , , bits, size) 133 VEC_DEFINE_OPERATIONS_SIGN( , , bits, size)
242 231
243 // Now we can go ahead and do the 128-bit ones. 232 // Now we can go ahead and do the 128-bit ones.
244 233
245 // NEON doesn't have native 64-bit multiplication, so we have 234 // NEON doesn't have native 64-bit multiplication, so we have
246 // to do it ourselves 235 // to do it ourselves
247 static inline int64x2_t vmulq_s64(const int64x2_t a, const int64x2_t b) 236 VEC_FUNC_IMPL int64x2_t vmulq_s64(const int64x2_t a, const int64x2_t b)
248 { 237 {
249 const uint32x2_t ac = vreinterpret_u32_s32(vmovn_s64(a)); 238 const uint32x2_t ac = vreinterpret_u32_s32(vmovn_s64(a));
250 const uint32x2_t pr = vreinterpret_u32_s32(vmovn_s64(b)); 239 const uint32x2_t pr = vreinterpret_u32_s32(vmovn_s64(b));
251 240
252 const int32x4_t hi = vmulq_s32(vreinterpretq_s32_s64(b), vreinterpretq_s32_s64(a)); 241 const int32x4_t hi = vmulq_s32(vreinterpretq_s32_s64(b), vreinterpretq_s32_s64(a));
253 242
254 return vreinterpretq_s64_u64(vmlal_u32(vreinterpretq_u64_s64(vshlq_n_s64(vreinterpretq_s64_u64(vpaddlq_u32(vreinterpretq_u32_s32(hi))), 32)), ac, pr)); 243 return vreinterpretq_s64_u64(vmlal_u32(vreinterpretq_u64_s64(vshlq_n_s64(vreinterpretq_s64_u64(vpaddlq_u32(vreinterpretq_u32_s32(hi))), 32)), ac, pr));
255 } 244 }
256 245
257 static inline uint64x2_t vmulq_u64(const uint64x2_t a, const uint64x2_t b) 246 VEC_FUNC_IMPL uint64x2_t vmulq_u64(const uint64x2_t a, const uint64x2_t b)
258 { 247 {
259 const uint32x2_t ac = vmovn_u64(a); 248 const uint32x2_t ac = vmovn_u64(a);
260 const uint32x2_t pr = vmovn_u64(b); 249 const uint32x2_t pr = vmovn_u64(b);
261 250
262 const uint32x4_t hi = vmulq_u32(vreinterpretq_u32_u64(b), vreinterpretq_u32_u64(a)); 251 const uint32x4_t hi = vmulq_u32(vreinterpretq_u32_u64(b), vreinterpretq_u32_u64(a));