Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include <type_traits>
6 :
7 : #include "src/assembler-inl.h"
8 : #include "src/base/bits.h"
9 : #include "src/base/overflowing-math.h"
10 : #include "test/cctest/cctest.h"
11 : #include "test/cctest/compiler/value-helper.h"
12 : #include "test/cctest/wasm/wasm-run-utils.h"
13 : #include "test/common/wasm/wasm-macro-gen.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 : namespace wasm {
18 : namespace test_run_wasm_simd {
19 :
20 : namespace {
21 :
22 : typedef float (*FloatUnOp)(float);
23 : typedef float (*FloatBinOp)(float, float);
24 : typedef int (*FloatCompareOp)(float, float);
25 : typedef int32_t (*Int32UnOp)(int32_t);
26 : typedef int32_t (*Int32BinOp)(int32_t, int32_t);
27 : typedef int (*Int32CompareOp)(int32_t, int32_t);
28 : typedef int32_t (*Int32ShiftOp)(int32_t, int);
29 : typedef int16_t (*Int16UnOp)(int16_t);
30 : typedef int16_t (*Int16BinOp)(int16_t, int16_t);
31 : typedef int (*Int16CompareOp)(int16_t, int16_t);
32 : typedef int16_t (*Int16ShiftOp)(int16_t, int);
33 : typedef int8_t (*Int8UnOp)(int8_t);
34 : typedef int8_t (*Int8BinOp)(int8_t, int8_t);
35 : typedef int (*Int8CompareOp)(int8_t, int8_t);
36 : typedef int8_t (*Int8ShiftOp)(int8_t, int);
37 :
38 : #define WASM_SIMD_TEST(name) \
39 : void RunWasm_##name##_Impl(LowerSimd lower_simd, \
40 : ExecutionTier execution_tier); \
41 : TEST(RunWasm_##name##_turbofan) { \
42 : EXPERIMENTAL_FLAG_SCOPE(simd); \
43 : RunWasm_##name##_Impl(kNoLowerSimd, ExecutionTier::kOptimized); \
44 : } \
45 : TEST(RunWasm_##name##_interpreter) { \
46 : EXPERIMENTAL_FLAG_SCOPE(simd); \
47 : RunWasm_##name##_Impl(kNoLowerSimd, ExecutionTier::kInterpreter); \
48 : } \
49 : TEST(RunWasm_##name##_simd_lowered) { \
50 : EXPERIMENTAL_FLAG_SCOPE(simd); \
51 : RunWasm_##name##_Impl(kLowerSimd, ExecutionTier::kOptimized); \
52 : } \
53 : void RunWasm_##name##_Impl(LowerSimd lower_simd, ExecutionTier execution_tier)
54 :
55 : // Generic expected value functions.
56 : template <typename T, typename = typename std::enable_if<
57 : std::is_floating_point<T>::value>::type>
58 1380 : T Negate(T a) {
59 1380 : return -a;
60 : }
61 :
62 : // For signed integral types, use base::AddWithWraparound.
63 : template <typename T, typename = typename std::enable_if<
64 : std::is_floating_point<T>::value>::type>
65 158700 : T Add(T a, T b) {
66 158700 : return a + b;
67 : }
68 :
69 : // For signed integral types, use base::SubWithWraparound.
70 : template <typename T, typename = typename std::enable_if<
71 : std::is_floating_point<T>::value>::type>
72 158700 : T Sub(T a, T b) {
73 158700 : return a - b;
74 : }
75 :
76 : // For signed integral types, use base::MulWithWraparound.
77 : template <typename T, typename = typename std::enable_if<
78 : std::is_floating_point<T>::value>::type>
79 158700 : T Mul(T a, T b) {
80 158700 : return a * b;
81 : }
82 :
83 : template <typename T>
84 42312 : T Minimum(T a, T b) {
85 42312 : return a <= b ? a : b;
86 : }
87 :
88 : template <typename T>
89 42312 : T Maximum(T a, T b) {
90 42312 : return a >= b ? a : b;
91 : }
92 :
93 : template <typename T>
94 42312 : T UnsignedMinimum(T a, T b) {
95 : using UnsignedT = typename std::make_unsigned<T>::type;
96 42312 : return static_cast<UnsignedT>(a) <= static_cast<UnsignedT>(b) ? a : b;
97 : }
98 :
99 : template <typename T>
100 42312 : T UnsignedMaximum(T a, T b) {
101 : using UnsignedT = typename std::make_unsigned<T>::type;
102 42312 : return static_cast<UnsignedT>(a) >= static_cast<UnsignedT>(b) ? a : b;
103 : }
104 :
105 158700 : int Equal(float a, float b) { return a == b ? -1 : 0; }
106 :
107 : template <typename T>
108 42312 : T Equal(T a, T b) {
109 42312 : return a == b ? -1 : 0;
110 : }
111 :
112 158700 : int NotEqual(float a, float b) { return a != b ? -1 : 0; }
113 :
114 : template <typename T>
115 42312 : T NotEqual(T a, T b) {
116 42312 : return a != b ? -1 : 0;
117 : }
118 :
119 158700 : int Less(float a, float b) { return a < b ? -1 : 0; }
120 :
121 : template <typename T>
122 42312 : T Less(T a, T b) {
123 42312 : return a < b ? -1 : 0;
124 : }
125 :
126 158700 : int LessEqual(float a, float b) { return a <= b ? -1 : 0; }
127 :
128 : template <typename T>
129 42312 : T LessEqual(T a, T b) {
130 42312 : return a <= b ? -1 : 0;
131 : }
132 :
133 158700 : int Greater(float a, float b) { return a > b ? -1 : 0; }
134 :
135 : template <typename T>
136 42312 : T Greater(T a, T b) {
137 42312 : return a > b ? -1 : 0;
138 : }
139 :
140 158700 : int GreaterEqual(float a, float b) { return a >= b ? -1 : 0; }
141 :
142 : template <typename T>
143 42312 : T GreaterEqual(T a, T b) {
144 42312 : return a >= b ? -1 : 0;
145 : }
146 :
147 : template <typename T>
148 42312 : T UnsignedLess(T a, T b) {
149 : using UnsignedT = typename std::make_unsigned<T>::type;
150 42312 : return static_cast<UnsignedT>(a) < static_cast<UnsignedT>(b) ? -1 : 0;
151 : }
152 :
153 : template <typename T>
154 42312 : T UnsignedLessEqual(T a, T b) {
155 : using UnsignedT = typename std::make_unsigned<T>::type;
156 42312 : return static_cast<UnsignedT>(a) <= static_cast<UnsignedT>(b) ? -1 : 0;
157 : }
158 :
159 : template <typename T>
160 42312 : T UnsignedGreater(T a, T b) {
161 : using UnsignedT = typename std::make_unsigned<T>::type;
162 42312 : return static_cast<UnsignedT>(a) > static_cast<UnsignedT>(b) ? -1 : 0;
163 : }
164 :
165 : template <typename T>
166 42312 : T UnsignedGreaterEqual(T a, T b) {
167 : using UnsignedT = typename std::make_unsigned<T>::type;
168 42312 : return static_cast<UnsignedT>(a) >= static_cast<UnsignedT>(b) ? -1 : 0;
169 : }
170 :
171 : template <typename T>
172 23952 : T LogicalShiftLeft(T a, int shift) {
173 : using UnsignedT = typename std::make_unsigned<T>::type;
174 23952 : return static_cast<UnsignedT>(a) << shift;
175 : }
176 :
177 : template <typename T>
178 23952 : T LogicalShiftRight(T a, int shift) {
179 : using UnsignedT = typename std::make_unsigned<T>::type;
180 23952 : return static_cast<UnsignedT>(a) >> shift;
181 : }
182 :
183 : template <typename T>
184 : T Clamp(int64_t value) {
185 : static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller");
186 9384 : int64_t min = static_cast<int64_t>(std::numeric_limits<T>::min());
187 9384 : int64_t max = static_cast<int64_t>(std::numeric_limits<T>::max());
188 9384 : int64_t clamped = std::max(min, std::min(max, value));
189 4692 : return static_cast<T>(clamped);
190 : }
191 :
192 : template <typename T>
193 : int64_t Widen(T value) {
194 : static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller");
195 3888 : return static_cast<int64_t>(value);
196 : }
197 :
198 : template <typename T>
199 : int64_t UnsignedWiden(T value) {
200 : static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller");
201 : using UnsignedT = typename std::make_unsigned<T>::type;
202 4104 : return static_cast<int64_t>(static_cast<UnsignedT>(value));
203 : }
204 :
205 : template <typename T>
206 : T Narrow(int64_t value) {
207 : return Clamp<T>(value);
208 : }
209 :
210 : template <typename T>
211 : T UnsignedNarrow(int64_t value) {
212 : static_assert(sizeof(int64_t) > sizeof(T), "T must be int32_t or smaller");
213 : using UnsignedT = typename std::make_unsigned<T>::type;
214 1608 : return static_cast<T>(Clamp<UnsignedT>(value & 0xFFFFFFFFu));
215 : }
216 :
217 : template <typename T>
218 1944 : T AddSaturate(T a, T b) {
219 3888 : return Clamp<T>(Widen(a) + Widen(b));
220 : }
221 :
222 : template <typename T>
223 1944 : T SubSaturate(T a, T b) {
224 3888 : return Clamp<T>(Widen(a) - Widen(b));
225 : }
226 :
227 : template <typename T>
228 1944 : T UnsignedAddSaturate(T a, T b) {
229 : using UnsignedT = typename std::make_unsigned<T>::type;
230 3888 : return Clamp<UnsignedT>(UnsignedWiden(a) + UnsignedWiden(b));
231 : }
232 :
233 : template <typename T>
234 1944 : T UnsignedSubSaturate(T a, T b) {
235 : using UnsignedT = typename std::make_unsigned<T>::type;
236 3888 : return Clamp<UnsignedT>(UnsignedWiden(a) - UnsignedWiden(b));
237 : }
238 :
239 : template <typename T>
240 40368 : T And(T a, T b) {
241 40368 : return a & b;
242 : }
243 :
244 : template <typename T>
245 40368 : T Or(T a, T b) {
246 40368 : return a | b;
247 : }
248 :
249 : template <typename T>
250 40368 : T Xor(T a, T b) {
251 40368 : return a ^ b;
252 : }
253 :
254 : template <typename T>
255 696 : T Not(T a) {
256 696 : return ~a;
257 : }
258 :
259 : template <typename T>
260 : T LogicalNot(T a) {
261 : return a == 0 ? -1 : 0;
262 : }
263 :
264 : template <typename T>
265 : T Sqrt(T a) {
266 : return std::sqrt(a);
267 : }
268 :
269 : } // namespace
270 :
271 : #define WASM_SIMD_CHECK_LANE(TYPE, value, LANE_TYPE, lane_value, lane_index) \
272 : WASM_IF(WASM_##LANE_TYPE##_NE(WASM_GET_LOCAL(lane_value), \
273 : WASM_SIMD_##TYPE##_EXTRACT_LANE( \
274 : lane_index, WASM_GET_LOCAL(value))), \
275 : WASM_RETURN1(WASM_ZERO))
276 :
277 : #define TO_BYTE(val) static_cast<byte>(val)
278 : #define WASM_SIMD_OP(op) kSimdPrefix, TO_BYTE(op)
279 : #define WASM_SIMD_SPLAT(Type, x) x, WASM_SIMD_OP(kExpr##Type##Splat)
280 : #define WASM_SIMD_UNOP(op, x) x, WASM_SIMD_OP(op)
281 : #define WASM_SIMD_BINOP(op, x, y) x, y, WASM_SIMD_OP(op)
282 : #define WASM_SIMD_SHIFT_OP(op, shift, x) x, WASM_SIMD_OP(op), TO_BYTE(shift)
283 : #define WASM_SIMD_CONCAT_OP(op, bytes, x, y) \
284 : x, y, WASM_SIMD_OP(op), TO_BYTE(bytes)
285 : #define WASM_SIMD_SELECT(format, x, y, z) x, y, z, WASM_SIMD_OP(kExprS128Select)
286 : #define WASM_SIMD_F32x4_SPLAT(x) x, WASM_SIMD_OP(kExprF32x4Splat)
287 : #define WASM_SIMD_F32x4_EXTRACT_LANE(lane, x) \
288 : x, WASM_SIMD_OP(kExprF32x4ExtractLane), TO_BYTE(lane)
289 : #define WASM_SIMD_F32x4_REPLACE_LANE(lane, x, y) \
290 : x, y, WASM_SIMD_OP(kExprF32x4ReplaceLane), TO_BYTE(lane)
291 :
292 : #define WASM_SIMD_I32x4_SPLAT(x) x, WASM_SIMD_OP(kExprI32x4Splat)
293 : #define WASM_SIMD_I32x4_EXTRACT_LANE(lane, x) \
294 : x, WASM_SIMD_OP(kExprI32x4ExtractLane), TO_BYTE(lane)
295 : #define WASM_SIMD_I32x4_REPLACE_LANE(lane, x, y) \
296 : x, y, WASM_SIMD_OP(kExprI32x4ReplaceLane), TO_BYTE(lane)
297 :
298 : #define WASM_SIMD_I16x8_SPLAT(x) x, WASM_SIMD_OP(kExprI16x8Splat)
299 : #define WASM_SIMD_I16x8_EXTRACT_LANE(lane, x) \
300 : x, WASM_SIMD_OP(kExprI16x8ExtractLane), TO_BYTE(lane)
301 : #define WASM_SIMD_I16x8_REPLACE_LANE(lane, x, y) \
302 : x, y, WASM_SIMD_OP(kExprI16x8ReplaceLane), TO_BYTE(lane)
303 :
304 : #define WASM_SIMD_I8x16_SPLAT(x) x, WASM_SIMD_OP(kExprI8x16Splat)
305 : #define WASM_SIMD_I8x16_EXTRACT_LANE(lane, x) \
306 : x, WASM_SIMD_OP(kExprI8x16ExtractLane), TO_BYTE(lane)
307 : #define WASM_SIMD_I8x16_REPLACE_LANE(lane, x, y) \
308 : x, y, WASM_SIMD_OP(kExprI8x16ReplaceLane), TO_BYTE(lane)
309 :
310 : #define WASM_SIMD_S8x16_SHUFFLE_OP(opcode, m, x, y) \
311 : x, y, WASM_SIMD_OP(opcode), TO_BYTE(m[0]), TO_BYTE(m[1]), TO_BYTE(m[2]), \
312 : TO_BYTE(m[3]), TO_BYTE(m[4]), TO_BYTE(m[5]), TO_BYTE(m[6]), \
313 : TO_BYTE(m[7]), TO_BYTE(m[8]), TO_BYTE(m[9]), TO_BYTE(m[10]), \
314 : TO_BYTE(m[11]), TO_BYTE(m[12]), TO_BYTE(m[13]), TO_BYTE(m[14]), \
315 : TO_BYTE(m[15])
316 :
317 : #define WASM_SIMD_LOAD_MEM(index) \
318 : index, WASM_SIMD_OP(kExprS128LoadMem), ZERO_ALIGNMENT, ZERO_OFFSET
319 : #define WASM_SIMD_STORE_MEM(index, val) \
320 : index, val, WASM_SIMD_OP(kExprS128StoreMem), ZERO_ALIGNMENT, ZERO_OFFSET
321 :
322 : // Runs tests of compiled code, using the interpreter as a reference.
323 : #define WASM_SIMD_COMPILED_TEST(name) \
324 : void RunWasm_##name##_Impl(LowerSimd lower_simd, \
325 : ExecutionTier execution_tier); \
326 : TEST(RunWasm_##name##_turbofan) { \
327 : EXPERIMENTAL_FLAG_SCOPE(simd); \
328 : RunWasm_##name##_Impl(kNoLowerSimd, ExecutionTier::kOptimized); \
329 : } \
330 : TEST(RunWasm_##name##_simd_lowered) { \
331 : EXPERIMENTAL_FLAG_SCOPE(simd); \
332 : RunWasm_##name##_Impl(kLowerSimd, ExecutionTier::kOptimized); \
333 : } \
334 : void RunWasm_##name##_Impl(LowerSimd lower_simd, ExecutionTier execution_tier)
335 :
336 : // The macro below disables tests lowering for certain nodes where the simd
337 : // lowering doesn't work correctly. Early return here if the CPU does not
338 : // support SIMD as the graph will be implicitly lowered in that case.
339 : #define WASM_SIMD_TEST_TURBOFAN(name) \
340 : void RunWasm_##name##_Impl(LowerSimd lower_simd, \
341 : ExecutionTier execution_tier); \
342 : TEST(RunWasm_##name##_turbofan) { \
343 : if (!CpuFeatures::SupportsWasmSimd128()) return; \
344 : EXPERIMENTAL_FLAG_SCOPE(simd); \
345 : RunWasm_##name##_Impl(kNoLowerSimd, ExecutionTier::kOptimized); \
346 : } \
347 : void RunWasm_##name##_Impl(LowerSimd lower_simd, ExecutionTier execution_tier)
348 :
349 : // Returns true if the platform can represent the result.
350 0 : bool PlatformCanRepresent(float x) {
351 : #if V8_TARGET_ARCH_ARM
352 : return std::fpclassify(x) != FP_SUBNORMAL;
353 : #else
354 0 : return true;
355 : #endif
356 : }
357 :
358 : // Returns true for very small and very large numbers. We skip these test
359 : // values for the approximation instructions, which don't work at the extremes.
360 0 : bool IsExtreme(float x) {
361 : float abs_x = std::fabs(x);
362 : const float kSmallFloatThreshold = 1.0e-32f;
363 : const float kLargeFloatThreshold = 1.0e32f;
364 2760 : return abs_x != 0.0f && // 0 or -0 are fine.
365 2616 : (abs_x < kSmallFloatThreshold || abs_x > kLargeFloatThreshold);
366 : }
367 :
368 26111 : WASM_SIMD_TEST(F32x4Splat) {
369 12 : WasmRunner<int32_t, float> r(execution_tier, lower_simd);
370 : // Set up a global to hold output vector.
371 : float* g = r.builder().AddGlobal<float>(kWasmS128);
372 : byte param1 = 0;
373 12 : BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(param1))),
374 : WASM_ONE);
375 :
376 2772 : FOR_FLOAT32_INPUTS(x) {
377 1380 : r.Call(x);
378 : float expected = x;
379 12420 : for (int i = 0; i < 4; i++) {
380 5520 : float actual = ReadLittleEndianValue<float>(&g[i]);
381 5520 : if (std::isnan(expected)) {
382 96 : CHECK(std::isnan(actual));
383 : } else {
384 5424 : CHECK_EQ(actual, expected);
385 : }
386 : }
387 : }
388 12 : }
389 :
390 26111 : WASM_SIMD_TEST(F32x4ReplaceLane) {
391 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
392 : // Set up a global to hold input/output vector.
393 : float* g = r.builder().AddGlobal<float>(kWasmS128);
394 : // Build function to replace each lane with its (FP) index.
395 : byte temp1 = r.AllocateLocal(kWasmS128);
396 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_SPLAT(WASM_F32(3.14159f))),
397 : WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_REPLACE_LANE(
398 : 0, WASM_GET_LOCAL(temp1), WASM_F32(0.0f))),
399 : WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_REPLACE_LANE(
400 : 1, WASM_GET_LOCAL(temp1), WASM_F32(1.0f))),
401 : WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_REPLACE_LANE(
402 : 2, WASM_GET_LOCAL(temp1), WASM_F32(2.0f))),
403 : WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_REPLACE_LANE(
404 : 3, WASM_GET_LOCAL(temp1), WASM_F32(3.0f))),
405 : WASM_ONE);
406 :
407 12 : r.Call();
408 108 : for (int i = 0; i < 4; i++) {
409 48 : CHECK_EQ(static_cast<float>(i), ReadLittleEndianValue<float>(&g[i]));
410 : }
411 12 : }
412 :
413 : // Tests both signed and unsigned conversion.
414 : // v8:8425 tracks this test being enabled in the interpreter.
415 26095 : WASM_SIMD_COMPILED_TEST(F32x4ConvertI32x4) {
416 8 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
417 : // Create two output vectors to hold signed and unsigned results.
418 : float* g0 = r.builder().AddGlobal<float>(kWasmS128);
419 : float* g1 = r.builder().AddGlobal<float>(kWasmS128);
420 : // Build fn to splat test value, perform conversions, and write the results.
421 : byte value = 0;
422 : byte temp1 = r.AllocateLocal(kWasmS128);
423 8 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value))),
424 : WASM_SET_GLOBAL(
425 : 0, WASM_SIMD_UNOP(kExprF32x4SConvertI32x4, WASM_GET_LOCAL(temp1))),
426 : WASM_SET_GLOBAL(
427 : 1, WASM_SIMD_UNOP(kExprF32x4UConvertI32x4, WASM_GET_LOCAL(temp1))),
428 : WASM_ONE);
429 :
430 936 : FOR_INT32_INPUTS(x) {
431 464 : r.Call(x);
432 464 : float expected_signed = static_cast<float>(x);
433 464 : float expected_unsigned = static_cast<float>(static_cast<uint32_t>(x));
434 4176 : for (int i = 0; i < 4; i++) {
435 1856 : CHECK_EQ(expected_signed, ReadLittleEndianValue<float>(&g0[i]));
436 1856 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<float>(&g1[i]));
437 : }
438 : }
439 8 : }
440 :
441 48 : void RunF32x4UnOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
442 : WasmOpcode opcode, FloatUnOp expected_op,
443 : bool approximate = false) {
444 48 : WasmRunner<int32_t, float> r(execution_tier, lower_simd);
445 : // Global to hold output.
446 : float* g = r.builder().AddGlobal<float>(kWasmS128);
447 : // Build fn to splat test value, perform unop, and write the result.
448 : byte value = 0;
449 : byte temp1 = r.AllocateLocal(kWasmS128);
450 48 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value))),
451 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(opcode, WASM_GET_LOCAL(temp1))),
452 : WASM_ONE);
453 :
454 11088 : FOR_FLOAT32_INPUTS(x) {
455 : if (!PlatformCanRepresent(x)) continue;
456 : // Extreme values have larger errors so skip them for approximation tests.
457 8280 : if (approximate && IsExtreme(x)) continue;
458 5088 : float expected = expected_op(x);
459 : if (!PlatformCanRepresent(expected)) continue;
460 5088 : r.Call(x);
461 45792 : for (int i = 0; i < 4; i++) {
462 20352 : float actual = ReadLittleEndianValue<float>(&g[i]);
463 20352 : if (std::isnan(expected)) {
464 2928 : CHECK(std::isnan(actual));
465 : } else {
466 : // First check for equality, to handle +/-Inf, since min and max would
467 : // be NaNs in those cases.
468 17424 : if (expected == actual) continue;
469 : // 1% error allows all platforms to pass easily.
470 : constexpr float kApproximationError = 0.01f;
471 2128 : float abs_error = std::abs(expected) * kApproximationError,
472 2128 : min = expected - abs_error, max = expected + abs_error;
473 2128 : CHECK_LE(min, actual);
474 2128 : CHECK_GE(max, actual);
475 : }
476 : }
477 : }
478 48 : }
479 :
480 26087 : WASM_SIMD_TEST(F32x4Abs) {
481 12 : RunF32x4UnOpTest(execution_tier, lower_simd, kExprF32x4Abs, std::abs);
482 0 : }
483 26087 : WASM_SIMD_TEST(F32x4Neg) {
484 12 : RunF32x4UnOpTest(execution_tier, lower_simd, kExprF32x4Neg, Negate);
485 0 : }
486 :
487 26087 : WASM_SIMD_TEST(F32x4RecipApprox) {
488 0 : RunF32x4UnOpTest(execution_tier, lower_simd, kExprF32x4RecipApprox,
489 12 : base::Recip, true /* approximate */);
490 0 : }
491 :
492 26087 : WASM_SIMD_TEST(F32x4RecipSqrtApprox) {
493 0 : RunF32x4UnOpTest(execution_tier, lower_simd, kExprF32x4RecipSqrtApprox,
494 12 : base::RecipSqrt, true /* approximate */);
495 0 : }
496 :
497 52 : void RunF32x4BinOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
498 : WasmOpcode opcode, FloatBinOp expected_op) {
499 52 : WasmRunner<int32_t, float, float> r(execution_tier, lower_simd);
500 : // Global to hold output.
501 : float* g = r.builder().AddGlobal<float>(kWasmS128);
502 : // Build fn to splat test values, perform binop, and write the result.
503 : byte value1 = 0, value2 = 1;
504 : byte temp1 = r.AllocateLocal(kWasmS128);
505 : byte temp2 = r.AllocateLocal(kWasmS128);
506 52 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value1))),
507 : WASM_SET_LOCAL(temp2, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value2))),
508 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(opcode, WASM_GET_LOCAL(temp1),
509 : WASM_GET_LOCAL(temp2))),
510 : WASM_ONE);
511 :
512 12012 : FOR_FLOAT32_INPUTS(x) {
513 : if (!PlatformCanRepresent(x)) continue;
514 1381380 : FOR_FLOAT32_INPUTS(y) {
515 : if (!PlatformCanRepresent(y)) continue;
516 687700 : float expected = expected_op(x, y);
517 : if (!PlatformCanRepresent(expected)) continue;
518 687700 : r.Call(x, y);
519 6189300 : for (int i = 0; i < 4; i++) {
520 2750800 : float actual = ReadLittleEndianValue<float>(&g[i]);
521 2750800 : if (std::isnan(expected)) {
522 95424 : CHECK(std::isnan(actual));
523 : } else {
524 2655376 : CHECK_EQ(expected, actual);
525 : }
526 : }
527 : }
528 : }
529 52 : }
530 :
531 26087 : WASM_SIMD_TEST(F32x4Add) {
532 12 : RunF32x4BinOpTest(execution_tier, lower_simd, kExprF32x4Add, Add);
533 0 : }
534 26087 : WASM_SIMD_TEST(F32x4Sub) {
535 12 : RunF32x4BinOpTest(execution_tier, lower_simd, kExprF32x4Sub, Sub);
536 0 : }
537 26087 : WASM_SIMD_TEST(F32x4Mul) {
538 12 : RunF32x4BinOpTest(execution_tier, lower_simd, kExprF32x4Mul, Mul);
539 0 : }
540 : // v8:8425 tracks this test being enabled in the interpreter.
541 26079 : WASM_SIMD_COMPILED_TEST(F32x4Min) {
542 8 : RunF32x4BinOpTest(execution_tier, lower_simd, kExprF32x4Min, JSMin);
543 0 : }
544 : // v8:8425 tracks this test being enabled in the interpreter.
545 26079 : WASM_SIMD_COMPILED_TEST(F32x4Max) {
546 8 : RunF32x4BinOpTest(execution_tier, lower_simd, kExprF32x4Max, JSMax);
547 0 : }
548 :
549 72 : void RunF32x4CompareOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
550 : WasmOpcode opcode, FloatCompareOp expected_op) {
551 72 : WasmRunner<int32_t, float, float> r(execution_tier, lower_simd);
552 : // Set up global to hold mask output.
553 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
554 : // Build fn to splat test values, perform compare op, and write the result.
555 : byte value1 = 0, value2 = 1;
556 : byte temp1 = r.AllocateLocal(kWasmS128);
557 : byte temp2 = r.AllocateLocal(kWasmS128);
558 72 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value1))),
559 : WASM_SET_LOCAL(temp2, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value2))),
560 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(opcode, WASM_GET_LOCAL(temp1),
561 : WASM_GET_LOCAL(temp2))),
562 : WASM_ONE);
563 :
564 16632 : FOR_FLOAT32_INPUTS(x) {
565 : if (!PlatformCanRepresent(x)) continue;
566 1912680 : FOR_FLOAT32_INPUTS(y) {
567 : if (!PlatformCanRepresent(y)) continue;
568 : float diff = x - y; // Model comparison as subtraction.
569 : if (!PlatformCanRepresent(diff)) continue;
570 952200 : r.Call(x, y);
571 952200 : int32_t expected = expected_op(x, y);
572 8569800 : for (int i = 0; i < 4; i++) {
573 3808800 : CHECK_EQ(expected, ReadLittleEndianValue<int32_t>(&g[i]));
574 : }
575 : }
576 : }
577 72 : }
578 :
579 26087 : WASM_SIMD_TEST(F32x4Eq) {
580 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Eq, Equal);
581 0 : }
582 :
583 26087 : WASM_SIMD_TEST(F32x4Ne) {
584 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Ne, NotEqual);
585 0 : }
586 :
587 26087 : WASM_SIMD_TEST(F32x4Gt) {
588 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Gt, Greater);
589 0 : }
590 :
591 26087 : WASM_SIMD_TEST(F32x4Ge) {
592 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Ge, GreaterEqual);
593 0 : }
594 :
595 26087 : WASM_SIMD_TEST(F32x4Lt) {
596 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Lt, Less);
597 0 : }
598 :
599 26087 : WASM_SIMD_TEST(F32x4Le) {
600 12 : RunF32x4CompareOpTest(execution_tier, lower_simd, kExprF32x4Le, LessEqual);
601 0 : }
602 :
603 26111 : WASM_SIMD_TEST(I32x4Splat) {
604 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
605 : // Set up a global to hold output vector.
606 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
607 : byte param1 = 0;
608 12 : BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(param1))),
609 : WASM_ONE);
610 :
611 1404 : FOR_INT32_INPUTS(x) {
612 696 : r.Call(x);
613 : int32_t expected = x;
614 6264 : for (int i = 0; i < 4; i++) {
615 2784 : int32_t actual = ReadLittleEndianValue<int32_t>(&g[i]);
616 2784 : CHECK_EQ(actual, expected);
617 : }
618 : }
619 12 : }
620 :
621 26111 : WASM_SIMD_TEST(I32x4ReplaceLane) {
622 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
623 : // Set up a global to hold input/output vector.
624 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
625 : // Build function to replace each lane with its index.
626 : byte temp1 = r.AllocateLocal(kWasmS128);
627 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_SPLAT(WASM_I32V(-1))),
628 : WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_REPLACE_LANE(
629 : 0, WASM_GET_LOCAL(temp1), WASM_I32V(0))),
630 : WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_REPLACE_LANE(
631 : 1, WASM_GET_LOCAL(temp1), WASM_I32V(1))),
632 : WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_REPLACE_LANE(
633 : 2, WASM_GET_LOCAL(temp1), WASM_I32V(2))),
634 : WASM_SET_GLOBAL(0, WASM_SIMD_I32x4_REPLACE_LANE(
635 : 3, WASM_GET_LOCAL(temp1), WASM_I32V(3))),
636 : WASM_ONE);
637 :
638 12 : r.Call();
639 108 : for (int32_t i = 0; i < 4; i++) {
640 48 : CHECK_EQ(i, ReadLittleEndianValue<int32_t>(&g[i]));
641 : }
642 12 : }
643 :
644 26111 : WASM_SIMD_TEST(I16x8Splat) {
645 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
646 : // Set up a global to hold output vector.
647 : int16_t* g = r.builder().AddGlobal<int16_t>(kWasmS128);
648 : byte param1 = 0;
649 12 : BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(param1))),
650 : WASM_ONE);
651 :
652 228 : FOR_INT16_INPUTS(x) {
653 108 : r.Call(x);
654 : int16_t expected = x;
655 1836 : for (int i = 0; i < 8; i++) {
656 864 : int16_t actual = ReadLittleEndianValue<int16_t>(&g[i]);
657 864 : CHECK_EQ(actual, expected);
658 : }
659 : }
660 12 : }
661 :
662 26111 : WASM_SIMD_TEST(I16x8ReplaceLane) {
663 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
664 : // Set up a global to hold input/output vector.
665 : int16_t* g = r.builder().AddGlobal<int16_t>(kWasmS128);
666 : // Build function to replace each lane with its index.
667 : byte temp1 = r.AllocateLocal(kWasmS128);
668 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_SPLAT(WASM_I32V(-1))),
669 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
670 : 0, WASM_GET_LOCAL(temp1), WASM_I32V(0))),
671 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
672 : 1, WASM_GET_LOCAL(temp1), WASM_I32V(1))),
673 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
674 : 2, WASM_GET_LOCAL(temp1), WASM_I32V(2))),
675 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
676 : 3, WASM_GET_LOCAL(temp1), WASM_I32V(3))),
677 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
678 : 4, WASM_GET_LOCAL(temp1), WASM_I32V(4))),
679 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
680 : 5, WASM_GET_LOCAL(temp1), WASM_I32V(5))),
681 : WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_REPLACE_LANE(
682 : 6, WASM_GET_LOCAL(temp1), WASM_I32V(6))),
683 : WASM_SET_GLOBAL(0, WASM_SIMD_I16x8_REPLACE_LANE(
684 : 7, WASM_GET_LOCAL(temp1), WASM_I32V(7))),
685 : WASM_ONE);
686 :
687 12 : r.Call();
688 204 : for (int16_t i = 0; i < 8; i++) {
689 96 : CHECK_EQ(i, ReadLittleEndianValue<int16_t>(&g[i]));
690 : }
691 12 : }
692 :
693 26111 : WASM_SIMD_TEST(I8x16Splat) {
694 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
695 : // Set up a global to hold output vector.
696 : int8_t* g = r.builder().AddGlobal<int8_t>(kWasmS128);
697 : byte param1 = 0;
698 12 : BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(param1))),
699 : WASM_ONE);
700 :
701 228 : FOR_INT8_INPUTS(x) {
702 108 : r.Call(x);
703 : int8_t expected = x;
704 3564 : for (int i = 0; i < 16; i++) {
705 1728 : int8_t actual = ReadLittleEndianValue<int8_t>(&g[i]);
706 1728 : CHECK_EQ(actual, expected);
707 : }
708 : }
709 12 : }
710 :
711 26111 : WASM_SIMD_TEST(I8x16ReplaceLane) {
712 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
713 : // Set up a global to hold input/output vector.
714 : int8_t* g = r.builder().AddGlobal<int8_t>(kWasmS128);
715 : // Build function to replace each lane with its index.
716 : byte temp1 = r.AllocateLocal(kWasmS128);
717 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_SPLAT(WASM_I32V(-1))),
718 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
719 : 0, WASM_GET_LOCAL(temp1), WASM_I32V(0))),
720 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
721 : 1, WASM_GET_LOCAL(temp1), WASM_I32V(1))),
722 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
723 : 2, WASM_GET_LOCAL(temp1), WASM_I32V(2))),
724 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
725 : 3, WASM_GET_LOCAL(temp1), WASM_I32V(3))),
726 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
727 : 4, WASM_GET_LOCAL(temp1), WASM_I32V(4))),
728 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
729 : 5, WASM_GET_LOCAL(temp1), WASM_I32V(5))),
730 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
731 : 6, WASM_GET_LOCAL(temp1), WASM_I32V(6))),
732 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
733 : 7, WASM_GET_LOCAL(temp1), WASM_I32V(7))),
734 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
735 : 8, WASM_GET_LOCAL(temp1), WASM_I32V(8))),
736 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
737 : 9, WASM_GET_LOCAL(temp1), WASM_I32V(9))),
738 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
739 : 10, WASM_GET_LOCAL(temp1), WASM_I32V(10))),
740 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
741 : 11, WASM_GET_LOCAL(temp1), WASM_I32V(11))),
742 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
743 : 12, WASM_GET_LOCAL(temp1), WASM_I32V(12))),
744 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
745 : 13, WASM_GET_LOCAL(temp1), WASM_I32V(13))),
746 : WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_REPLACE_LANE(
747 : 14, WASM_GET_LOCAL(temp1), WASM_I32V(14))),
748 : WASM_SET_GLOBAL(0, WASM_SIMD_I8x16_REPLACE_LANE(
749 : 15, WASM_GET_LOCAL(temp1), WASM_I32V(15))),
750 : WASM_ONE);
751 :
752 12 : r.Call();
753 396 : for (int8_t i = 0; i < 16; i++) {
754 192 : CHECK_EQ(i, ReadLittleEndianValue<int8_t>(&g[i]));
755 : }
756 12 : }
757 :
758 : // Use doubles to ensure exact conversion.
759 0 : int32_t ConvertToInt(double val, bool unsigned_integer) {
760 2760 : if (std::isnan(val)) return 0;
761 0 : if (unsigned_integer) {
762 1356 : if (val < 0) return 0;
763 612 : if (val > kMaxUInt32) return kMaxUInt32;
764 360 : return static_cast<uint32_t>(val);
765 : } else {
766 1356 : if (val < kMinInt) return kMinInt;
767 1116 : if (val > kMaxInt) return kMaxInt;
768 852 : return static_cast<int>(val);
769 : }
770 : }
771 :
772 : // Tests both signed and unsigned conversion.
773 26111 : WASM_SIMD_TEST(I32x4ConvertF32x4) {
774 12 : WasmRunner<int32_t, float> r(execution_tier, lower_simd);
775 : // Create two output vectors to hold signed and unsigned results.
776 : int32_t* g0 = r.builder().AddGlobal<int32_t>(kWasmS128);
777 : int32_t* g1 = r.builder().AddGlobal<int32_t>(kWasmS128);
778 : // Build fn to splat test value, perform conversions, and write the results.
779 : byte value = 0;
780 : byte temp1 = r.AllocateLocal(kWasmS128);
781 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_F32x4_SPLAT(WASM_GET_LOCAL(value))),
782 : WASM_SET_GLOBAL(
783 : 0, WASM_SIMD_UNOP(kExprI32x4SConvertF32x4, WASM_GET_LOCAL(temp1))),
784 : WASM_SET_GLOBAL(
785 : 1, WASM_SIMD_UNOP(kExprI32x4UConvertF32x4, WASM_GET_LOCAL(temp1))),
786 : WASM_ONE);
787 :
788 2772 : FOR_FLOAT32_INPUTS(x) {
789 : if (!PlatformCanRepresent(x)) continue;
790 1380 : r.Call(x);
791 1380 : int32_t expected_signed = ConvertToInt(x, false);
792 : int32_t expected_unsigned = ConvertToInt(x, true);
793 12420 : for (int i = 0; i < 4; i++) {
794 5520 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int32_t>(&g0[i]));
795 5520 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int32_t>(&g1[i]));
796 : }
797 : }
798 12 : }
799 :
800 : // Tests both signed and unsigned conversion from I16x8 (unpacking).
801 26111 : WASM_SIMD_TEST(I32x4ConvertI16x8) {
802 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
803 : // Create four output vectors to hold signed and unsigned results.
804 : int32_t* g0 = r.builder().AddGlobal<int32_t>(kWasmS128);
805 : int32_t* g1 = r.builder().AddGlobal<int32_t>(kWasmS128);
806 : int32_t* g2 = r.builder().AddGlobal<int32_t>(kWasmS128);
807 : int32_t* g3 = r.builder().AddGlobal<int32_t>(kWasmS128);
808 : // Build fn to splat test value, perform conversions, and write the results.
809 : byte value = 0;
810 : byte temp1 = r.AllocateLocal(kWasmS128);
811 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value))),
812 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(kExprI32x4SConvertI16x8High,
813 : WASM_GET_LOCAL(temp1))),
814 : WASM_SET_GLOBAL(1, WASM_SIMD_UNOP(kExprI32x4SConvertI16x8Low,
815 : WASM_GET_LOCAL(temp1))),
816 : WASM_SET_GLOBAL(2, WASM_SIMD_UNOP(kExprI32x4UConvertI16x8High,
817 : WASM_GET_LOCAL(temp1))),
818 : WASM_SET_GLOBAL(3, WASM_SIMD_UNOP(kExprI32x4UConvertI16x8Low,
819 : WASM_GET_LOCAL(temp1))),
820 : WASM_ONE);
821 :
822 228 : FOR_INT16_INPUTS(x) {
823 108 : r.Call(x);
824 : int32_t expected_signed = static_cast<int32_t>(Widen<int16_t>(x));
825 108 : int32_t expected_unsigned = static_cast<int32_t>(UnsignedWiden<int16_t>(x));
826 972 : for (int i = 0; i < 4; i++) {
827 432 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int32_t>(&g0[i]));
828 432 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int32_t>(&g1[i]));
829 432 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int32_t>(&g2[i]));
830 432 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int32_t>(&g3[i]));
831 : }
832 : }
833 12 : }
834 :
835 24 : void RunI32x4UnOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
836 : WasmOpcode opcode, Int32UnOp expected_op) {
837 24 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
838 : // Global to hold output.
839 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
840 : // Build fn to splat test value, perform unop, and write the result.
841 : byte value = 0;
842 : byte temp1 = r.AllocateLocal(kWasmS128);
843 24 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value))),
844 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(opcode, WASM_GET_LOCAL(temp1))),
845 : WASM_ONE);
846 :
847 2808 : FOR_INT32_INPUTS(x) {
848 1392 : r.Call(x);
849 1392 : int32_t expected = expected_op(x);
850 12528 : for (int i = 0; i < 4; i++) {
851 5568 : CHECK_EQ(expected, ReadLittleEndianValue<int32_t>(&g[i]));
852 : }
853 : }
854 24 : }
855 :
856 26087 : WASM_SIMD_TEST(I32x4Neg) {
857 0 : RunI32x4UnOpTest(execution_tier, lower_simd, kExprI32x4Neg,
858 12 : base::NegateWithWraparound);
859 0 : }
860 :
861 26087 : WASM_SIMD_TEST(S128Not) {
862 12 : RunI32x4UnOpTest(execution_tier, lower_simd, kExprS128Not, Not);
863 0 : }
864 :
865 240 : void RunI32x4BinOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
866 : WasmOpcode opcode, Int32BinOp expected_op) {
867 240 : WasmRunner<int32_t, int32_t, int32_t> r(execution_tier, lower_simd);
868 : // Global to hold output.
869 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
870 : // Build fn to splat test values, perform binop, and write the result.
871 : byte value1 = 0, value2 = 1;
872 : byte temp1 = r.AllocateLocal(kWasmS128);
873 : byte temp2 = r.AllocateLocal(kWasmS128);
874 240 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value1))),
875 : WASM_SET_LOCAL(temp2, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value2))),
876 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(opcode, WASM_GET_LOCAL(temp1),
877 : WASM_GET_LOCAL(temp2))),
878 : WASM_ONE);
879 :
880 28080 : FOR_INT32_INPUTS(x) {
881 1628640 : FOR_INT32_INPUTS(y) {
882 807360 : r.Call(x, y);
883 807360 : int32_t expected = expected_op(x, y);
884 7266240 : for (int i = 0; i < 4; i++) {
885 3229440 : CHECK_EQ(expected, ReadLittleEndianValue<int32_t>(&g[i]));
886 : }
887 : }
888 : }
889 240 : }
890 :
891 26087 : WASM_SIMD_TEST(I32x4Add) {
892 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4Add,
893 12 : base::AddWithWraparound);
894 0 : }
895 :
896 26087 : WASM_SIMD_TEST(I32x4Sub) {
897 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4Sub,
898 12 : base::SubWithWraparound);
899 0 : }
900 :
901 26087 : WASM_SIMD_TEST(I32x4Mul) {
902 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4Mul,
903 12 : base::MulWithWraparound);
904 0 : }
905 :
906 26087 : WASM_SIMD_TEST(I32x4MinS) {
907 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4MinS, Minimum);
908 0 : }
909 :
910 26087 : WASM_SIMD_TEST(I32x4MaxS) {
911 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4MaxS, Maximum);
912 0 : }
913 :
914 26087 : WASM_SIMD_TEST(I32x4MinU) {
915 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4MinU,
916 12 : UnsignedMinimum);
917 0 : }
918 26087 : WASM_SIMD_TEST(I32x4MaxU) {
919 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4MaxU,
920 :
921 12 : UnsignedMaximum);
922 0 : }
923 :
924 26087 : WASM_SIMD_TEST(S128And) {
925 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprS128And, And);
926 0 : }
927 :
928 26087 : WASM_SIMD_TEST(S128Or) {
929 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprS128Or, Or);
930 0 : }
931 :
932 26087 : WASM_SIMD_TEST(S128Xor) {
933 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprS128Xor, Xor);
934 0 : }
935 :
936 26087 : WASM_SIMD_TEST(I32x4Eq) {
937 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4Eq, Equal);
938 0 : }
939 :
940 26087 : WASM_SIMD_TEST(I32x4Ne) {
941 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4Ne, NotEqual);
942 0 : }
943 :
944 26087 : WASM_SIMD_TEST(I32x4LtS) {
945 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4LtS, Less);
946 0 : }
947 :
948 26087 : WASM_SIMD_TEST(I32x4LeS) {
949 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4LeS, LessEqual);
950 0 : }
951 :
952 26087 : WASM_SIMD_TEST(I32x4GtS) {
953 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4GtS, Greater);
954 0 : }
955 :
956 26087 : WASM_SIMD_TEST(I32x4GeS) {
957 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4GeS, GreaterEqual);
958 0 : }
959 :
960 26087 : WASM_SIMD_TEST(I32x4LtU) {
961 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4LtU, UnsignedLess);
962 0 : }
963 :
964 26087 : WASM_SIMD_TEST(I32x4LeU) {
965 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4LeU,
966 12 : UnsignedLessEqual);
967 0 : }
968 :
969 26087 : WASM_SIMD_TEST(I32x4GtU) {
970 12 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4GtU, UnsignedGreater);
971 0 : }
972 :
973 26087 : WASM_SIMD_TEST(I32x4GeU) {
974 0 : RunI32x4BinOpTest(execution_tier, lower_simd, kExprI32x4GeU,
975 12 : UnsignedGreaterEqual);
976 0 : }
977 :
978 36 : void RunI32x4ShiftOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
979 : WasmOpcode opcode, Int32ShiftOp expected_op) {
980 2268 : for (int shift = 1; shift < 32; shift++) {
981 1116 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
982 : int32_t* g = r.builder().AddGlobal<int32_t>(kWasmS128);
983 : byte value = 0;
984 : byte simd1 = r.AllocateLocal(kWasmS128);
985 1116 : BUILD(r,
986 : WASM_SET_LOCAL(simd1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value))),
987 : WASM_SET_GLOBAL(
988 : 0, WASM_SIMD_SHIFT_OP(opcode, shift, WASM_GET_LOCAL(simd1))),
989 : WASM_ONE);
990 :
991 130572 : FOR_INT32_INPUTS(x) {
992 64728 : r.Call(x);
993 64728 : float expected = expected_op(x, shift);
994 582552 : for (int i = 0; i < 4; i++) {
995 517824 : CHECK_EQ(expected, ReadLittleEndianValue<int32_t>(&g[i]));
996 : }
997 : }
998 : }
999 36 : }
1000 :
1001 26087 : WASM_SIMD_TEST(I32x4Shl) {
1002 0 : RunI32x4ShiftOpTest(execution_tier, lower_simd, kExprI32x4Shl,
1003 12 : LogicalShiftLeft);
1004 0 : }
1005 :
1006 26087 : WASM_SIMD_TEST(I32x4ShrS) {
1007 0 : RunI32x4ShiftOpTest(execution_tier, lower_simd, kExprI32x4ShrS,
1008 12 : ArithmeticShiftRight);
1009 0 : }
1010 :
1011 26087 : WASM_SIMD_TEST(I32x4ShrU) {
1012 0 : RunI32x4ShiftOpTest(execution_tier, lower_simd, kExprI32x4ShrU,
1013 12 : LogicalShiftRight);
1014 0 : }
1015 :
1016 : // Tests both signed and unsigned conversion from I8x16 (unpacking).
1017 26111 : WASM_SIMD_TEST(I16x8ConvertI8x16) {
1018 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1019 : // Create four output vectors to hold signed and unsigned results.
1020 : int16_t* g0 = r.builder().AddGlobal<int16_t>(kWasmS128);
1021 : int16_t* g1 = r.builder().AddGlobal<int16_t>(kWasmS128);
1022 : int16_t* g2 = r.builder().AddGlobal<int16_t>(kWasmS128);
1023 : int16_t* g3 = r.builder().AddGlobal<int16_t>(kWasmS128);
1024 : // Build fn to splat test value, perform conversions, and write the results.
1025 : byte value = 0;
1026 : byte temp1 = r.AllocateLocal(kWasmS128);
1027 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(value))),
1028 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(kExprI16x8SConvertI8x16High,
1029 : WASM_GET_LOCAL(temp1))),
1030 : WASM_SET_GLOBAL(1, WASM_SIMD_UNOP(kExprI16x8SConvertI8x16Low,
1031 : WASM_GET_LOCAL(temp1))),
1032 : WASM_SET_GLOBAL(2, WASM_SIMD_UNOP(kExprI16x8UConvertI8x16High,
1033 : WASM_GET_LOCAL(temp1))),
1034 : WASM_SET_GLOBAL(3, WASM_SIMD_UNOP(kExprI16x8UConvertI8x16Low,
1035 : WASM_GET_LOCAL(temp1))),
1036 : WASM_ONE);
1037 :
1038 228 : FOR_INT8_INPUTS(x) {
1039 108 : r.Call(x);
1040 : int16_t expected_signed = static_cast<int16_t>(Widen<int8_t>(x));
1041 : int16_t expected_unsigned = static_cast<int16_t>(UnsignedWiden<int8_t>(x));
1042 1836 : for (int i = 0; i < 8; i++) {
1043 864 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int16_t>(&g0[i]));
1044 864 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int16_t>(&g1[i]));
1045 864 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int16_t>(&g2[i]));
1046 864 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int16_t>(&g3[i]));
1047 : }
1048 : }
1049 12 : }
1050 :
1051 : // Tests both signed and unsigned conversion from I32x4 (packing).
1052 26111 : WASM_SIMD_TEST(I16x8ConvertI32x4) {
1053 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1054 : // Create output vectors to hold signed and unsigned results.
1055 : int16_t* g0 = r.builder().AddGlobal<int16_t>(kWasmS128);
1056 : int16_t* g1 = r.builder().AddGlobal<int16_t>(kWasmS128);
1057 : // Build fn to splat test value, perform conversions, and write the results.
1058 : byte value = 0;
1059 : byte temp1 = r.AllocateLocal(kWasmS128);
1060 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(value))),
1061 : WASM_SET_GLOBAL(
1062 : 0, WASM_SIMD_BINOP(kExprI16x8SConvertI32x4, WASM_GET_LOCAL(temp1),
1063 : WASM_GET_LOCAL(temp1))),
1064 : WASM_SET_GLOBAL(
1065 : 1, WASM_SIMD_BINOP(kExprI16x8UConvertI32x4, WASM_GET_LOCAL(temp1),
1066 : WASM_GET_LOCAL(temp1))),
1067 : WASM_ONE);
1068 :
1069 1404 : FOR_INT32_INPUTS(x) {
1070 696 : r.Call(x);
1071 696 : int16_t expected_signed = Narrow<int16_t>(x);
1072 : int16_t expected_unsigned = UnsignedNarrow<int16_t>(x);
1073 11832 : for (int i = 0; i < 8; i++) {
1074 5568 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int16_t>(&g0[i]));
1075 5568 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int16_t>(&g1[i]));
1076 : }
1077 : }
1078 12 : }
1079 :
1080 12 : void RunI16x8UnOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1081 : WasmOpcode opcode, Int16UnOp expected_op) {
1082 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1083 : // Global to hold output.
1084 : int16_t* g = r.builder().AddGlobal<int16_t>(kWasmS128);
1085 : // Build fn to splat test value, perform unop, and write the result.
1086 : byte value = 0;
1087 : byte temp1 = r.AllocateLocal(kWasmS128);
1088 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value))),
1089 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(opcode, WASM_GET_LOCAL(temp1))),
1090 : WASM_ONE);
1091 :
1092 228 : FOR_INT16_INPUTS(x) {
1093 108 : r.Call(x);
1094 108 : int16_t expected = expected_op(x);
1095 1836 : for (int i = 0; i < 8; i++) {
1096 864 : CHECK_EQ(expected, ReadLittleEndianValue<int16_t>(&g[i]));
1097 : }
1098 : }
1099 12 : }
1100 :
1101 26087 : WASM_SIMD_TEST(I16x8Neg) {
1102 0 : RunI16x8UnOpTest(execution_tier, lower_simd, kExprI16x8Neg,
1103 12 : base::NegateWithWraparound);
1104 0 : }
1105 :
1106 252 : void RunI16x8BinOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1107 : WasmOpcode opcode, Int16BinOp expected_op) {
1108 252 : WasmRunner<int32_t, int32_t, int32_t> r(execution_tier, lower_simd);
1109 : // Global to hold output.
1110 : int16_t* g = r.builder().AddGlobal<int16_t>(kWasmS128);
1111 : // Build fn to splat test values, perform binop, and write the result.
1112 : byte value1 = 0, value2 = 1;
1113 : byte temp1 = r.AllocateLocal(kWasmS128);
1114 : byte temp2 = r.AllocateLocal(kWasmS128);
1115 252 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value1))),
1116 : WASM_SET_LOCAL(temp2, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value2))),
1117 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(opcode, WASM_GET_LOCAL(temp1),
1118 : WASM_GET_LOCAL(temp2))),
1119 : WASM_ONE);
1120 :
1121 4788 : FOR_INT16_INPUTS(x) {
1122 43092 : FOR_INT16_INPUTS(y) {
1123 20412 : r.Call(x, y);
1124 20412 : int16_t expected = expected_op(x, y);
1125 347004 : for (int i = 0; i < 8; i++) {
1126 163296 : CHECK_EQ(expected, ReadLittleEndianValue<int16_t>(&g[i]));
1127 : }
1128 : }
1129 : }
1130 252 : }
1131 :
1132 26087 : WASM_SIMD_TEST(I16x8Add) {
1133 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8Add,
1134 12 : base::AddWithWraparound);
1135 0 : }
1136 :
1137 26087 : WASM_SIMD_TEST(I16x8AddSaturateS) {
1138 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8AddSaturateS,
1139 12 : AddSaturate);
1140 0 : }
1141 :
1142 26087 : WASM_SIMD_TEST(I16x8Sub) {
1143 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8Sub,
1144 12 : base::SubWithWraparound);
1145 0 : }
1146 :
1147 26087 : WASM_SIMD_TEST(I16x8SubSaturateS) {
1148 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8SubSaturateS,
1149 12 : SubSaturate);
1150 0 : }
1151 :
1152 26087 : WASM_SIMD_TEST(I16x8Mul) {
1153 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8Mul,
1154 12 : base::MulWithWraparound);
1155 0 : }
1156 :
1157 26087 : WASM_SIMD_TEST(I16x8MinS) {
1158 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8MinS, Minimum);
1159 0 : }
1160 :
1161 26087 : WASM_SIMD_TEST(I16x8MaxS) {
1162 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8MaxS, Maximum);
1163 0 : }
1164 :
1165 26087 : WASM_SIMD_TEST(I16x8AddSaturateU) {
1166 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8AddSaturateU,
1167 12 : UnsignedAddSaturate);
1168 0 : }
1169 :
1170 26087 : WASM_SIMD_TEST(I16x8SubSaturateU) {
1171 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8SubSaturateU,
1172 12 : UnsignedSubSaturate);
1173 0 : }
1174 :
1175 26087 : WASM_SIMD_TEST(I16x8MinU) {
1176 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8MinU,
1177 12 : UnsignedMinimum);
1178 0 : }
1179 :
1180 26087 : WASM_SIMD_TEST(I16x8MaxU) {
1181 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8MaxU,
1182 12 : UnsignedMaximum);
1183 0 : }
1184 :
1185 26087 : WASM_SIMD_TEST(I16x8Eq) {
1186 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8Eq, Equal);
1187 0 : }
1188 :
1189 26087 : WASM_SIMD_TEST(I16x8Ne) {
1190 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8Ne, NotEqual);
1191 0 : }
1192 :
1193 26087 : WASM_SIMD_TEST(I16x8LtS) {
1194 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8LtS, Less);
1195 0 : }
1196 :
1197 26087 : WASM_SIMD_TEST(I16x8LeS) {
1198 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8LeS, LessEqual);
1199 0 : }
1200 :
1201 26087 : WASM_SIMD_TEST(I16x8GtS) {
1202 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8GtS, Greater);
1203 0 : }
1204 :
1205 26087 : WASM_SIMD_TEST(I16x8GeS) {
1206 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8GeS, GreaterEqual);
1207 0 : }
1208 :
1209 26087 : WASM_SIMD_TEST(I16x8GtU) {
1210 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8GtU, UnsignedGreater);
1211 0 : }
1212 :
1213 26087 : WASM_SIMD_TEST(I16x8GeU) {
1214 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8GeU,
1215 12 : UnsignedGreaterEqual);
1216 0 : }
1217 :
1218 26087 : WASM_SIMD_TEST(I16x8LtU) {
1219 12 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8LtU, UnsignedLess);
1220 0 : }
1221 :
1222 26087 : WASM_SIMD_TEST(I16x8LeU) {
1223 0 : RunI16x8BinOpTest(execution_tier, lower_simd, kExprI16x8LeU,
1224 12 : UnsignedLessEqual);
1225 0 : }
1226 :
1227 36 : void RunI16x8ShiftOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1228 : WasmOpcode opcode, Int16ShiftOp expected_op) {
1229 1116 : for (int shift = 1; shift < 16; shift++) {
1230 540 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1231 : int16_t* g = r.builder().AddGlobal<int16_t>(kWasmS128);
1232 : byte value = 0;
1233 : byte simd1 = r.AllocateLocal(kWasmS128);
1234 540 : BUILD(r,
1235 : WASM_SET_LOCAL(simd1, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value))),
1236 : WASM_SET_GLOBAL(
1237 : 0, WASM_SIMD_SHIFT_OP(opcode, shift, WASM_GET_LOCAL(simd1))),
1238 : WASM_ONE);
1239 :
1240 10260 : FOR_INT16_INPUTS(x) {
1241 4860 : r.Call(x);
1242 4860 : float expected = expected_op(x, shift);
1243 82620 : for (int i = 0; i < 8; i++) {
1244 77760 : CHECK_EQ(expected, ReadLittleEndianValue<int16_t>(&g[i]));
1245 : }
1246 : }
1247 : }
1248 36 : }
1249 :
1250 26087 : WASM_SIMD_TEST(I16x8Shl) {
1251 0 : RunI16x8ShiftOpTest(execution_tier, lower_simd, kExprI16x8Shl,
1252 12 : LogicalShiftLeft);
1253 0 : }
1254 :
1255 26087 : WASM_SIMD_TEST(I16x8ShrS) {
1256 0 : RunI16x8ShiftOpTest(execution_tier, lower_simd, kExprI16x8ShrS,
1257 12 : ArithmeticShiftRight);
1258 0 : }
1259 :
1260 26087 : WASM_SIMD_TEST(I16x8ShrU) {
1261 0 : RunI16x8ShiftOpTest(execution_tier, lower_simd, kExprI16x8ShrU,
1262 12 : LogicalShiftRight);
1263 0 : }
1264 :
1265 12 : void RunI8x16UnOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1266 : WasmOpcode opcode, Int8UnOp expected_op) {
1267 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1268 : // Global to hold output.
1269 : int8_t* g = r.builder().AddGlobal<int8_t>(kWasmS128);
1270 : // Build fn to splat test value, perform unop, and write the result.
1271 : byte value = 0;
1272 : byte temp1 = r.AllocateLocal(kWasmS128);
1273 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(value))),
1274 : WASM_SET_GLOBAL(0, WASM_SIMD_UNOP(opcode, WASM_GET_LOCAL(temp1))),
1275 : WASM_ONE);
1276 :
1277 228 : FOR_INT8_INPUTS(x) {
1278 108 : r.Call(x);
1279 108 : int8_t expected = expected_op(x);
1280 3564 : for (int i = 0; i < 16; i++) {
1281 1728 : CHECK_EQ(expected, ReadLittleEndianValue<int8_t>(&g[i]));
1282 : }
1283 : }
1284 12 : }
1285 :
1286 26087 : WASM_SIMD_TEST(I8x16Neg) {
1287 0 : RunI8x16UnOpTest(execution_tier, lower_simd, kExprI8x16Neg,
1288 12 : base::NegateWithWraparound);
1289 0 : }
1290 :
1291 : // Tests both signed and unsigned conversion from I16x8 (packing).
1292 26111 : WASM_SIMD_TEST(I8x16ConvertI16x8) {
1293 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1294 : // Create output vectors to hold signed and unsigned results.
1295 : int8_t* g0 = r.builder().AddGlobal<int8_t>(kWasmS128);
1296 : int8_t* g1 = r.builder().AddGlobal<int8_t>(kWasmS128);
1297 : // Build fn to splat test value, perform conversions, and write the results.
1298 : byte value = 0;
1299 : byte temp1 = r.AllocateLocal(kWasmS128);
1300 12 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I16x8_SPLAT(WASM_GET_LOCAL(value))),
1301 : WASM_SET_GLOBAL(
1302 : 0, WASM_SIMD_BINOP(kExprI8x16SConvertI16x8, WASM_GET_LOCAL(temp1),
1303 : WASM_GET_LOCAL(temp1))),
1304 : WASM_SET_GLOBAL(
1305 : 1, WASM_SIMD_BINOP(kExprI8x16UConvertI16x8, WASM_GET_LOCAL(temp1),
1306 : WASM_GET_LOCAL(temp1))),
1307 : WASM_ONE);
1308 :
1309 228 : FOR_INT16_INPUTS(x) {
1310 108 : r.Call(x);
1311 108 : int8_t expected_signed = Narrow<int8_t>(x);
1312 : int8_t expected_unsigned = UnsignedNarrow<int8_t>(x);
1313 3564 : for (int i = 0; i < 16; i++) {
1314 1728 : CHECK_EQ(expected_signed, ReadLittleEndianValue<int8_t>(&g0[i]));
1315 1728 : CHECK_EQ(expected_unsigned, ReadLittleEndianValue<int8_t>(&g1[i]));
1316 : }
1317 : }
1318 12 : }
1319 :
1320 252 : void RunI8x16BinOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1321 : WasmOpcode opcode, Int8BinOp expected_op) {
1322 252 : WasmRunner<int32_t, int32_t, int32_t> r(execution_tier, lower_simd);
1323 : // Global to hold output.
1324 : int8_t* g = r.builder().AddGlobal<int8_t>(kWasmS128);
1325 : // Build fn to splat test values, perform binop, and write the result.
1326 : byte value1 = 0, value2 = 1;
1327 : byte temp1 = r.AllocateLocal(kWasmS128);
1328 : byte temp2 = r.AllocateLocal(kWasmS128);
1329 252 : BUILD(r, WASM_SET_LOCAL(temp1, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(value1))),
1330 : WASM_SET_LOCAL(temp2, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(value2))),
1331 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(opcode, WASM_GET_LOCAL(temp1),
1332 : WASM_GET_LOCAL(temp2))),
1333 : WASM_ONE);
1334 :
1335 4788 : FOR_INT8_INPUTS(x) {
1336 43092 : FOR_INT8_INPUTS(y) {
1337 20412 : r.Call(x, y);
1338 20412 : int8_t expected = expected_op(x, y);
1339 673596 : for (int i = 0; i < 16; i++) {
1340 326592 : CHECK_EQ(expected, ReadLittleEndianValue<int8_t>(&g[i]));
1341 : }
1342 : }
1343 : }
1344 252 : }
1345 :
1346 26087 : WASM_SIMD_TEST(I8x16Add) {
1347 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16Add,
1348 12 : base::AddWithWraparound);
1349 0 : }
1350 :
1351 26087 : WASM_SIMD_TEST(I8x16AddSaturateS) {
1352 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16AddSaturateS,
1353 12 : AddSaturate);
1354 0 : }
1355 :
1356 26087 : WASM_SIMD_TEST(I8x16Sub) {
1357 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16Sub,
1358 12 : base::SubWithWraparound);
1359 0 : }
1360 :
1361 26087 : WASM_SIMD_TEST(I8x16SubSaturateS) {
1362 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16SubSaturateS,
1363 12 : SubSaturate);
1364 0 : }
1365 :
1366 26087 : WASM_SIMD_TEST(I8x16MinS) {
1367 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16MinS, Minimum);
1368 0 : }
1369 :
1370 26087 : WASM_SIMD_TEST(I8x16MaxS) {
1371 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16MaxS, Maximum);
1372 0 : }
1373 :
1374 26087 : WASM_SIMD_TEST(I8x16AddSaturateU) {
1375 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16AddSaturateU,
1376 12 : UnsignedAddSaturate);
1377 0 : }
1378 :
1379 26087 : WASM_SIMD_TEST(I8x16SubSaturateU) {
1380 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16SubSaturateU,
1381 12 : UnsignedSubSaturate);
1382 0 : }
1383 :
1384 26087 : WASM_SIMD_TEST(I8x16MinU) {
1385 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16MinU,
1386 12 : UnsignedMinimum);
1387 0 : }
1388 :
1389 26087 : WASM_SIMD_TEST(I8x16MaxU) {
1390 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16MaxU,
1391 12 : UnsignedMaximum);
1392 0 : }
1393 :
1394 26087 : WASM_SIMD_TEST(I8x16Eq) {
1395 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16Eq, Equal);
1396 0 : }
1397 :
1398 26087 : WASM_SIMD_TEST(I8x16Ne) {
1399 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16Ne, NotEqual);
1400 0 : }
1401 :
1402 26087 : WASM_SIMD_TEST(I8x16GtS) {
1403 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16GtS, Greater);
1404 0 : }
1405 :
1406 26087 : WASM_SIMD_TEST(I8x16GeS) {
1407 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16GeS, GreaterEqual);
1408 0 : }
1409 :
1410 26087 : WASM_SIMD_TEST(I8x16LtS) {
1411 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16LtS, Less);
1412 0 : }
1413 :
1414 26087 : WASM_SIMD_TEST(I8x16LeS) {
1415 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16LeS, LessEqual);
1416 0 : }
1417 :
1418 26087 : WASM_SIMD_TEST(I8x16GtU) {
1419 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16GtU, UnsignedGreater);
1420 0 : }
1421 :
1422 26087 : WASM_SIMD_TEST(I8x16GeU) {
1423 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16GeU,
1424 12 : UnsignedGreaterEqual);
1425 0 : }
1426 :
1427 26087 : WASM_SIMD_TEST(I8x16LtU) {
1428 12 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16LtU, UnsignedLess);
1429 0 : }
1430 :
1431 26087 : WASM_SIMD_TEST(I8x16LeU) {
1432 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16LeU,
1433 12 : UnsignedLessEqual);
1434 0 : }
1435 :
1436 26087 : WASM_SIMD_TEST(I8x16Mul) {
1437 0 : RunI8x16BinOpTest(execution_tier, lower_simd, kExprI8x16Mul,
1438 12 : base::MulWithWraparound);
1439 0 : }
1440 :
1441 36 : void RunI8x16ShiftOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1442 : WasmOpcode opcode, Int8ShiftOp expected_op) {
1443 540 : for (int shift = 1; shift < 8; shift++) {
1444 252 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
1445 : int8_t* g = r.builder().AddGlobal<int8_t>(kWasmS128);
1446 : byte value = 0;
1447 : byte simd1 = r.AllocateLocal(kWasmS128);
1448 252 : BUILD(r,
1449 : WASM_SET_LOCAL(simd1, WASM_SIMD_I8x16_SPLAT(WASM_GET_LOCAL(value))),
1450 : WASM_SET_GLOBAL(
1451 : 0, WASM_SIMD_SHIFT_OP(opcode, shift, WASM_GET_LOCAL(simd1))),
1452 : WASM_ONE);
1453 :
1454 4788 : FOR_INT8_INPUTS(x) {
1455 2268 : r.Call(x);
1456 2268 : float expected = expected_op(x, shift);
1457 74844 : for (int i = 0; i < 16; i++) {
1458 72576 : CHECK_EQ(expected, ReadLittleEndianValue<int8_t>(&g[i]));
1459 : }
1460 : }
1461 : }
1462 36 : }
1463 :
1464 26087 : WASM_SIMD_TEST(I8x16Shl) {
1465 0 : RunI8x16ShiftOpTest(execution_tier, lower_simd, kExprI8x16Shl,
1466 12 : LogicalShiftLeft);
1467 0 : }
1468 :
1469 26087 : WASM_SIMD_TEST(I8x16ShrS) {
1470 0 : RunI8x16ShiftOpTest(execution_tier, lower_simd, kExprI8x16ShrS,
1471 12 : ArithmeticShiftRight);
1472 0 : }
1473 :
1474 26087 : WASM_SIMD_TEST(I8x16ShrU) {
1475 0 : RunI8x16ShiftOpTest(execution_tier, lower_simd, kExprI8x16ShrU,
1476 12 : LogicalShiftRight);
1477 0 : }
1478 :
1479 : // Test Select by making a mask where the 0th and 3rd lanes are true and the
1480 : // rest false, and comparing for non-equality with zero to convert to a boolean
1481 : // vector.
1482 : #define WASM_SIMD_SELECT_TEST(format) \
1483 : WASM_SIMD_TEST_TURBOFAN(S##format##Select) { \
1484 : WasmRunner<int32_t, int32_t, int32_t> r(execution_tier, lower_simd); \
1485 : byte val1 = 0; \
1486 : byte val2 = 1; \
1487 : byte src1 = r.AllocateLocal(kWasmS128); \
1488 : byte src2 = r.AllocateLocal(kWasmS128); \
1489 : byte zero = r.AllocateLocal(kWasmS128); \
1490 : byte mask = r.AllocateLocal(kWasmS128); \
1491 : BUILD(r, \
1492 : WASM_SET_LOCAL(src1, \
1493 : WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(val1))), \
1494 : WASM_SET_LOCAL(src2, \
1495 : WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(val2))), \
1496 : WASM_SET_LOCAL(zero, WASM_SIMD_I##format##_SPLAT(WASM_ZERO)), \
1497 : WASM_SET_LOCAL(mask, WASM_SIMD_I##format##_REPLACE_LANE( \
1498 : 1, WASM_GET_LOCAL(zero), WASM_I32V(-1))), \
1499 : WASM_SET_LOCAL(mask, WASM_SIMD_I##format##_REPLACE_LANE( \
1500 : 2, WASM_GET_LOCAL(mask), WASM_I32V(-1))), \
1501 : WASM_SET_LOCAL( \
1502 : mask, \
1503 : WASM_SIMD_SELECT( \
1504 : format, WASM_GET_LOCAL(src1), WASM_GET_LOCAL(src2), \
1505 : WASM_SIMD_BINOP(kExprI##format##Ne, WASM_GET_LOCAL(mask), \
1506 : WASM_GET_LOCAL(zero)))), \
1507 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val2, 0), \
1508 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val1, 1), \
1509 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val1, 2), \
1510 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val2, 3), WASM_ONE); \
1511 : \
1512 : CHECK_EQ(1, r.Call(0x12, 0x34)); \
1513 : }
1514 :
1515 26083 : WASM_SIMD_SELECT_TEST(32x4)
1516 26083 : WASM_SIMD_SELECT_TEST(16x8)
1517 26083 : WASM_SIMD_SELECT_TEST(8x16)
1518 :
1519 : // Test Select by making a mask where the 0th and 3rd lanes are non-zero and the
1520 : // rest 0. The mask is not the result of a comparison op.
1521 : #define WASM_SIMD_NON_CANONICAL_SELECT_TEST(format) \
1522 : WASM_SIMD_TEST_TURBOFAN(S##format##NonCanonicalSelect) { \
1523 : WasmRunner<int32_t, int32_t, int32_t, int32_t> r(execution_tier, \
1524 : lower_simd); \
1525 : byte val1 = 0; \
1526 : byte val2 = 1; \
1527 : byte combined = 2; \
1528 : byte src1 = r.AllocateLocal(kWasmS128); \
1529 : byte src2 = r.AllocateLocal(kWasmS128); \
1530 : byte zero = r.AllocateLocal(kWasmS128); \
1531 : byte mask = r.AllocateLocal(kWasmS128); \
1532 : BUILD(r, \
1533 : WASM_SET_LOCAL(src1, \
1534 : WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(val1))), \
1535 : WASM_SET_LOCAL(src2, \
1536 : WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(val2))), \
1537 : WASM_SET_LOCAL(zero, WASM_SIMD_I##format##_SPLAT(WASM_ZERO)), \
1538 : WASM_SET_LOCAL(mask, WASM_SIMD_I##format##_REPLACE_LANE( \
1539 : 1, WASM_GET_LOCAL(zero), WASM_I32V(0xF))), \
1540 : WASM_SET_LOCAL(mask, WASM_SIMD_I##format##_REPLACE_LANE( \
1541 : 2, WASM_GET_LOCAL(mask), WASM_I32V(0xF))), \
1542 : WASM_SET_LOCAL(mask, WASM_SIMD_SELECT(format, WASM_GET_LOCAL(src1), \
1543 : WASM_GET_LOCAL(src2), \
1544 : WASM_GET_LOCAL(mask))), \
1545 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val2, 0), \
1546 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, combined, 1), \
1547 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, combined, 2), \
1548 : WASM_SIMD_CHECK_LANE(I##format, mask, I32, val2, 3), WASM_ONE); \
1549 : \
1550 : CHECK_EQ(1, r.Call(0x12, 0x34, 0x32)); \
1551 : }
1552 :
1553 26083 : WASM_SIMD_NON_CANONICAL_SELECT_TEST(32x4)
1554 26083 : WASM_SIMD_NON_CANONICAL_SELECT_TEST(16x8)
1555 26083 : WASM_SIMD_NON_CANONICAL_SELECT_TEST(8x16)
1556 :
1557 : // Test binary ops with two lane test patterns, all lanes distinct.
1558 : template <typename T>
1559 7764 : void RunBinaryLaneOpTest(
1560 : ExecutionTier execution_tier, LowerSimd lower_simd, WasmOpcode simd_op,
1561 : const std::array<T, kSimd128Size / sizeof(T)>& expected) {
1562 7764 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1563 : // Set up two test patterns as globals, e.g. [0, 1, 2, 3] and [4, 5, 6, 7].
1564 : T* src0 = r.builder().AddGlobal<T>(kWasmS128);
1565 : T* src1 = r.builder().AddGlobal<T>(kWasmS128);
1566 : static const int kElems = kSimd128Size / sizeof(T);
1567 255444 : for (int i = 0; i < kElems; i++) {
1568 123840 : WriteLittleEndianValue<T>(&src0[i], i);
1569 123840 : WriteLittleEndianValue<T>(&src1[i], kElems + i);
1570 : }
1571 7764 : if (simd_op == kExprS8x16Shuffle) {
1572 7728 : BUILD(r,
1573 : WASM_SET_GLOBAL(0, WASM_SIMD_S8x16_SHUFFLE_OP(simd_op, expected,
1574 : WASM_GET_GLOBAL(0),
1575 : WASM_GET_GLOBAL(1))),
1576 : WASM_ONE);
1577 : } else {
1578 36 : BUILD(r,
1579 : WASM_SET_GLOBAL(0, WASM_SIMD_BINOP(simd_op, WASM_GET_GLOBAL(0),
1580 : WASM_GET_GLOBAL(1))),
1581 : WASM_ONE);
1582 : }
1583 :
1584 7764 : CHECK_EQ(1, r.Call());
1585 255444 : for (size_t i = 0; i < expected.size(); i++) {
1586 123840 : CHECK_EQ(ReadLittleEndianValue<T>(&src0[i]), expected[i]);
1587 : }
1588 7764 : }
1589 :
1590 26087 : WASM_SIMD_TEST(I32x4AddHoriz) {
1591 : // Inputs are [0 1 2 3] and [4 5 6 7].
1592 24 : RunBinaryLaneOpTest<int32_t>(execution_tier, lower_simd, kExprI32x4AddHoriz,
1593 12 : {{1, 5, 9, 13}});
1594 0 : }
1595 :
1596 26111 : WASM_SIMD_TEST(I16x8AddHoriz) {
1597 : // Inputs are [0 1 2 3 4 5 6 7] and [8 9 10 11 12 13 14 15].
1598 24 : RunBinaryLaneOpTest<int16_t>(execution_tier, lower_simd, kExprI16x8AddHoriz,
1599 12 : {{1, 5, 9, 13, 17, 21, 25, 29}});
1600 12 : }
1601 :
1602 26087 : WASM_SIMD_TEST(F32x4AddHoriz) {
1603 : // Inputs are [0.0f 1.0f 2.0f 3.0f] and [4.0f 5.0f 6.0f 7.0f].
1604 24 : RunBinaryLaneOpTest<float>(execution_tier, lower_simd, kExprF32x4AddHoriz,
1605 12 : {{1.0f, 5.0f, 9.0f, 13.0f}});
1606 0 : }
1607 :
1608 : // Test shuffle ops.
1609 1932 : void RunShuffleOpTest(ExecutionTier execution_tier, LowerSimd lower_simd,
1610 : WasmOpcode simd_op,
1611 : const std::array<int8_t, kSimd128Size>& shuffle) {
1612 : // Test the original shuffle.
1613 1932 : RunBinaryLaneOpTest<int8_t>(execution_tier, lower_simd, simd_op, shuffle);
1614 :
1615 : // Test a non-canonical (inputs reversed) version of the shuffle.
1616 1932 : std::array<int8_t, kSimd128Size> other_shuffle(shuffle);
1617 32844 : for (size_t i = 0; i < shuffle.size(); ++i) other_shuffle[i] ^= kSimd128Size;
1618 : RunBinaryLaneOpTest<int8_t>(execution_tier, lower_simd, simd_op,
1619 1932 : other_shuffle);
1620 :
1621 : // Test the swizzle (one-operand) version of the shuffle.
1622 1932 : std::array<int8_t, kSimd128Size> swizzle(shuffle);
1623 32844 : for (size_t i = 0; i < shuffle.size(); ++i) swizzle[i] &= (kSimd128Size - 1);
1624 1932 : RunBinaryLaneOpTest<int8_t>(execution_tier, lower_simd, simd_op, swizzle);
1625 :
1626 : // Test the non-canonical swizzle (one-operand) version of the shuffle.
1627 1932 : std::array<int8_t, kSimd128Size> other_swizzle(shuffle);
1628 32844 : for (size_t i = 0; i < shuffle.size(); ++i) other_swizzle[i] |= kSimd128Size;
1629 : RunBinaryLaneOpTest<int8_t>(execution_tier, lower_simd, simd_op,
1630 1932 : other_swizzle);
1631 1932 : }
1632 :
1633 : #define SHUFFLE_LIST(V) \
1634 : V(S128Identity) \
1635 : V(S32x4Dup) \
1636 : V(S32x4ZipLeft) \
1637 : V(S32x4ZipRight) \
1638 : V(S32x4UnzipLeft) \
1639 : V(S32x4UnzipRight) \
1640 : V(S32x4TransposeLeft) \
1641 : V(S32x4TransposeRight) \
1642 : V(S32x2Reverse) \
1643 : V(S32x4Irregular) \
1644 : V(S16x8Dup) \
1645 : V(S16x8ZipLeft) \
1646 : V(S16x8ZipRight) \
1647 : V(S16x8UnzipLeft) \
1648 : V(S16x8UnzipRight) \
1649 : V(S16x8TransposeLeft) \
1650 : V(S16x8TransposeRight) \
1651 : V(S16x4Reverse) \
1652 : V(S16x2Reverse) \
1653 : V(S16x8Irregular) \
1654 : V(S8x16Dup) \
1655 : V(S8x16ZipLeft) \
1656 : V(S8x16ZipRight) \
1657 : V(S8x16UnzipLeft) \
1658 : V(S8x16UnzipRight) \
1659 : V(S8x16TransposeLeft) \
1660 : V(S8x16TransposeRight) \
1661 : V(S8x8Reverse) \
1662 : V(S8x4Reverse) \
1663 : V(S8x2Reverse) \
1664 : V(S8x16Irregular)
1665 :
1666 : enum ShuffleKey {
1667 : #define SHUFFLE_ENUM_VALUE(Name) k##Name,
1668 : SHUFFLE_LIST(SHUFFLE_ENUM_VALUE)
1669 : #undef SHUFFLE_ENUM_VALUE
1670 : kNumShuffleKeys
1671 : };
1672 :
1673 : using Shuffle = std::array<int8_t, kSimd128Size>;
1674 : using ShuffleMap = std::map<ShuffleKey, const Shuffle>;
1675 :
1676 26063 : ShuffleMap test_shuffles = {
1677 : {kS128Identity,
1678 : {{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}}},
1679 : {kS32x4Dup,
1680 : {{16, 17, 18, 19, 16, 17, 18, 19, 16, 17, 18, 19, 16, 17, 18, 19}}},
1681 : {kS32x4ZipLeft, {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23}}},
1682 : {kS32x4ZipRight,
1683 : {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31}}},
1684 : {kS32x4UnzipLeft,
1685 : {{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27}}},
1686 : {kS32x4UnzipRight,
1687 : {{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31}}},
1688 : {kS32x4TransposeLeft,
1689 : {{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27}}},
1690 : {kS32x4TransposeRight,
1691 : {{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31}}},
1692 : {kS32x2Reverse, // swizzle only
1693 : {{4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}}},
1694 : {kS32x4Irregular,
1695 : {{0, 1, 2, 3, 16, 17, 18, 19, 16, 17, 18, 19, 20, 21, 22, 23}}},
1696 : {kS16x8Dup,
1697 : {{18, 19, 18, 19, 18, 19, 18, 19, 18, 19, 18, 19, 18, 19, 18, 19}}},
1698 : {kS16x8ZipLeft, {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23}}},
1699 : {kS16x8ZipRight,
1700 : {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31}}},
1701 : {kS16x8UnzipLeft,
1702 : {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29}}},
1703 : {kS16x8UnzipRight,
1704 : {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31}}},
1705 : {kS16x8TransposeLeft,
1706 : {{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29}}},
1707 : {kS16x8TransposeRight,
1708 : {{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}}},
1709 : {kS16x4Reverse, // swizzle only
1710 : {{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9}}},
1711 : {kS16x2Reverse, // swizzle only
1712 : {{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}}},
1713 : {kS16x8Irregular,
1714 : {{0, 1, 16, 17, 16, 17, 0, 1, 4, 5, 20, 21, 6, 7, 22, 23}}},
1715 : {kS8x16Dup,
1716 : {{19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19}}},
1717 : {kS8x16ZipLeft, {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}}},
1718 : {kS8x16ZipRight,
1719 : {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31}}},
1720 : {kS8x16UnzipLeft,
1721 : {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}}},
1722 : {kS8x16UnzipRight,
1723 : {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}}},
1724 : {kS8x16TransposeLeft,
1725 : {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30}}},
1726 : {kS8x16TransposeRight,
1727 : {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31}}},
1728 : {kS8x8Reverse, // swizzle only
1729 : {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}}},
1730 : {kS8x4Reverse, // swizzle only
1731 : {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}}},
1732 : {kS8x2Reverse, // swizzle only
1733 : {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14}}},
1734 : {kS8x16Irregular,
1735 : {{0, 16, 0, 16, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}}},
1736 : };
1737 :
1738 : #define SHUFFLE_TEST(Name) \
1739 : WASM_SIMD_TEST(Name) { \
1740 : ShuffleMap::const_iterator it = test_shuffles.find(k##Name); \
1741 : DCHECK_NE(it, test_shuffles.end()); \
1742 : RunShuffleOpTest(execution_tier, lower_simd, kExprS8x16Shuffle, \
1743 : it->second); \
1744 : }
1745 27923 : SHUFFLE_LIST(SHUFFLE_TEST)
1746 : #undef SHUFFLE_TEST
1747 : #undef SHUFFLE_LIST
1748 :
1749 : // Test shuffles that blend the two vectors (elements remain in their lanes.)
1750 26111 : WASM_SIMD_TEST(S8x16Blend) {
1751 : std::array<int8_t, kSimd128Size> expected;
1752 372 : for (int bias = 1; bias < kSimd128Size; bias++) {
1753 1620 : for (int i = 0; i < bias; i++) expected[i] = i;
1754 1620 : for (int i = bias; i < kSimd128Size; i++) expected[i] = i + kSimd128Size;
1755 180 : RunShuffleOpTest(execution_tier, lower_simd, kExprS8x16Shuffle, expected);
1756 : }
1757 12 : }
1758 :
1759 : // Test shuffles that concatenate the two vectors.
1760 26111 : WASM_SIMD_TEST(S8x16Concat) {
1761 : std::array<int8_t, kSimd128Size> expected;
1762 : // n is offset or bias of concatenation.
1763 372 : for (int n = 1; n < kSimd128Size; ++n) {
1764 : int i = 0;
1765 : // last kLanes - n bytes of first vector.
1766 3060 : for (int j = n; j < kSimd128Size; ++j) {
1767 1440 : expected[i++] = j;
1768 : }
1769 : // first n bytes of second vector
1770 3060 : for (int j = 0; j < n; ++j) {
1771 1440 : expected[i++] = j + kSimd128Size;
1772 : }
1773 180 : RunShuffleOpTest(execution_tier, lower_simd, kExprS8x16Shuffle, expected);
1774 : }
1775 12 : }
1776 :
1777 : // Combine 3 shuffles a, b, and c by applying both a and b and then applying c
1778 : // to those two results.
1779 0 : Shuffle Combine(const Shuffle& a, const Shuffle& b, const Shuffle& c) {
1780 : Shuffle result;
1781 39600 : for (int i = 0; i < kSimd128Size; ++i) {
1782 19200 : result[i] = c[i] < kSimd128Size ? a[c[i]] : b[c[i] - kSimd128Size];
1783 : }
1784 1200 : return result;
1785 : }
1786 :
1787 13248 : const Shuffle& GetRandomTestShuffle(v8::base::RandomNumberGenerator* rng) {
1788 13248 : return test_shuffles[static_cast<ShuffleKey>(rng->NextInt(kNumShuffleKeys))];
1789 : }
1790 :
1791 : // Test shuffles that are random combinations of 3 test shuffles. Completely
1792 : // random shuffles almost always generate the slow general shuffle code, so
1793 : // don't exercise as many code paths.
1794 26111 : WASM_SIMD_TEST(S8x16ShuffleFuzz) {
1795 12 : v8::base::RandomNumberGenerator* rng = CcTest::random_number_generator();
1796 : static const int kTests = 100;
1797 2412 : for (int i = 0; i < kTests; ++i) {
1798 1200 : auto shuffle = Combine(GetRandomTestShuffle(rng), GetRandomTestShuffle(rng),
1799 2400 : GetRandomTestShuffle(rng));
1800 1200 : RunShuffleOpTest(execution_tier, lower_simd, kExprS8x16Shuffle, shuffle);
1801 : }
1802 12 : }
1803 :
1804 9648 : void AppendShuffle(const Shuffle& shuffle, std::vector<byte>* buffer) {
1805 9648 : byte opcode[] = {WASM_SIMD_OP(kExprS8x16Shuffle)};
1806 28944 : for (size_t i = 0; i < arraysize(opcode); ++i) buffer->push_back(opcode[i]);
1807 318384 : for (size_t i = 0; i < kSimd128Size; ++i) buffer->push_back((shuffle[i]));
1808 9648 : }
1809 :
1810 800 : void BuildShuffle(std::vector<Shuffle>& shuffles, std::vector<byte>* buffer) {
1811 : // Perform the leaf shuffles on globals 0 and 1.
1812 800 : size_t row_index = (shuffles.size() - 1) / 2;
1813 11248 : for (size_t i = row_index; i < shuffles.size(); ++i) {
1814 5224 : byte operands[] = {WASM_GET_GLOBAL(0), WASM_GET_GLOBAL(1)};
1815 47016 : for (size_t j = 0; j < arraysize(operands); ++j)
1816 20896 : buffer->push_back(operands[j]);
1817 5224 : AppendShuffle(shuffles[i], buffer);
1818 : }
1819 : // Now perform inner shuffles in the correct order on operands on the stack.
1820 : do {
1821 6712 : for (size_t i = row_index / 2; i < row_index; ++i) {
1822 4424 : AppendShuffle(shuffles[i], buffer);
1823 : }
1824 : row_index /= 2;
1825 2288 : } while (row_index != 0);
1826 800 : byte epilog[] = {kExprSetGlobal, static_cast<byte>(0), WASM_ONE};
1827 4000 : for (size_t j = 0; j < arraysize(epilog); ++j) buffer->push_back(epilog[j]);
1828 800 : }
1829 :
1830 1600 : void RunWasmCode(ExecutionTier execution_tier, LowerSimd lower_simd,
1831 : const std::vector<byte>& code,
1832 : std::array<int8_t, kSimd128Size>* result) {
1833 1600 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1834 : // Set up two test patterns as globals, e.g. [0, 1, 2, 3] and [4, 5, 6, 7].
1835 : int8_t* src0 = r.builder().AddGlobal<int8_t>(kWasmS128);
1836 : int8_t* src1 = r.builder().AddGlobal<int8_t>(kWasmS128);
1837 52800 : for (int i = 0; i < kSimd128Size; ++i) {
1838 25600 : WriteLittleEndianValue<int8_t>(&src0[i], i);
1839 25600 : WriteLittleEndianValue<int8_t>(&src1[i], kSimd128Size + i);
1840 : }
1841 1600 : r.Build(code.data(), code.data() + code.size());
1842 1600 : CHECK_EQ(1, r.Call());
1843 52800 : for (size_t i = 0; i < kSimd128Size; i++) {
1844 25600 : (*result)[i] = ReadLittleEndianValue<int8_t>(&src0[i]);
1845 : }
1846 1600 : }
1847 :
1848 : // Test multiple shuffles executed in sequence.
1849 26095 : WASM_SIMD_COMPILED_TEST(S8x16MultiShuffleFuzz) {
1850 8 : v8::base::RandomNumberGenerator* rng = CcTest::random_number_generator();
1851 : static const int kShuffles = 100;
1852 1608 : for (int i = 0; i < kShuffles; ++i) {
1853 : // Create an odd number in [3..23] of random test shuffles so we can build
1854 : // a complete binary tree (stored as a heap) of shuffle operations. The leaf
1855 : // shuffles operate on the test pattern inputs, while the interior shuffles
1856 : // operate on the results of the two child shuffles.
1857 800 : int num_shuffles = rng->NextInt(10) * 2 + 3;
1858 : std::vector<Shuffle> shuffles;
1859 20096 : for (int j = 0; j < num_shuffles; ++j) {
1860 9648 : shuffles.push_back(GetRandomTestShuffle(rng));
1861 : }
1862 : // Generate the code for the shuffle expression.
1863 : std::vector<byte> buffer;
1864 800 : BuildShuffle(shuffles, &buffer);
1865 :
1866 : // Run the code using the interpreter to get the expected result.
1867 : std::array<int8_t, kSimd128Size> expected;
1868 800 : RunWasmCode(ExecutionTier::kInterpreter, kNoLowerSimd, buffer, &expected);
1869 : // Run the SIMD or scalar lowered compiled code and compare results.
1870 : std::array<int8_t, kSimd128Size> result;
1871 800 : RunWasmCode(execution_tier, lower_simd, buffer, &result);
1872 26400 : for (size_t i = 0; i < kSimd128Size; ++i) {
1873 12800 : CHECK_EQ(result[i], expected[i]);
1874 : }
1875 : }
1876 8 : }
1877 :
1878 : // Boolean unary operations are 'AllTrue' and 'AnyTrue', which return an integer
1879 : // result. Use relational ops on numeric vectors to create the boolean vector
1880 : // test inputs. Test inputs with all true, all false, one true, and one false.
1881 : #define WASM_SIMD_BOOL_REDUCTION_TEST(format, lanes) \
1882 : WASM_SIMD_TEST(ReductionTest##lanes) { \
1883 : WasmRunner<int32_t> r(execution_tier, lower_simd); \
1884 : byte zero = r.AllocateLocal(kWasmS128); \
1885 : byte one_one = r.AllocateLocal(kWasmS128); \
1886 : byte reduced = r.AllocateLocal(kWasmI32); \
1887 : BUILD(r, WASM_SET_LOCAL(zero, WASM_SIMD_I##format##_SPLAT(WASM_ZERO)), \
1888 : WASM_SET_LOCAL( \
1889 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AnyTrue, \
1890 : WASM_SIMD_BINOP(kExprI##format##Eq, \
1891 : WASM_GET_LOCAL(zero), \
1892 : WASM_GET_LOCAL(zero)))), \
1893 : WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1894 : WASM_RETURN1(WASM_ZERO)), \
1895 : WASM_SET_LOCAL( \
1896 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AnyTrue, \
1897 : WASM_SIMD_BINOP(kExprI##format##Ne, \
1898 : WASM_GET_LOCAL(zero), \
1899 : WASM_GET_LOCAL(zero)))), \
1900 : WASM_IF(WASM_I32_NE(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1901 : WASM_RETURN1(WASM_ZERO)), \
1902 : WASM_SET_LOCAL( \
1903 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AllTrue, \
1904 : WASM_SIMD_BINOP(kExprI##format##Eq, \
1905 : WASM_GET_LOCAL(zero), \
1906 : WASM_GET_LOCAL(zero)))), \
1907 : WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1908 : WASM_RETURN1(WASM_ZERO)), \
1909 : WASM_SET_LOCAL( \
1910 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AllTrue, \
1911 : WASM_SIMD_BINOP(kExprI##format##Ne, \
1912 : WASM_GET_LOCAL(zero), \
1913 : WASM_GET_LOCAL(zero)))), \
1914 : WASM_IF(WASM_I32_NE(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1915 : WASM_RETURN1(WASM_ZERO)), \
1916 : WASM_SET_LOCAL(one_one, \
1917 : WASM_SIMD_I##format##_REPLACE_LANE( \
1918 : lanes - 1, WASM_GET_LOCAL(zero), WASM_ONE)), \
1919 : WASM_SET_LOCAL( \
1920 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AnyTrue, \
1921 : WASM_SIMD_BINOP(kExprI##format##Eq, \
1922 : WASM_GET_LOCAL(one_one), \
1923 : WASM_GET_LOCAL(zero)))), \
1924 : WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1925 : WASM_RETURN1(WASM_ZERO)), \
1926 : WASM_SET_LOCAL( \
1927 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AnyTrue, \
1928 : WASM_SIMD_BINOP(kExprI##format##Ne, \
1929 : WASM_GET_LOCAL(one_one), \
1930 : WASM_GET_LOCAL(zero)))), \
1931 : WASM_IF(WASM_I32_EQ(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1932 : WASM_RETURN1(WASM_ZERO)), \
1933 : WASM_SET_LOCAL( \
1934 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AllTrue, \
1935 : WASM_SIMD_BINOP(kExprI##format##Eq, \
1936 : WASM_GET_LOCAL(one_one), \
1937 : WASM_GET_LOCAL(zero)))), \
1938 : WASM_IF(WASM_I32_NE(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1939 : WASM_RETURN1(WASM_ZERO)), \
1940 : WASM_SET_LOCAL( \
1941 : reduced, WASM_SIMD_UNOP(kExprS1x##lanes##AllTrue, \
1942 : WASM_SIMD_BINOP(kExprI##format##Ne, \
1943 : WASM_GET_LOCAL(one_one), \
1944 : WASM_GET_LOCAL(zero)))), \
1945 : WASM_IF(WASM_I32_NE(WASM_GET_LOCAL(reduced), WASM_ZERO), \
1946 : WASM_RETURN1(WASM_ZERO)), \
1947 : WASM_ONE); \
1948 : CHECK_EQ(1, r.Call()); \
1949 : }
1950 :
1951 26123 : WASM_SIMD_BOOL_REDUCTION_TEST(32x4, 4)
1952 26123 : WASM_SIMD_BOOL_REDUCTION_TEST(16x8, 8)
1953 26123 : WASM_SIMD_BOOL_REDUCTION_TEST(8x16, 16)
1954 :
1955 26111 : WASM_SIMD_TEST(SimdI32x4ExtractWithF32x4) {
1956 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1957 12 : BUILD(r, WASM_IF_ELSE_I(
1958 : WASM_I32_EQ(WASM_SIMD_I32x4_EXTRACT_LANE(
1959 : 0, WASM_SIMD_F32x4_SPLAT(WASM_F32(30.5))),
1960 : WASM_I32_REINTERPRET_F32(WASM_F32(30.5))),
1961 : WASM_I32V(1), WASM_I32V(0)));
1962 12 : CHECK_EQ(1, r.Call());
1963 12 : }
1964 :
1965 26111 : WASM_SIMD_TEST(SimdF32x4ExtractWithI32x4) {
1966 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1967 12 : BUILD(r,
1968 : WASM_IF_ELSE_I(WASM_F32_EQ(WASM_SIMD_F32x4_EXTRACT_LANE(
1969 : 0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(15))),
1970 : WASM_F32_REINTERPRET_I32(WASM_I32V(15))),
1971 : WASM_I32V(1), WASM_I32V(0)));
1972 12 : CHECK_EQ(1, r.Call());
1973 12 : }
1974 :
1975 26111 : WASM_SIMD_TEST(SimdF32x4AddWithI32x4) {
1976 : // Choose two floating point values whose sum is normal and exactly
1977 : // representable as a float.
1978 : const int kOne = 0x3F800000;
1979 : const int kTwo = 0x40000000;
1980 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1981 12 : BUILD(r,
1982 : WASM_IF_ELSE_I(
1983 : WASM_F32_EQ(
1984 : WASM_SIMD_F32x4_EXTRACT_LANE(
1985 : 0, WASM_SIMD_BINOP(kExprF32x4Add,
1986 : WASM_SIMD_I32x4_SPLAT(WASM_I32V(kOne)),
1987 : WASM_SIMD_I32x4_SPLAT(WASM_I32V(kTwo)))),
1988 : WASM_F32_ADD(WASM_F32_REINTERPRET_I32(WASM_I32V(kOne)),
1989 : WASM_F32_REINTERPRET_I32(WASM_I32V(kTwo)))),
1990 : WASM_I32V(1), WASM_I32V(0)));
1991 12 : CHECK_EQ(1, r.Call());
1992 12 : }
1993 :
1994 26111 : WASM_SIMD_TEST(SimdI32x4AddWithF32x4) {
1995 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
1996 12 : BUILD(r,
1997 : WASM_IF_ELSE_I(
1998 : WASM_I32_EQ(
1999 : WASM_SIMD_I32x4_EXTRACT_LANE(
2000 : 0, WASM_SIMD_BINOP(kExprI32x4Add,
2001 : WASM_SIMD_F32x4_SPLAT(WASM_F32(21.25)),
2002 : WASM_SIMD_F32x4_SPLAT(WASM_F32(31.5)))),
2003 : WASM_I32_ADD(WASM_I32_REINTERPRET_F32(WASM_F32(21.25)),
2004 : WASM_I32_REINTERPRET_F32(WASM_F32(31.5)))),
2005 : WASM_I32V(1), WASM_I32V(0)));
2006 12 : CHECK_EQ(1, r.Call());
2007 12 : }
2008 :
2009 26111 : WASM_SIMD_TEST(SimdI32x4Local) {
2010 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
2011 : r.AllocateLocal(kWasmS128);
2012 12 : BUILD(r, WASM_SET_LOCAL(0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(31))),
2013 :
2014 : WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_LOCAL(0)));
2015 12 : CHECK_EQ(31, r.Call());
2016 12 : }
2017 :
2018 26111 : WASM_SIMD_TEST(SimdI32x4SplatFromExtract) {
2019 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
2020 : r.AllocateLocal(kWasmI32);
2021 : r.AllocateLocal(kWasmS128);
2022 12 : BUILD(r, WASM_SET_LOCAL(0, WASM_SIMD_I32x4_EXTRACT_LANE(
2023 : 0, WASM_SIMD_I32x4_SPLAT(WASM_I32V(76)))),
2024 : WASM_SET_LOCAL(1, WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(0))),
2025 : WASM_SIMD_I32x4_EXTRACT_LANE(1, WASM_GET_LOCAL(1)));
2026 12 : CHECK_EQ(76, r.Call());
2027 12 : }
2028 :
2029 26111 : WASM_SIMD_TEST(SimdI32x4For) {
2030 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
2031 : r.AllocateLocal(kWasmI32);
2032 : r.AllocateLocal(kWasmS128);
2033 12 : BUILD(r,
2034 :
2035 : WASM_SET_LOCAL(1, WASM_SIMD_I32x4_SPLAT(WASM_I32V(31))),
2036 : WASM_SET_LOCAL(1, WASM_SIMD_I32x4_REPLACE_LANE(1, WASM_GET_LOCAL(1),
2037 : WASM_I32V(53))),
2038 : WASM_SET_LOCAL(1, WASM_SIMD_I32x4_REPLACE_LANE(2, WASM_GET_LOCAL(1),
2039 : WASM_I32V(23))),
2040 : WASM_SET_LOCAL(0, WASM_I32V(0)),
2041 : WASM_LOOP(
2042 : WASM_SET_LOCAL(
2043 : 1, WASM_SIMD_BINOP(kExprI32x4Add, WASM_GET_LOCAL(1),
2044 : WASM_SIMD_I32x4_SPLAT(WASM_I32V(1)))),
2045 : WASM_IF(WASM_I32_NE(WASM_INC_LOCAL(0), WASM_I32V(5)), WASM_BR(1))),
2046 : WASM_SET_LOCAL(0, WASM_I32V(1)),
2047 : WASM_IF(WASM_I32_NE(WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_LOCAL(1)),
2048 : WASM_I32V(36)),
2049 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2050 : WASM_IF(WASM_I32_NE(WASM_SIMD_I32x4_EXTRACT_LANE(1, WASM_GET_LOCAL(1)),
2051 : WASM_I32V(58)),
2052 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2053 : WASM_IF(WASM_I32_NE(WASM_SIMD_I32x4_EXTRACT_LANE(2, WASM_GET_LOCAL(1)),
2054 : WASM_I32V(28)),
2055 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2056 : WASM_IF(WASM_I32_NE(WASM_SIMD_I32x4_EXTRACT_LANE(3, WASM_GET_LOCAL(1)),
2057 : WASM_I32V(36)),
2058 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2059 : WASM_GET_LOCAL(0));
2060 12 : CHECK_EQ(1, r.Call());
2061 12 : }
2062 :
2063 26111 : WASM_SIMD_TEST(SimdF32x4For) {
2064 12 : WasmRunner<int32_t> r(execution_tier, lower_simd);
2065 : r.AllocateLocal(kWasmI32);
2066 : r.AllocateLocal(kWasmS128);
2067 12 : BUILD(r, WASM_SET_LOCAL(1, WASM_SIMD_F32x4_SPLAT(WASM_F32(21.25))),
2068 : WASM_SET_LOCAL(1, WASM_SIMD_F32x4_REPLACE_LANE(3, WASM_GET_LOCAL(1),
2069 : WASM_F32(19.5))),
2070 : WASM_SET_LOCAL(0, WASM_I32V(0)),
2071 : WASM_LOOP(
2072 : WASM_SET_LOCAL(
2073 : 1, WASM_SIMD_BINOP(kExprF32x4Add, WASM_GET_LOCAL(1),
2074 : WASM_SIMD_F32x4_SPLAT(WASM_F32(2.0)))),
2075 : WASM_IF(WASM_I32_NE(WASM_INC_LOCAL(0), WASM_I32V(3)), WASM_BR(1))),
2076 : WASM_SET_LOCAL(0, WASM_I32V(1)),
2077 : WASM_IF(WASM_F32_NE(WASM_SIMD_F32x4_EXTRACT_LANE(0, WASM_GET_LOCAL(1)),
2078 : WASM_F32(27.25)),
2079 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2080 : WASM_IF(WASM_F32_NE(WASM_SIMD_F32x4_EXTRACT_LANE(3, WASM_GET_LOCAL(1)),
2081 : WASM_F32(25.5)),
2082 : WASM_SET_LOCAL(0, WASM_I32V(0))),
2083 : WASM_GET_LOCAL(0));
2084 12 : CHECK_EQ(1, r.Call());
2085 12 : }
2086 :
2087 : template <typename T, int numLanes = 4>
2088 24 : void SetVectorByLanes(T* v, const std::array<T, numLanes>& arr) {
2089 216 : for (int lane = 0; lane < numLanes; lane++) {
2090 96 : WriteLittleEndianValue<T>(&v[lane], arr[lane]);
2091 : }
2092 24 : }
2093 :
2094 : template <typename T>
2095 : const T GetScalar(T* v, int lane) {
2096 : constexpr int kElems = kSimd128Size / sizeof(T);
2097 : const int index = lane;
2098 : USE(kElems);
2099 : DCHECK(index >= 0 && index < kElems);
2100 : return ReadLittleEndianValue<T>(&v[index]);
2101 : }
2102 :
2103 26111 : WASM_SIMD_TEST(SimdI32x4GetGlobal) {
2104 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
2105 : // Pad the globals with a few unused slots to get a non-zero offset.
2106 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2107 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2108 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2109 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2110 : int32_t* global = r.builder().AddGlobal<int32_t>(kWasmS128);
2111 12 : SetVectorByLanes(global, {{0, 1, 2, 3}});
2112 : r.AllocateLocal(kWasmI32);
2113 12 : BUILD(
2114 : r, WASM_SET_LOCAL(1, WASM_I32V(1)),
2115 : WASM_IF(WASM_I32_NE(WASM_I32V(0),
2116 : WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_GLOBAL(4))),
2117 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2118 : WASM_IF(WASM_I32_NE(WASM_I32V(1),
2119 : WASM_SIMD_I32x4_EXTRACT_LANE(1, WASM_GET_GLOBAL(4))),
2120 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2121 : WASM_IF(WASM_I32_NE(WASM_I32V(2),
2122 : WASM_SIMD_I32x4_EXTRACT_LANE(2, WASM_GET_GLOBAL(4))),
2123 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2124 : WASM_IF(WASM_I32_NE(WASM_I32V(3),
2125 : WASM_SIMD_I32x4_EXTRACT_LANE(3, WASM_GET_GLOBAL(4))),
2126 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2127 : WASM_GET_LOCAL(1));
2128 12 : CHECK_EQ(1, r.Call(0));
2129 12 : }
2130 :
2131 26111 : WASM_SIMD_TEST(SimdI32x4SetGlobal) {
2132 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
2133 : // Pad the globals with a few unused slots to get a non-zero offset.
2134 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2135 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2136 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2137 : r.builder().AddGlobal<int32_t>(kWasmI32); // purposefully unused
2138 : int32_t* global = r.builder().AddGlobal<int32_t>(kWasmS128);
2139 12 : BUILD(r, WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_SPLAT(WASM_I32V(23))),
2140 : WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(1, WASM_GET_GLOBAL(4),
2141 : WASM_I32V(34))),
2142 : WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(2, WASM_GET_GLOBAL(4),
2143 : WASM_I32V(45))),
2144 : WASM_SET_GLOBAL(4, WASM_SIMD_I32x4_REPLACE_LANE(3, WASM_GET_GLOBAL(4),
2145 : WASM_I32V(56))),
2146 : WASM_I32V(1));
2147 12 : CHECK_EQ(1, r.Call(0));
2148 12 : CHECK_EQ(GetScalar(global, 0), 23);
2149 12 : CHECK_EQ(GetScalar(global, 1), 34);
2150 12 : CHECK_EQ(GetScalar(global, 2), 45);
2151 12 : CHECK_EQ(GetScalar(global, 3), 56);
2152 12 : }
2153 :
2154 26111 : WASM_SIMD_TEST(SimdF32x4GetGlobal) {
2155 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
2156 : float* global = r.builder().AddGlobal<float>(kWasmS128);
2157 12 : SetVectorByLanes<float>(global, {{0.0, 1.5, 2.25, 3.5}});
2158 : r.AllocateLocal(kWasmI32);
2159 12 : BUILD(
2160 : r, WASM_SET_LOCAL(1, WASM_I32V(1)),
2161 : WASM_IF(WASM_F32_NE(WASM_F32(0.0),
2162 : WASM_SIMD_F32x4_EXTRACT_LANE(0, WASM_GET_GLOBAL(0))),
2163 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2164 : WASM_IF(WASM_F32_NE(WASM_F32(1.5),
2165 : WASM_SIMD_F32x4_EXTRACT_LANE(1, WASM_GET_GLOBAL(0))),
2166 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2167 : WASM_IF(WASM_F32_NE(WASM_F32(2.25),
2168 : WASM_SIMD_F32x4_EXTRACT_LANE(2, WASM_GET_GLOBAL(0))),
2169 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2170 : WASM_IF(WASM_F32_NE(WASM_F32(3.5),
2171 : WASM_SIMD_F32x4_EXTRACT_LANE(3, WASM_GET_GLOBAL(0))),
2172 : WASM_SET_LOCAL(1, WASM_I32V(0))),
2173 : WASM_GET_LOCAL(1));
2174 12 : CHECK_EQ(1, r.Call(0));
2175 12 : }
2176 :
2177 26111 : WASM_SIMD_TEST(SimdF32x4SetGlobal) {
2178 12 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
2179 : float* global = r.builder().AddGlobal<float>(kWasmS128);
2180 12 : BUILD(r, WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_SPLAT(WASM_F32(13.5))),
2181 : WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_REPLACE_LANE(1, WASM_GET_GLOBAL(0),
2182 : WASM_F32(45.5))),
2183 : WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_REPLACE_LANE(2, WASM_GET_GLOBAL(0),
2184 : WASM_F32(32.25))),
2185 : WASM_SET_GLOBAL(0, WASM_SIMD_F32x4_REPLACE_LANE(3, WASM_GET_GLOBAL(0),
2186 : WASM_F32(65.0))),
2187 : WASM_I32V(1));
2188 12 : CHECK_EQ(1, r.Call(0));
2189 12 : CHECK_EQ(GetScalar(global, 0), 13.5f);
2190 12 : CHECK_EQ(GetScalar(global, 1), 45.5f);
2191 12 : CHECK_EQ(GetScalar(global, 2), 32.25f);
2192 12 : CHECK_EQ(GetScalar(global, 3), 65.0f);
2193 12 : }
2194 :
2195 26095 : WASM_SIMD_COMPILED_TEST(SimdLoadStoreLoad) {
2196 8 : WasmRunner<int32_t> r(execution_tier, lower_simd);
2197 : int32_t* memory =
2198 : r.builder().AddMemoryElems<int32_t>(kWasmPageSize / sizeof(int32_t));
2199 : // Load memory, store it, then reload it and extract the first lane. Use a
2200 : // non-zero offset into the memory of 1 lane (4 bytes) to test indexing.
2201 8 : BUILD(r, WASM_SIMD_STORE_MEM(WASM_I32V(4), WASM_SIMD_LOAD_MEM(WASM_I32V(4))),
2202 : WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_SIMD_LOAD_MEM(WASM_I32V(4))));
2203 :
2204 936 : FOR_INT32_INPUTS(i) {
2205 : int32_t expected = i;
2206 464 : r.builder().WriteMemory(&memory[1], expected);
2207 464 : CHECK_EQ(expected, r.Call());
2208 : }
2209 8 : }
2210 :
2211 : #if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
2212 : // V8:8665 - Tracking bug to enable reduction tests in the interpreter,
2213 : // and for SIMD lowering.
2214 : // TODO(gdeepti): Enable these tests for ARM/ARM64
2215 : #define WASM_SIMD_ANYTRUE_TEST(format, lanes, max) \
2216 : WASM_SIMD_TEST_TURBOFAN(S##format##AnyTrue) { \
2217 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd); \
2218 : byte simd = r.AllocateLocal(kWasmS128); \
2219 : BUILD( \
2220 : r, \
2221 : WASM_SET_LOCAL(simd, WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(0))), \
2222 : WASM_SIMD_UNOP(kExprS1x##lanes##AnyTrue, WASM_GET_LOCAL(simd))); \
2223 : DCHECK_EQ(1, r.Call(max)); \
2224 : DCHECK_EQ(1, r.Call(5)); \
2225 : DCHECK_EQ(0, r.Call(0)); \
2226 : }
2227 26083 : WASM_SIMD_ANYTRUE_TEST(32x4, 4, 0xffffffff)
2228 26083 : WASM_SIMD_ANYTRUE_TEST(16x8, 8, 0xffff)
2229 26083 : WASM_SIMD_ANYTRUE_TEST(8x16, 16, 0xff)
2230 :
2231 : #define WASM_SIMD_ALLTRUE_TEST(format, lanes, max) \
2232 : WASM_SIMD_TEST_TURBOFAN(S##format##AllTrue) { \
2233 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd); \
2234 : byte simd = r.AllocateLocal(kWasmS128); \
2235 : BUILD( \
2236 : r, \
2237 : WASM_SET_LOCAL(simd, WASM_SIMD_I##format##_SPLAT(WASM_GET_LOCAL(0))), \
2238 : WASM_SIMD_UNOP(kExprS1x##lanes##AllTrue, WASM_GET_LOCAL(simd))); \
2239 : DCHECK_EQ(1, r.Call(max)); \
2240 : DCHECK_EQ(0, r.Call(21)); \
2241 : DCHECK_EQ(0, r.Call(0)); \
2242 : }
2243 26083 : WASM_SIMD_ALLTRUE_TEST(32x4, 4, 0xffffffff)
2244 26083 : WASM_SIMD_ALLTRUE_TEST(16x8, 8, 0xffff)
2245 26083 : WASM_SIMD_ALLTRUE_TEST(8x16, 16, 0xff)
2246 : #endif // V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_IA32
2247 :
2248 26079 : WASM_SIMD_TEST_TURBOFAN(BitSelect) {
2249 4 : WasmRunner<int32_t, int32_t> r(execution_tier, lower_simd);
2250 : byte simd = r.AllocateLocal(kWasmS128);
2251 4 : BUILD(r,
2252 : WASM_SET_LOCAL(
2253 : simd,
2254 : WASM_SIMD_SELECT(32x4, WASM_SIMD_I32x4_SPLAT(WASM_I32V(0x01020304)),
2255 : WASM_SIMD_I32x4_SPLAT(WASM_I32V(0)),
2256 : WASM_SIMD_I32x4_SPLAT(WASM_GET_LOCAL(0)))),
2257 : WASM_SIMD_I32x4_EXTRACT_LANE(0, WASM_GET_LOCAL(simd)));
2258 : DCHECK_EQ(0x01020304, r.Call(0xFFFFFFFF));
2259 4 : }
2260 :
2261 : #undef WASM_SIMD_TEST
2262 : #undef WASM_SIMD_CHECK_LANE
2263 : #undef TO_BYTE
2264 : #undef WASM_SIMD_OP
2265 : #undef WASM_SIMD_SPLAT
2266 : #undef WASM_SIMD_UNOP
2267 : #undef WASM_SIMD_BINOP
2268 : #undef WASM_SIMD_SHIFT_OP
2269 : #undef WASM_SIMD_CONCAT_OP
2270 : #undef WASM_SIMD_SELECT
2271 : #undef WASM_SIMD_F32x4_SPLAT
2272 : #undef WASM_SIMD_F32x4_EXTRACT_LANE
2273 : #undef WASM_SIMD_F32x4_REPLACE_LANE
2274 : #undef WASM_SIMD_I32x4_SPLAT
2275 : #undef WASM_SIMD_I32x4_EXTRACT_LANE
2276 : #undef WASM_SIMD_I32x4_REPLACE_LANE
2277 : #undef WASM_SIMD_I16x8_SPLAT
2278 : #undef WASM_SIMD_I16x8_EXTRACT_LANE
2279 : #undef WASM_SIMD_I16x8_REPLACE_LANE
2280 : #undef WASM_SIMD_I8x16_SPLAT
2281 : #undef WASM_SIMD_I8x16_EXTRACT_LANE
2282 : #undef WASM_SIMD_I8x16_REPLACE_LANE
2283 : #undef WASM_SIMD_S8x16_SHUFFLE_OP
2284 : #undef WASM_SIMD_LOAD_MEM
2285 : #undef WASM_SIMD_STORE_MEM
2286 : #undef WASM_SIMD_SELECT_TEST
2287 : #undef WASM_SIMD_NON_CANONICAL_SELECT_TEST
2288 : #undef WASM_SIMD_COMPILED_TEST
2289 : #undef WASM_SIMD_BOOL_REDUCTION_TEST
2290 : #undef WASM_SIMD_TEST_TURBOFAN
2291 : #undef WASM_SIMD_ANYTRUE_TEST
2292 : #undef WASM_SIMD_ALLTRUE_TEST
2293 :
2294 : } // namespace test_run_wasm_simd
2295 : } // namespace wasm
2296 : } // namespace internal
2297 78189 : } // namespace v8
|