17 if (!input.saturated_use_count.IsOne()) {
23 static constexpr std::array low_half_ops = {
24 Simd128UnaryOp::Kind::kI16x8SConvertI8x16Low,
25 Simd128UnaryOp::Kind::kI16x8UConvertI8x16Low,
26 Simd128UnaryOp::Kind::kI32x4SConvertI16x8Low,
27 Simd128UnaryOp::Kind::kI32x4UConvertI16x8Low,
28 Simd128UnaryOp::Kind::kI64x2SConvertI32x4Low,
29 Simd128UnaryOp::Kind::kI64x2UConvertI32x4Low,
32 for (
auto const kind : low_half_ops) {
33 if (
kind == unop.kind) {
35 lanes >>= lanes.count() / 2;
47 static constexpr std::array low_half_ops = {
48 Simd128BinopOp::Kind::kI16x8ExtMulLowI8x16S,
49 Simd128BinopOp::Kind::kI16x8ExtMulLowI8x16U,
50 Simd128BinopOp::Kind::kI32x4ExtMulLowI16x8S,
51 Simd128BinopOp::Kind::kI32x4ExtMulLowI16x8U,
52 Simd128BinopOp::Kind::kI64x2ExtMulLowI32x4S,
53 Simd128BinopOp::Kind::kI64x2ExtMulLowI32x4U,
57 for (
auto const&
kind : low_half_ops) {
58 if (
kind == binop.kind) {
60 lanes >>= lanes.count() / 2;
64 if (right.saturated_use_count.IsOne()) {
72 if (
auto* unop = op->
TryCast<Simd128UnaryOp>()) {
74 }
else if (
auto* binop = op->
TryCast<Simd128BinopOp>()) {
76 }
else if (
auto* shuffle = op->
TryCast<Simd128ShuffleOp>()) {
82 for (uint32_t processed =
input_graph().block_count(); processed > 0;
86 auto idx_range =
input_graph().OperationIndices(block);
87 for (
auto it = idx_range.rbegin(); it != idx_range.rend(); ++it) {
99 if (
auto* unop = op.
TryCast<Simd128UnaryOp>()) {
104 if (
auto* binop = op.
TryCast<Simd128BinopOp>()) {
109 if (
auto* shuffle_op = op.
TryCast<Simd128ShuffleOp>()) {
124 const Simd128ShuffleOp& shuffle_op,
const Simd128ShuffleOp& shuffle,
125 uint8_t lower_limit, uint8_t upper_limit) {
145 struct ShuffleHelper {
146 explicit ShuffleHelper(
const uint8_t* shuffle) : shuffle(shuffle) {}
148 const uint8_t*
begin()
const {
return shuffle; }
150 const uint8_t* midpoint()
const {
152 return shuffle + half_lanes;
157 const uint8_t* shuffle;
160 ShuffleHelper view(shuffle.shuffle);
163 auto all_low_half = [&view](uint8_t lower_limit, uint8_t upper_limit) {
164 return std::all_of(view.begin(), view.midpoint(),
165 [lower_limit, upper_limit](uint8_t
i) {
166 return i >= lower_limit && i <= upper_limit;
170 auto all_high_half = [&view](uint8_t lower_limit, uint8_t upper_limit) {
171 return std::all_of(view.midpoint(), view.end(),
172 [lower_limit, upper_limit](uint8_t
i) {
173 return i >= lower_limit && i <= upper_limit;
178 auto none_low_half = [&view](uint8_t lower_limit, uint8_t upper_limit) {
179 return std::none_of(view.begin(), view.midpoint(),
180 [lower_limit, upper_limit](uint8_t
i) {
181 return i >= lower_limit && i <= upper_limit;
186 auto none_high_half = [&view](uint8_t lower_limit, uint8_t upper_limit) {
187 return std::none_of(view.midpoint(), view.end(),
188 [lower_limit, upper_limit](uint8_t
i) {
189 return i >= lower_limit && i <= upper_limit;
197 bool shf_into_low_half = all_low_half(lower_limit, upper_limit) &&
198 none_high_half(lower_limit, upper_limit);
199 bool shf_into_high_half = all_high_half(lower_limit, upper_limit) &&
200 none_low_half(lower_limit, upper_limit);
201 DCHECK(!(shf_into_low_half && shf_into_high_half));
204 if (shf_into_low_half) {
205 if (all_low_half(lower_limit + quarter_lanes, upper_limit)) {
211 }
else if (all_low_half(lower_limit, upper_limit - quarter_lanes)) {
216 }
else if (shf_into_high_half) {
217 if (all_high_half(lower_limit + quarter_lanes, upper_limit)) {
223 }
else if (all_high_half(lower_limit, upper_limit - quarter_lanes)) {
231#if V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
232bool WasmShuffleAnalyzer::CouldLoadPair(
const LoadOp& load0,
233 const LoadOp& load1)
const {
261 auto CanReschedule = [
this](
OpIndex first_idx,
OpIndex second_idx) {
263 OpEffects second_effects =
second.Effects();
265 bool second_is_protected_load =
second.IsProtectedLoad();
267 while (prev_idx != first_idx) {
269 OpEffects prev_effects = prev_op.Effects();
270 bool both_protected =
271 second_is_protected_load && prev_op.IsProtectedLoad();
272 if (both_protected &&
285 if (first_idx.offset() > second_idx.offset()) {
286 std::swap(first_idx, second_idx);
289 return CanReschedule(first_idx, second_idx);
292void WasmShuffleAnalyzer::AddLoadMultipleCandidate(
293 const Simd128ShuffleOp& even_shuffle,
const Simd128ShuffleOp& odd_shuffle,
294 const LoadOp& left,
const LoadOp& right,
295 Simd128LoadPairDeinterleaveOp::Kind
kind) {
298 if (CouldLoadPair(left, right)) {
299 deinterleave_load_candidates_.emplace_back(
kind, left, right, even_shuffle,
304void WasmShuffleAnalyzer::ProcessShuffleOfLoads(
const Simd128ShuffleOp& shuffle,
306 const LoadOp& right) {
311 if (left.saturated_use_count.Get() != 2 ||
312 right.saturated_use_count.Get() != 2) {
320 auto AddShuffle = [
this, &shuffle, &left, &right](
323 Simd128LoadPairDeinterleaveOp::Kind
kind) {
324 shuffles.push_back(&shuffle);
325 if (
auto other_shuffle = GetOtherShuffleUser(left, right, other_shuffles)) {
327 AddLoadMultipleCandidate(shuffle, *other_shuffle.value(), left, right,
330 AddLoadMultipleCandidate(*other_shuffle.value(), shuffle, left, right,
339 std::copy_n(shuffle.shuffle,
kSimd128Size, shuffle_bytes.begin());
345 AddShuffle(even_64x2_shuffles_, odd_64x2_shuffles_,
true,
346 Simd128LoadPairDeinterleaveOp::Kind::k64x4);
349 AddShuffle(odd_64x2_shuffles_, even_64x2_shuffles_,
false,
350 Simd128LoadPairDeinterleaveOp::Kind::k64x4);
353 AddShuffle(even_32x4_shuffles_, odd_32x4_shuffles_,
true,
354 Simd128LoadPairDeinterleaveOp::Kind::k32x8);
357 AddShuffle(odd_32x4_shuffles_, even_32x4_shuffles_,
false,
358 Simd128LoadPairDeinterleaveOp::Kind::k32x8);
361 AddShuffle(even_16x8_shuffles_, odd_16x8_shuffles_,
true,
362 Simd128LoadPairDeinterleaveOp::Kind::k16x16);
365 AddShuffle(odd_16x8_shuffles_, even_16x8_shuffles_,
false,
366 Simd128LoadPairDeinterleaveOp::Kind::k16x16);
369 AddShuffle(even_8x16_shuffles_, odd_8x16_shuffles_,
true,
370 Simd128LoadPairDeinterleaveOp::Kind::k8x32);
373 AddShuffle(odd_8x16_shuffles_, even_8x16_shuffles_,
false,
374 Simd128LoadPairDeinterleaveOp::Kind::k8x32);
382 if (shuffle.kind != Simd128ShuffleOp::Kind::kI8x16) {
388#if V8_ENABLE_WASM_DEINTERLEAVED_MEM_OPS
394 if (load_left && load_right) {
395 ProcessShuffleOfLoads(shuffle, *load_left, *load_right);
400 auto* shuffle_left = left.
TryCast<Simd128ShuffleOp>();
401 auto* shuffle_right = right.TryCast<Simd128ShuffleOp>();
402 if (!shuffle_left && !shuffle_right) {
405 constexpr uint8_t left_lower = 0;
406 constexpr uint8_t left_upper = 15;
407 constexpr uint8_t right_lower = 16;
408 constexpr uint8_t right_upper = 31;
409 if (shuffle_left && shuffle_left->kind == Simd128ShuffleOp::Kind::kI8x16 &&
410 shuffle_left->saturated_use_count.IsOne()) {
413 if (shuffle_right && shuffle_right->kind == Simd128ShuffleOp::Kind::kI8x16 &&
414 shuffle_right->saturated_use_count.IsOne()) {
T & emplace_back(Args &&... args)
static constexpr uint16_t k8x16
ZoneUnorderedSet< const Operation * > visited_
DemandedElementMap demanded_elements_
bool Visited(const Operation *op) const
static constexpr uint16_t k8x8Low
std::bitset< 16 > LaneBitSet
const Graph & input_graph() const
void RecordOp(const Operation *op, LaneBitSet lanes)
void AddUnaryOp(const Simd128UnaryOp &unop, LaneBitSet lanes)
void AddBinaryOp(const Simd128BinopOp &binop, LaneBitSet lanes)
static constexpr uint16_t k8x4Low
std::optional< DemandedElementAnalysis::LaneBitSet > DemandedByteLanes(const Operation *op) const
SmallShuffleVector shift_shuffles_
void ProcessUnary(const Simd128UnaryOp &unop)
const Graph & input_graph() const
SmallShuffleVector low_half_shuffles_
V8_EXPORT_PRIVATE void Run()
SmallShuffleVector high_half_shuffles_
void ProcessShuffle(const Simd128ShuffleOp &shuffle_op)
void ProcessBinary(const Simd128BinopOp &binop)
DemandedElementAnalysis demanded_element_analysis
void ProcessShuffleOfShuffle(const Simd128ShuffleOp &shuffle_op, const Simd128ShuffleOp &shuffle, uint8_t lower_limit, uint8_t upper_limit)
void Process(const Operation &op)
static CanonicalShuffle TryMatchCanonical(const ShuffleArray &shuffle)
std::array< uint8_t, kSimd128Size > ShuffleArray
V8_EXPORT_PRIVATE V8_INLINE bool ShouldSkipOperation(const Operation &op)
bool CannotSwapProtectedLoads(OpEffects first, OpEffects second)
bool CannotSwapOperations(OpEffects first, OpEffects second)
SmallZoneVector< const Simd128ShuffleOp *, 8 > SmallShuffleVector
Node::Uses::const_iterator begin(const Node::Uses &uses)
constexpr int kSimd128Size
#define DCHECK_NE(v1, v2)
#define DCHECK(condition)
#define DCHECK_EQ(v1, v2)
OptionalOpIndex index() const
SaturatedUint8 saturated_use_count
const underlying_operation_t< Op > * TryCast() const