// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/simd-shuffle.h" #include <algorithm> #include "src/common/globals.h" namespace v8 { namespace internal { namespace wasm { void SimdShuffle::CanonicalizeShuffle(bool inputs_equal, uint8_t* shuffle, bool* needs_swap, bool* is_swizzle) { *needs_swap = false; // Inputs equal, then it's a swizzle. if (inputs_equal) { *is_swizzle = true; } else { // Inputs are distinct; check that both are required. bool src0_is_used = false; bool src1_is_used = false; for (int i = 0; i < kSimd128Size; ++i) { if (shuffle[i] < kSimd128Size) { src0_is_used = true; } else { src1_is_used = true; } } if (src0_is_used && !src1_is_used) { *is_swizzle = true; } else if (src1_is_used && !src0_is_used) { *needs_swap = true; *is_swizzle = true; } else { *is_swizzle = false; // Canonicalize general 2 input shuffles so that the first input lanes are // encountered first. This makes architectural shuffle pattern matching // easier, since we only need to consider 1 input ordering instead of 2. if (shuffle[0] >= kSimd128Size) { // The second operand is used first. Swap inputs and adjust the shuffle. *needs_swap = true; for (int i = 0; i < kSimd128Size; ++i) { shuffle[i] ^= kSimd128Size; } } } } if (*is_swizzle) { for (int i = 0; i < kSimd128Size; ++i) shuffle[i] &= kSimd128Size - 1; } } bool SimdShuffle::TryMatchIdentity(const uint8_t* shuffle) { for (int i = 0; i < kSimd128Size; ++i) { if (shuffle[i] != i) return false; } return true; } bool SimdShuffle::TryMatch32x4Rotate(const uint8_t* shuffle, uint8_t* shuffle32x4, bool is_swizzle) { uint8_t offset; bool is_concat = TryMatchConcat(shuffle, &offset); DCHECK_NE(offset, 0); // 0 is identity, it should not be matched. // Since we already have a concat shuffle, we know that the indices goes from: // [ offset, ..., 15, 0, ... ], it suffices to check that the offset points // to the low byte of a 32x4 element. if (!is_concat || !is_swizzle || offset % 4 != 0) { return false; } uint8_t offset_32 = offset / 4; for (int i = 0; i < 4; i++) { shuffle32x4[i] = (offset_32 + i) % 4; } return true; } bool SimdShuffle::TryMatch32x4Shuffle(const uint8_t* shuffle, uint8_t* shuffle32x4) { for (int i = 0; i < 4; ++i) { if (shuffle[i * 4] % 4 != 0) return false; for (int j = 1; j < 4; ++j) { if (shuffle[i * 4 + j] - shuffle[i * 4 + j - 1] != 1) return false; } shuffle32x4[i] = shuffle[i * 4] / 4; } return true; } bool SimdShuffle::TryMatch16x8Shuffle(const uint8_t* shuffle, uint8_t* shuffle16x8) { for (int i = 0; i < 8; ++i) { if (shuffle[i * 2] % 2 != 0) return false; for (int j = 1; j < 2; ++j) { if (shuffle[i * 2 + j] - shuffle[i * 2 + j - 1] != 1) return false; } shuffle16x8[i] = shuffle[i * 2] / 2; } return true; } bool SimdShuffle::TryMatchConcat(const uint8_t* shuffle, uint8_t* offset) { // Don't match the identity shuffle (e.g. [0 1 2 ... 15]). uint8_t start = shuffle[0]; if (start == 0) return false; DCHECK_GT(kSimd128Size, start); // The shuffle should be canonicalized. // A concatenation is a series of consecutive indices, with at most one jump // in the middle from the last lane to the first. for (int i = 1; i < kSimd128Size; ++i) { if ((shuffle[i]) != ((shuffle[i - 1] + 1))) { if (shuffle[i - 1] != 15) return false; if (shuffle[i] % kSimd128Size != 0) return false; } } *offset = start; return true; } bool SimdShuffle::TryMatchBlend(const uint8_t* shuffle) { for (int i = 0; i < 16; ++i) { if ((shuffle[i] & 0xF) != i) return false; } return true; } uint8_t SimdShuffle::PackShuffle4(uint8_t* shuffle) { return (shuffle[0] & 3) | ((shuffle[1] & 3) << 2) | ((shuffle[2] & 3) << 4) | ((shuffle[3] & 3) << 6); } uint8_t SimdShuffle::PackBlend8(const uint8_t* shuffle16x8) { int8_t result = 0; for (int i = 0; i < 8; ++i) { result |= (shuffle16x8[i] >= 8 ? 1 : 0) << i; } return result; } uint8_t SimdShuffle::PackBlend4(const uint8_t* shuffle32x4) { int8_t result = 0; for (int i = 0; i < 4; ++i) { result |= (shuffle32x4[i] >= 4 ? 0x3 : 0) << (i * 2); } return result; } int32_t SimdShuffle::Pack4Lanes(const uint8_t* shuffle) { int32_t result = 0; for (int i = 3; i >= 0; --i) { result <<= 8; result |= shuffle[i]; } return result; } void SimdShuffle::Pack16Lanes(uint32_t* dst, const uint8_t* shuffle) { for (int i = 0; i < 4; i++) { dst[i] = wasm::SimdShuffle::Pack4Lanes(shuffle + (i * 4)); } } bool SimdSwizzle::AllInRangeOrTopBitSet( std::array<uint8_t, kSimd128Size> shuffle) { return std::all_of(shuffle.begin(), shuffle.end(), [](auto i) { return (i < kSimd128Size) || (i & 0x80); }); } } // namespace wasm } // namespace internal } // namespace v8