• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 "src/wasm/simd-shuffle.h"
6 
7 #include <algorithm>
8 
9 #include "src/common/globals.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14 
CanonicalizeShuffle(bool inputs_equal,uint8_t * shuffle,bool * needs_swap,bool * is_swizzle)15 void SimdShuffle::CanonicalizeShuffle(bool inputs_equal, uint8_t* shuffle,
16                                       bool* needs_swap, bool* is_swizzle) {
17   *needs_swap = false;
18   // Inputs equal, then it's a swizzle.
19   if (inputs_equal) {
20     *is_swizzle = true;
21   } else {
22     // Inputs are distinct; check that both are required.
23     bool src0_is_used = false;
24     bool src1_is_used = false;
25     for (int i = 0; i < kSimd128Size; ++i) {
26       if (shuffle[i] < kSimd128Size) {
27         src0_is_used = true;
28       } else {
29         src1_is_used = true;
30       }
31     }
32     if (src0_is_used && !src1_is_used) {
33       *is_swizzle = true;
34     } else if (src1_is_used && !src0_is_used) {
35       *needs_swap = true;
36       *is_swizzle = true;
37     } else {
38       *is_swizzle = false;
39       // Canonicalize general 2 input shuffles so that the first input lanes are
40       // encountered first. This makes architectural shuffle pattern matching
41       // easier, since we only need to consider 1 input ordering instead of 2.
42       if (shuffle[0] >= kSimd128Size) {
43         // The second operand is used first. Swap inputs and adjust the shuffle.
44         *needs_swap = true;
45         for (int i = 0; i < kSimd128Size; ++i) {
46           shuffle[i] ^= kSimd128Size;
47         }
48       }
49     }
50   }
51   if (*is_swizzle) {
52     for (int i = 0; i < kSimd128Size; ++i) shuffle[i] &= kSimd128Size - 1;
53   }
54 }
55 
TryMatchIdentity(const uint8_t * shuffle)56 bool SimdShuffle::TryMatchIdentity(const uint8_t* shuffle) {
57   for (int i = 0; i < kSimd128Size; ++i) {
58     if (shuffle[i] != i) return false;
59   }
60   return true;
61 }
62 
TryMatch32x4Rotate(const uint8_t * shuffle,uint8_t * shuffle32x4,bool is_swizzle)63 bool SimdShuffle::TryMatch32x4Rotate(const uint8_t* shuffle,
64                                      uint8_t* shuffle32x4, bool is_swizzle) {
65   uint8_t offset;
66   bool is_concat = TryMatchConcat(shuffle, &offset);
67   DCHECK_NE(offset, 0);  // 0 is identity, it should not be matched.
68   // Since we already have a concat shuffle, we know that the indices goes from:
69   // [ offset, ..., 15, 0, ... ], it suffices to check that the offset points
70   // to the low byte of a 32x4 element.
71   if (!is_concat || !is_swizzle || offset % 4 != 0) {
72     return false;
73   }
74 
75   uint8_t offset_32 = offset / 4;
76   for (int i = 0; i < 4; i++) {
77     shuffle32x4[i] = (offset_32 + i) % 4;
78   }
79   return true;
80 }
81 
TryMatch32x4Shuffle(const uint8_t * shuffle,uint8_t * shuffle32x4)82 bool SimdShuffle::TryMatch32x4Shuffle(const uint8_t* shuffle,
83                                       uint8_t* shuffle32x4) {
84   for (int i = 0; i < 4; ++i) {
85     if (shuffle[i * 4] % 4 != 0) return false;
86     for (int j = 1; j < 4; ++j) {
87       if (shuffle[i * 4 + j] - shuffle[i * 4 + j - 1] != 1) return false;
88     }
89     shuffle32x4[i] = shuffle[i * 4] / 4;
90   }
91   return true;
92 }
93 
TryMatch16x8Shuffle(const uint8_t * shuffle,uint8_t * shuffle16x8)94 bool SimdShuffle::TryMatch16x8Shuffle(const uint8_t* shuffle,
95                                       uint8_t* shuffle16x8) {
96   for (int i = 0; i < 8; ++i) {
97     if (shuffle[i * 2] % 2 != 0) return false;
98     for (int j = 1; j < 2; ++j) {
99       if (shuffle[i * 2 + j] - shuffle[i * 2 + j - 1] != 1) return false;
100     }
101     shuffle16x8[i] = shuffle[i * 2] / 2;
102   }
103   return true;
104 }
105 
TryMatchConcat(const uint8_t * shuffle,uint8_t * offset)106 bool SimdShuffle::TryMatchConcat(const uint8_t* shuffle, uint8_t* offset) {
107   // Don't match the identity shuffle (e.g. [0 1 2 ... 15]).
108   uint8_t start = shuffle[0];
109   if (start == 0) return false;
110   DCHECK_GT(kSimd128Size, start);  // The shuffle should be canonicalized.
111   // A concatenation is a series of consecutive indices, with at most one jump
112   // in the middle from the last lane to the first.
113   for (int i = 1; i < kSimd128Size; ++i) {
114     if ((shuffle[i]) != ((shuffle[i - 1] + 1))) {
115       if (shuffle[i - 1] != 15) return false;
116       if (shuffle[i] % kSimd128Size != 0) return false;
117     }
118   }
119   *offset = start;
120   return true;
121 }
122 
TryMatchBlend(const uint8_t * shuffle)123 bool SimdShuffle::TryMatchBlend(const uint8_t* shuffle) {
124   for (int i = 0; i < 16; ++i) {
125     if ((shuffle[i] & 0xF) != i) return false;
126   }
127   return true;
128 }
129 
PackShuffle4(uint8_t * shuffle)130 uint8_t SimdShuffle::PackShuffle4(uint8_t* shuffle) {
131   return (shuffle[0] & 3) | ((shuffle[1] & 3) << 2) | ((shuffle[2] & 3) << 4) |
132          ((shuffle[3] & 3) << 6);
133 }
134 
PackBlend8(const uint8_t * shuffle16x8)135 uint8_t SimdShuffle::PackBlend8(const uint8_t* shuffle16x8) {
136   int8_t result = 0;
137   for (int i = 0; i < 8; ++i) {
138     result |= (shuffle16x8[i] >= 8 ? 1 : 0) << i;
139   }
140   return result;
141 }
142 
PackBlend4(const uint8_t * shuffle32x4)143 uint8_t SimdShuffle::PackBlend4(const uint8_t* shuffle32x4) {
144   int8_t result = 0;
145   for (int i = 0; i < 4; ++i) {
146     result |= (shuffle32x4[i] >= 4 ? 0x3 : 0) << (i * 2);
147   }
148   return result;
149 }
150 
Pack4Lanes(const uint8_t * shuffle)151 int32_t SimdShuffle::Pack4Lanes(const uint8_t* shuffle) {
152   int32_t result = 0;
153   for (int i = 3; i >= 0; --i) {
154     result <<= 8;
155     result |= shuffle[i];
156   }
157   return result;
158 }
159 
Pack16Lanes(uint32_t * dst,const uint8_t * shuffle)160 void SimdShuffle::Pack16Lanes(uint32_t* dst, const uint8_t* shuffle) {
161   for (int i = 0; i < 4; i++) {
162     dst[i] = wasm::SimdShuffle::Pack4Lanes(shuffle + (i * 4));
163   }
164 }
165 
AllInRangeOrTopBitSet(std::array<uint8_t,kSimd128Size> shuffle)166 bool SimdSwizzle::AllInRangeOrTopBitSet(
167     std::array<uint8_t, kSimd128Size> shuffle) {
168   return std::all_of(shuffle.begin(), shuffle.end(),
169                      [](auto i) { return (i < kSimd128Size) || (i & 0x80); });
170 }
171 
172 }  // namespace wasm
173 }  // namespace internal
174 }  // namespace v8
175