1*5f39d1b3SJooyung Han // Copyright 2020 Google Inc. All Rights Reserved.
2*5f39d1b3SJooyung Han //
3*5f39d1b3SJooyung Han // Licensed under the Apache License, Version 2.0 (the "License");
4*5f39d1b3SJooyung Han // you may not use this file except in compliance with the License.
5*5f39d1b3SJooyung Han // You may obtain a copy of the License at
6*5f39d1b3SJooyung Han //
7*5f39d1b3SJooyung Han // http://www.apache.org/licenses/LICENSE-2.0
8*5f39d1b3SJooyung Han //
9*5f39d1b3SJooyung Han // Unless required by applicable law or agreed to in writing, software
10*5f39d1b3SJooyung Han // distributed under the License is distributed on an "AS IS" BASIS,
11*5f39d1b3SJooyung Han // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5f39d1b3SJooyung Han // See the License for the specific language governing permissions and
13*5f39d1b3SJooyung Han // limitations under the License.
14*5f39d1b3SJooyung Han
15*5f39d1b3SJooyung Han // fixedpoint_wasmsimd.h: optimized WAsm SIMD specializations of the templates
16*5f39d1b3SJooyung Han // in fixedpoint.h.
17*5f39d1b3SJooyung Han
18*5f39d1b3SJooyung Han #ifndef GEMMLOWP_INTERNAL_FIXEDPOINT_WASMSIMD_H_
19*5f39d1b3SJooyung Han #define GEMMLOWP_INTERNAL_FIXEDPOINT_WASMSIMD_H_
20*5f39d1b3SJooyung Han
21*5f39d1b3SJooyung Han #include <wasm_simd128.h>
22*5f39d1b3SJooyung Han
23*5f39d1b3SJooyung Han namespace gemmlowp {
24*5f39d1b3SJooyung Han
25*5f39d1b3SJooyung Han // WAsm SIMD intrinsics are not typed: there is a single v128_t vector
26*5f39d1b3SJooyung Han // type that does not distinguish between "int32x4" and "int16x8" use
27*5f39d1b3SJooyung Han // cases, unlike the NEON equivalents. Because we had initially focused
28*5f39d1b3SJooyung Han // on int32x4, we did not pay attention and specialized these fixedpoint
29*5f39d1b3SJooyung Han // templates directly for v128_t hardcoding the int32x4 semantics,
30*5f39d1b3SJooyung Han // not leaving room for int16x8 semantics. Amending that by adding a separate
31*5f39d1b3SJooyung Han // data type, int16x8_v128_t, that wraps v128_t while being a separate
32*5f39d1b3SJooyung Han // type.
33*5f39d1b3SJooyung Han struct int16x8_v128_t {
34*5f39d1b3SJooyung Han v128_t v;
35*5f39d1b3SJooyung Han };
36*5f39d1b3SJooyung Han
37*5f39d1b3SJooyung Han // Keep int16x8_v128_t trivially constructible/destructible and provide
38*5f39d1b3SJooyung Han // easily optimized helper function.
to_int16x8_v128_t(v128_t w)39*5f39d1b3SJooyung Han inline int16x8_v128_t to_int16x8_v128_t(v128_t w) {
40*5f39d1b3SJooyung Han int16x8_v128_t r;
41*5f39d1b3SJooyung Han r.v = w;
42*5f39d1b3SJooyung Han return r;
43*5f39d1b3SJooyung Han }
44*5f39d1b3SJooyung Han
45*5f39d1b3SJooyung Han template <>
46*5f39d1b3SJooyung Han struct FixedPointRawTypeTraits<v128_t> {
47*5f39d1b3SJooyung Han typedef std::int32_t ScalarRawType;
48*5f39d1b3SJooyung Han static constexpr int kLanes = 4;
49*5f39d1b3SJooyung Han };
50*5f39d1b3SJooyung Han
51*5f39d1b3SJooyung Han template <>
52*5f39d1b3SJooyung Han struct FixedPointRawTypeTraits<int16x8_v128_t> {
53*5f39d1b3SJooyung Han typedef std::int16_t ScalarRawType;
54*5f39d1b3SJooyung Han static constexpr int kLanes = 8;
55*5f39d1b3SJooyung Han };
56*5f39d1b3SJooyung Han
57*5f39d1b3SJooyung Han template <>
58*5f39d1b3SJooyung Han inline v128_t BitAnd(v128_t a, v128_t b) {
59*5f39d1b3SJooyung Han return wasm_v128_and(a, b);
60*5f39d1b3SJooyung Han }
61*5f39d1b3SJooyung Han
62*5f39d1b3SJooyung Han template <>
63*5f39d1b3SJooyung Han inline int16x8_v128_t BitAnd(int16x8_v128_t a, int16x8_v128_t b) {
64*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_v128_and(a.v, b.v));
65*5f39d1b3SJooyung Han }
66*5f39d1b3SJooyung Han
67*5f39d1b3SJooyung Han template <>
68*5f39d1b3SJooyung Han inline v128_t BitOr(v128_t a, v128_t b) {
69*5f39d1b3SJooyung Han return wasm_v128_or(a, b);
70*5f39d1b3SJooyung Han }
71*5f39d1b3SJooyung Han
72*5f39d1b3SJooyung Han template <>
73*5f39d1b3SJooyung Han inline int16x8_v128_t BitOr(int16x8_v128_t a, int16x8_v128_t b) {
74*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_v128_or(a.v, b.v));
75*5f39d1b3SJooyung Han }
76*5f39d1b3SJooyung Han
77*5f39d1b3SJooyung Han template <>
78*5f39d1b3SJooyung Han inline v128_t BitXor(v128_t a, v128_t b) {
79*5f39d1b3SJooyung Han return wasm_v128_xor(a, b);
80*5f39d1b3SJooyung Han }
81*5f39d1b3SJooyung Han
82*5f39d1b3SJooyung Han template <>
83*5f39d1b3SJooyung Han inline int16x8_v128_t BitXor(int16x8_v128_t a, int16x8_v128_t b) {
84*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_v128_xor(a.v, b.v));
85*5f39d1b3SJooyung Han }
86*5f39d1b3SJooyung Han
87*5f39d1b3SJooyung Han template <>
88*5f39d1b3SJooyung Han inline v128_t BitNot(v128_t a) {
89*5f39d1b3SJooyung Han return wasm_v128_not(a);
90*5f39d1b3SJooyung Han }
91*5f39d1b3SJooyung Han
92*5f39d1b3SJooyung Han template <>
93*5f39d1b3SJooyung Han inline int16x8_v128_t BitNot(int16x8_v128_t a) {
94*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_v128_not(a.v));
95*5f39d1b3SJooyung Han }
96*5f39d1b3SJooyung Han
97*5f39d1b3SJooyung Han template <>
98*5f39d1b3SJooyung Han inline v128_t Add(v128_t a, v128_t b) {
99*5f39d1b3SJooyung Han return wasm_i32x4_add(a, b);
100*5f39d1b3SJooyung Han }
101*5f39d1b3SJooyung Han
102*5f39d1b3SJooyung Han template <>
103*5f39d1b3SJooyung Han inline int16x8_v128_t Add(int16x8_v128_t a, int16x8_v128_t b) {
104*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_add(a.v, b.v));
105*5f39d1b3SJooyung Han }
106*5f39d1b3SJooyung Han
107*5f39d1b3SJooyung Han template <>
108*5f39d1b3SJooyung Han inline v128_t Mul(v128_t a, v128_t b) {
109*5f39d1b3SJooyung Han return wasm_i32x4_mul(a, b);
110*5f39d1b3SJooyung Han }
111*5f39d1b3SJooyung Han
112*5f39d1b3SJooyung Han template <>
113*5f39d1b3SJooyung Han inline int16x8_v128_t Mul(int16x8_v128_t a, int16x8_v128_t b) {
114*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_mul(a.v, b.v));
115*5f39d1b3SJooyung Han }
116*5f39d1b3SJooyung Han
117*5f39d1b3SJooyung Han template <>
118*5f39d1b3SJooyung Han inline v128_t Sub(v128_t a, v128_t b) {
119*5f39d1b3SJooyung Han return wasm_i32x4_sub(a, b);
120*5f39d1b3SJooyung Han }
121*5f39d1b3SJooyung Han
122*5f39d1b3SJooyung Han template <>
123*5f39d1b3SJooyung Han inline int16x8_v128_t Sub(int16x8_v128_t a, int16x8_v128_t b) {
124*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_sub(a.v, b.v));
125*5f39d1b3SJooyung Han }
126*5f39d1b3SJooyung Han
127*5f39d1b3SJooyung Han template <>
128*5f39d1b3SJooyung Han inline v128_t Neg(v128_t a) {
129*5f39d1b3SJooyung Han return wasm_i32x4_neg(a);
130*5f39d1b3SJooyung Han }
131*5f39d1b3SJooyung Han
132*5f39d1b3SJooyung Han template <>
133*5f39d1b3SJooyung Han inline int16x8_v128_t Neg(int16x8_v128_t a) {
134*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_neg(a.v));
135*5f39d1b3SJooyung Han }
136*5f39d1b3SJooyung Han
137*5f39d1b3SJooyung Han template <>
138*5f39d1b3SJooyung Han inline v128_t ShiftLeft(v128_t a, int offset) {
139*5f39d1b3SJooyung Han return wasm_i32x4_shl(a, offset);
140*5f39d1b3SJooyung Han }
141*5f39d1b3SJooyung Han
142*5f39d1b3SJooyung Han template <>
143*5f39d1b3SJooyung Han inline int16x8_v128_t ShiftLeft(int16x8_v128_t a, int offset) {
144*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_shl(a.v, offset));
145*5f39d1b3SJooyung Han }
146*5f39d1b3SJooyung Han
147*5f39d1b3SJooyung Han template <>
148*5f39d1b3SJooyung Han inline v128_t ShiftRight(v128_t a, int offset) {
149*5f39d1b3SJooyung Han return wasm_i32x4_shr(a, offset);
150*5f39d1b3SJooyung Han }
151*5f39d1b3SJooyung Han
152*5f39d1b3SJooyung Han template <>
153*5f39d1b3SJooyung Han inline int16x8_v128_t ShiftRight(int16x8_v128_t a, int offset) {
154*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_shr(a.v, offset));
155*5f39d1b3SJooyung Han }
156*5f39d1b3SJooyung Han
157*5f39d1b3SJooyung Han template <>
158*5f39d1b3SJooyung Han inline v128_t SelectUsingMask(v128_t if_mask, v128_t then_val,
159*5f39d1b3SJooyung Han v128_t else_val) {
160*5f39d1b3SJooyung Han return wasm_v128_bitselect(then_val, else_val, if_mask);
161*5f39d1b3SJooyung Han }
162*5f39d1b3SJooyung Han
163*5f39d1b3SJooyung Han template <>
164*5f39d1b3SJooyung Han inline int16x8_v128_t SelectUsingMask(int16x8_v128_t if_mask,
165*5f39d1b3SJooyung Han int16x8_v128_t then_val,
166*5f39d1b3SJooyung Han int16x8_v128_t else_val) {
167*5f39d1b3SJooyung Han return to_int16x8_v128_t(
168*5f39d1b3SJooyung Han wasm_v128_bitselect(then_val.v, else_val.v, if_mask.v));
169*5f39d1b3SJooyung Han }
170*5f39d1b3SJooyung Han
171*5f39d1b3SJooyung Han template <>
172*5f39d1b3SJooyung Han inline v128_t MaskIfEqual(v128_t a, v128_t b) {
173*5f39d1b3SJooyung Han return wasm_i32x4_eq(a, b);
174*5f39d1b3SJooyung Han }
175*5f39d1b3SJooyung Han
176*5f39d1b3SJooyung Han template <>
177*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfEqual(int16x8_v128_t a, int16x8_v128_t b) {
178*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_eq(a.v, b.v));
179*5f39d1b3SJooyung Han }
180*5f39d1b3SJooyung Han
181*5f39d1b3SJooyung Han template <>
182*5f39d1b3SJooyung Han inline v128_t MaskIfNotEqual(v128_t a, v128_t b) {
183*5f39d1b3SJooyung Han return wasm_i32x4_ne(a, b);
184*5f39d1b3SJooyung Han }
185*5f39d1b3SJooyung Han
186*5f39d1b3SJooyung Han template <>
187*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfNotEqual(int16x8_v128_t a, int16x8_v128_t b) {
188*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_ne(a.v, b.v));
189*5f39d1b3SJooyung Han }
190*5f39d1b3SJooyung Han
191*5f39d1b3SJooyung Han template <>
192*5f39d1b3SJooyung Han inline v128_t MaskIfZero(v128_t a) {
193*5f39d1b3SJooyung Han return MaskIfEqual(a, wasm_i32x4_const(0, 0, 0, 0));
194*5f39d1b3SJooyung Han }
195*5f39d1b3SJooyung Han
196*5f39d1b3SJooyung Han template <>
197*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfZero(int16x8_v128_t a) {
198*5f39d1b3SJooyung Han return MaskIfEqual(
199*5f39d1b3SJooyung Han a, to_int16x8_v128_t(wasm_i16x8_const(0, 0, 0, 0, 0, 0, 0, 0)));
200*5f39d1b3SJooyung Han }
201*5f39d1b3SJooyung Han
202*5f39d1b3SJooyung Han template <>
203*5f39d1b3SJooyung Han inline v128_t MaskIfNonZero(v128_t a) {
204*5f39d1b3SJooyung Han return MaskIfNotEqual(a, wasm_i32x4_const(0, 0, 0, 0));
205*5f39d1b3SJooyung Han }
206*5f39d1b3SJooyung Han
207*5f39d1b3SJooyung Han template <>
208*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfNonZero(int16x8_v128_t a) {
209*5f39d1b3SJooyung Han return MaskIfNotEqual(
210*5f39d1b3SJooyung Han a, to_int16x8_v128_t(wasm_i16x8_const(0, 0, 0, 0, 0, 0, 0, 0)));
211*5f39d1b3SJooyung Han }
212*5f39d1b3SJooyung Han
213*5f39d1b3SJooyung Han template <>
214*5f39d1b3SJooyung Han inline v128_t MaskIfGreaterThan(v128_t a, v128_t b) {
215*5f39d1b3SJooyung Han return wasm_i32x4_gt(a, b);
216*5f39d1b3SJooyung Han }
217*5f39d1b3SJooyung Han
218*5f39d1b3SJooyung Han template <>
219*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfGreaterThan(int16x8_v128_t a, int16x8_v128_t b) {
220*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_gt(a.v, b.v));
221*5f39d1b3SJooyung Han }
222*5f39d1b3SJooyung Han
223*5f39d1b3SJooyung Han template <>
224*5f39d1b3SJooyung Han inline v128_t MaskIfLessThan(v128_t a, v128_t b) {
225*5f39d1b3SJooyung Han return wasm_i32x4_lt(a, b);
226*5f39d1b3SJooyung Han }
227*5f39d1b3SJooyung Han
228*5f39d1b3SJooyung Han template <>
229*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfLessThan(int16x8_v128_t a, int16x8_v128_t b) {
230*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_lt(a.v, b.v));
231*5f39d1b3SJooyung Han }
232*5f39d1b3SJooyung Han
233*5f39d1b3SJooyung Han template <>
234*5f39d1b3SJooyung Han inline v128_t MaskIfGreaterThanOrEqual(v128_t a, v128_t b) {
235*5f39d1b3SJooyung Han return wasm_i32x4_ge(a, b);
236*5f39d1b3SJooyung Han }
237*5f39d1b3SJooyung Han
238*5f39d1b3SJooyung Han template <>
239*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfGreaterThanOrEqual(int16x8_v128_t a,
240*5f39d1b3SJooyung Han int16x8_v128_t b) {
241*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_ge(a.v, b.v));
242*5f39d1b3SJooyung Han }
243*5f39d1b3SJooyung Han
244*5f39d1b3SJooyung Han template <>
245*5f39d1b3SJooyung Han inline v128_t MaskIfLessThanOrEqual(v128_t a, v128_t b) {
246*5f39d1b3SJooyung Han return wasm_i32x4_le(a, b);
247*5f39d1b3SJooyung Han }
248*5f39d1b3SJooyung Han
249*5f39d1b3SJooyung Han template <>
250*5f39d1b3SJooyung Han inline int16x8_v128_t MaskIfLessThanOrEqual(int16x8_v128_t a,
251*5f39d1b3SJooyung Han int16x8_v128_t b) {
252*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_le(a.v, b.v));
253*5f39d1b3SJooyung Han }
254*5f39d1b3SJooyung Han
255*5f39d1b3SJooyung Han /* Assumptions:
256*5f39d1b3SJooyung Han - All and Any are used on masks.
257*5f39d1b3SJooyung Han - masks are all_ones for true lanes, all_zeroes otherwise.
258*5f39d1b3SJooyung Han Hence, All means all 128bits set, and Any means any bit set.
259*5f39d1b3SJooyung Han */
260*5f39d1b3SJooyung Han
261*5f39d1b3SJooyung Han template <>
262*5f39d1b3SJooyung Han inline bool All(v128_t a) {
263*5f39d1b3SJooyung Han return wasm_i32x4_all_true(a);
264*5f39d1b3SJooyung Han }
265*5f39d1b3SJooyung Han
266*5f39d1b3SJooyung Han template <>
267*5f39d1b3SJooyung Han inline bool All(int16x8_v128_t a) {
268*5f39d1b3SJooyung Han return wasm_i16x8_all_true(a.v);
269*5f39d1b3SJooyung Han }
270*5f39d1b3SJooyung Han
271*5f39d1b3SJooyung Han template <>
272*5f39d1b3SJooyung Han inline bool Any(v128_t a) {
273*5f39d1b3SJooyung Han return wasm_i32x4_any_true(a);
274*5f39d1b3SJooyung Han }
275*5f39d1b3SJooyung Han
276*5f39d1b3SJooyung Han template <>
277*5f39d1b3SJooyung Han inline bool Any(int16x8_v128_t a) {
278*5f39d1b3SJooyung Han return wasm_i16x8_any_true(a.v);
279*5f39d1b3SJooyung Han }
280*5f39d1b3SJooyung Han
281*5f39d1b3SJooyung Han template <>
282*5f39d1b3SJooyung Han inline v128_t RoundingHalfSum(v128_t a, v128_t b) {
283*5f39d1b3SJooyung Han // We divide the inputs before the add to avoid the overflow and costly test.
284*5f39d1b3SJooyung Han const v128_t one = wasm_i32x4_const(1, 1, 1, 1);
285*5f39d1b3SJooyung Han const v128_t sign_bit_mask =
286*5f39d1b3SJooyung Han wasm_i32x4_const(0x80000000, 0x80000000, 0x80000000, 0x80000000);
287*5f39d1b3SJooyung Han const v128_t sum = Add(a, b);
288*5f39d1b3SJooyung Han const v128_t rounded_half_sum = ShiftRight(Add(sum, one), 1);
289*5f39d1b3SJooyung Han const v128_t overflow =
290*5f39d1b3SJooyung Han BitAnd(BitAnd(BitXor(a, rounded_half_sum), BitXor(b, rounded_half_sum)),
291*5f39d1b3SJooyung Han sign_bit_mask);
292*5f39d1b3SJooyung Han const v128_t result = BitXor(rounded_half_sum, overflow);
293*5f39d1b3SJooyung Han return result;
294*5f39d1b3SJooyung Han }
295*5f39d1b3SJooyung Han
296*5f39d1b3SJooyung Han template <>
297*5f39d1b3SJooyung Han inline int16x8_v128_t RoundingHalfSum(int16x8_v128_t a, int16x8_v128_t b) {
298*5f39d1b3SJooyung Han // Idea: go to unsigned to use wasm_u16x8_avgr,
299*5f39d1b3SJooyung Han // borrowed from Intel's arm_neon_sse.h header.
300*5f39d1b3SJooyung Han const v128_t constant_neg_32768 = wasm_i16x8_const(
301*5f39d1b3SJooyung Han -32768, -32768, -32768, -32768, -32768, -32768, -32768, -32768);
302*5f39d1b3SJooyung Han const v128_t a_unsigned = wasm_v128_xor(a.v, constant_neg_32768);
303*5f39d1b3SJooyung Han const v128_t b_unsigned = wasm_v128_xor(b.v, constant_neg_32768);
304*5f39d1b3SJooyung Han const v128_t avg_unsigned = wasm_u16x8_avgr(a_unsigned, b_unsigned);
305*5f39d1b3SJooyung Han const v128_t avg = wasm_v128_xor(avg_unsigned, constant_neg_32768);
306*5f39d1b3SJooyung Han return to_int16x8_v128_t(avg);
307*5f39d1b3SJooyung Han }
308*5f39d1b3SJooyung Han
309*5f39d1b3SJooyung Han template <>
310*5f39d1b3SJooyung Han inline v128_t SaturatingRoundingDoublingHighMul(v128_t a, v128_t b) {
311*5f39d1b3SJooyung Han // TODO: switch to extended multiplication once implemented in the toolchain
312*5f39d1b3SJooyung Han const v128_t a_sign = wasm_i32x4_shr(a, 31);
313*5f39d1b3SJooyung Han const v128_t b_sign = wasm_i32x4_shr(b, 31);
314*5f39d1b3SJooyung Han
315*5f39d1b3SJooyung Han const v128_t a_ext_lo = wasm_v32x4_shuffle(a, a_sign, 0, 4, 1, 5);
316*5f39d1b3SJooyung Han const v128_t a_ext_hi = wasm_v32x4_shuffle(a, a_sign, 2, 6, 3, 7);
317*5f39d1b3SJooyung Han const v128_t b_ext_lo = wasm_v32x4_shuffle(b, b_sign, 0, 4, 1, 5);
318*5f39d1b3SJooyung Han const v128_t b_ext_hi = wasm_v32x4_shuffle(b, b_sign, 2, 6, 3, 7);
319*5f39d1b3SJooyung Han
320*5f39d1b3SJooyung Han const v128_t ab_lo = wasm_i64x2_mul(a_ext_lo, b_ext_lo);
321*5f39d1b3SJooyung Han const v128_t ab_hi = wasm_i64x2_mul(a_ext_hi, b_ext_hi);
322*5f39d1b3SJooyung Han
323*5f39d1b3SJooyung Han const v128_t nudge_2x =
324*5f39d1b3SJooyung Han wasm_i64x2_const(INT64_C(0x80000000), INT64_C(0x80000000));
325*5f39d1b3SJooyung Han const v128_t ab_lo_2x = wasm_i64x2_add(ab_lo, ab_lo);
326*5f39d1b3SJooyung Han const v128_t ab_hi_2x = wasm_i64x2_add(ab_hi, ab_hi);
327*5f39d1b3SJooyung Han
328*5f39d1b3SJooyung Han const v128_t ab_lo_rounded_2x = wasm_i64x2_add(ab_lo_2x, nudge_2x);
329*5f39d1b3SJooyung Han const v128_t ab_hi_rounded_2x = wasm_i64x2_add(ab_hi_2x, nudge_2x);
330*5f39d1b3SJooyung Han
331*5f39d1b3SJooyung Han const v128_t prod =
332*5f39d1b3SJooyung Han wasm_v32x4_shuffle(ab_lo_rounded_2x, ab_hi_rounded_2x, 1, 3, 5, 7);
333*5f39d1b3SJooyung Han
334*5f39d1b3SJooyung Han // Saturation only happen if a == b == INT_MIN, and this is the only case
335*5f39d1b3SJooyung Han // where prod == INT_MIN (0x80000000) instead of INT_MAX (0x7FFFFFFF).
336*5f39d1b3SJooyung Han const v128_t min = wasm_i32x4_const(INT32_C(0x80000000), INT32_C(0x80000000),
337*5f39d1b3SJooyung Han INT32_C(0x80000000), INT32_C(0x80000000));
338*5f39d1b3SJooyung Han
339*5f39d1b3SJooyung Han return wasm_v128_xor(prod, wasm_i32x4_eq(prod, min));
340*5f39d1b3SJooyung Han }
341*5f39d1b3SJooyung Han
342*5f39d1b3SJooyung Han template <>
343*5f39d1b3SJooyung Han inline int16x8_v128_t SaturatingRoundingDoublingHighMul(int16x8_v128_t a,
344*5f39d1b3SJooyung Han int16x8_v128_t b) {
345*5f39d1b3SJooyung Han #if 0
346*5f39d1b3SJooyung Han // TODO: enable if https://github.com/WebAssembly/simd/pull/365 is accepted
347*5f39d1b3SJooyung Han return to_int16x8_v128_t(__builtin_wasm_q15mulr_saturate_s_i16x8(a.v, b.v));
348*5f39d1b3SJooyung Han #else
349*5f39d1b3SJooyung Han // TODO: switch to extended multiplication once implemented in the toolchain
350*5f39d1b3SJooyung Han v128_t lo = wasm_i32x4_mul(wasm_i32x4_widen_low_i16x8(a.v),
351*5f39d1b3SJooyung Han wasm_i32x4_widen_low_i16x8(b.v));
352*5f39d1b3SJooyung Han v128_t hi = wasm_i32x4_mul(wasm_i32x4_widen_high_i16x8(a.v),
353*5f39d1b3SJooyung Han wasm_i32x4_widen_high_i16x8(b.v));
354*5f39d1b3SJooyung Han const v128_t inc = wasm_i32x4_const(0x4000, 0x4000, 0x4000, 0x4000);
355*5f39d1b3SJooyung Han lo = wasm_i32x4_add(lo, inc);
356*5f39d1b3SJooyung Han hi = wasm_i32x4_add(hi, inc);
357*5f39d1b3SJooyung Han lo = wasm_i32x4_shr(lo, 15);
358*5f39d1b3SJooyung Han hi = wasm_i32x4_shr(hi, 15);
359*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_narrow_i32x4(lo, hi));
360*5f39d1b3SJooyung Han #endif
361*5f39d1b3SJooyung Han }
362*5f39d1b3SJooyung Han
363*5f39d1b3SJooyung Han template <>
364*5f39d1b3SJooyung Han inline v128_t Dup<v128_t>(std::int32_t x) {
365*5f39d1b3SJooyung Han return wasm_i32x4_splat(x);
366*5f39d1b3SJooyung Han }
367*5f39d1b3SJooyung Han
368*5f39d1b3SJooyung Han template <>
369*5f39d1b3SJooyung Han inline int16x8_v128_t Dup<int16x8_v128_t>(std::int16_t x) {
370*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_splat(x));
371*5f39d1b3SJooyung Han }
372*5f39d1b3SJooyung Han
373*5f39d1b3SJooyung Han // So far this is only needed for int16.
374*5f39d1b3SJooyung Han template <>
375*5f39d1b3SJooyung Han inline int16x8_v128_t SaturatingAdd(int16x8_v128_t a, int16x8_v128_t b) {
376*5f39d1b3SJooyung Han return to_int16x8_v128_t(wasm_i16x8_add_saturate(a.v, b.v));
377*5f39d1b3SJooyung Han }
378*5f39d1b3SJooyung Han
379*5f39d1b3SJooyung Han } // end namespace gemmlowp
380*5f39d1b3SJooyung Han
381*5f39d1b3SJooyung Han #endif // GEMMLOWP_INTERNAL_FIXEDPOINT_WASMSIMD_H_
382