Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions g3doc/quick_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ false is zero, true has all bits set:

#### Compress

* <code>V **Compress**(V v, M m)</code>: returns `r` such that `r[n]` is
* <code>V **Compress**([D, ] V v, M m)</code>: returns `r` such that `r[n]` is
`v[i]`, with `i` the n-th lane index (starting from 0) where `m[i]` is true.
Compacts lanes whose mask is true into the lower lanes. For targets and lane
type `T` where `CompressIsPartition<T>::value` is true, the upper lanes are
Expand All @@ -1670,9 +1670,15 @@ false is zero, true has all bits set:
implementation-defined. Potentially slow with 8 and 16-bit lanes. Use this
form when the input is already a mask, e.g. returned by a comparison.

* <code>V **CompressNot**(V v, M m)</code>: equivalent to `Compress(v,
The optional `D` parameter allows more efficient `Compress` of partial
I8/U8/I16/U16/F16/BF16 vectors on SVE targets.

* <code>V **CompressNot**([D, ] V v, M m)</code>: equivalent to `Compress(v,
Not(m))` but possibly faster if `CompressIsPartition<T>::value` is true.

The optional `D` parameter allows more efficient `Compress` of partial
I8/U8/I16/U16/F16/BF16 vectors on SVE targets.

* `V`: `u64` \
<code>V **CompressBlocksNot**(V v, M m)</code>: equivalent to
`CompressNot(v, m)` when `m` is structured as adjacent pairs (both true or
Expand All @@ -1691,7 +1697,7 @@ false is zero, true has all bits set:
lanes, but there is no guarantee of atomicity because this may be
implemented as `Compress, LoadU, IfThenElse(FirstN), StoreU`.

* <code>V **CompressBits**(V v, const uint8_t* HWY_RESTRICT bits)</code>:
* <code>V **CompressBits**([D, ] V v, const uint8_t* HWY_RESTRICT bits)</code>:
Equivalent to, but often faster than `Compress(v, LoadMaskBits(d, bits))`.
`bits` is as specified for `LoadMaskBits`. If called multiple times, the
`bits` pointer passed to this function must also be marked `HWY_RESTRICT` to
Expand All @@ -1700,6 +1706,10 @@ false is zero, true has all bits set:
`Compress`, `CompressIsPartition` indicates the mask=false lanes are moved
to the upper lanes. Potentially slow with 8 and 16-bit lanes.

The optional `D` parameter allows `CompressBits(d, v, bits)` to be more
efficient than `CompressBits(v, bits)` for partial vectors on RVV and SVE
targets.

* <code>size_t **CompressBitsStore**(V v, const uint8_t* HWY_RESTRICT bits, D
d, T* p)</code>: combination of `CompressStore` and `CompressBits`, see
remarks there.
Expand Down
553 changes: 0 additions & 553 deletions hwy/ops/arm_neon-inl.h

Large diffs are not rendered by default.

149 changes: 109 additions & 40 deletions hwy/ops/arm_sve-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4486,6 +4486,18 @@ HWY_API V BroadcastBlock(V v) {

// ------------------------------ Compress (PromoteTo)

#ifdef HWY_NATIVE_COMPRESS8
#undef HWY_NATIVE_COMPRESS8
#else
#define HWY_NATIVE_COMPRESS8
#endif

#ifdef HWY_NATIVE_COMPRESS16_32_64
#undef HWY_NATIVE_COMPRESS16_32_64
#else
#define HWY_NATIVE_COMPRESS16_32_64
#endif

template <typename T>
struct CompressIsPartition {
#if HWY_TARGET == HWY_SVE_256 || HWY_TARGET == HWY_SVE2_128
Expand Down Expand Up @@ -4530,7 +4542,6 @@ HWY_API V Compress(V v, svbool_t mask) {
0, 1, 3, 2, 2, 3, 0, 1, 0, 2, 3, 1, 1, 2, 3, 0, 0, 1, 2, 3};
return TableLookupLanes(v, SetTableIndices(d, table + offset));
}

#endif // HWY_TARGET == HWY_SVE_256
#if HWY_TARGET == HWY_SVE2_128 || HWY_IDE
template <class V, HWY_IF_T_SIZE_V(V, 8)>
Expand All @@ -4543,53 +4554,90 @@ HWY_API V Compress(V v, svbool_t mask) {
const svbool_t maskLL = svzip1_b64(mask, mask); // broadcast lower lane
return detail::Splice(v, v, AndNot(maskLL, mask));
}
#endif // HWY_TARGET == HWY_SVE_256

#endif // HWY_TARGET == HWY_SVE2_128
template <class D, HWY_IF_T_SIZE_ONE_OF_D(D, (1 << 4) | (1 << 8))>
HWY_API VFromD<D> Compress(D /*d*/, VFromD<D> v, const svbool_t mask) {
return Compress(v, mask);
}

template <class V, HWY_IF_T_SIZE_V(V, 2)>
HWY_API V Compress(V v, svbool_t mask16) {
static_assert(!IsSame<V, svfloat16_t>(), "Must use overload");
const DFromV<V> d16;

// Promote vector and mask to 32-bit
const RepartitionToWide<decltype(d16)> dw;
const auto v32L = PromoteTo(dw, v);
const auto v32H = detail::PromoteUpperTo(dw, v);
const svbool_t mask32L = svunpklo_b(mask16);
const svbool_t mask32H = svunpkhi_b(mask16);

const auto compressedL = Compress(v32L, mask32L);
const auto compressedH = Compress(v32H, mask32H);

// Demote to 16-bit (already in range) - separately so we can splice
const V evenL = BitCast(d16, compressedL);
const V evenH = BitCast(d16, compressedH);
const V v16L = detail::ConcatEvenFull(evenL, evenL); // lower half
const V v16H = detail::ConcatEvenFull(evenH, evenH);
namespace detail {

template <class D>
static constexpr bool NeedToSplitForCompress8Or16() {
return (HWY_MAX_LANES_D(D) * sizeof(TFromD<D>)) >= HWY_MIN_BYTES &&
HWY_POW2_D(D) >= 0;
}

} // namespace detail

template <class D, HWY_IF_T_SIZE_ONE_OF_D(D, (1 << 1) | (1 << 2)),
hwy::EnableIf<
detail::NeedToSplitForCompress8Or16<RemoveCvRef<D>>()>* = nullptr>
HWY_API VFromD<D> Compress(D d, VFromD<D> v, const svbool_t mask) {
const DFromV<decltype(v)> d_full;
const RebindToUnsigned<decltype(d_full)> du;
const RepartitionToWide<decltype(du)> dw;

const auto vu = BitCast(du, v);
const auto vw_lo = PromoteTo(dw, vu);
const auto vw_hi = detail::PromoteUpperTo(dw, vu);

const auto mask_lo = svunpklo_b(mask);
const auto mask_hi = svunpkhi_b(mask);

const auto lo_compress_result = TruncateTo(du, Compress(dw, vw_lo, mask_lo));
const auto hi_compress_result = TruncateTo(du, Compress(dw, vw_hi, mask_hi));

// We need to combine two vectors of non-constexpr length, so the only option
// is Splice, which requires us to synthesize a mask. NOTE: this function uses
// full vectors (SV_ALL instead of SV_POW2), hence we need unmasked svcnt.
const size_t countL = detail::CountTrueFull(dw, mask32L);
const auto compressed_maskL = FirstN(d16, countL);
return detail::Splice(v16H, v16L, compressed_maskL);
const size_t lo_count = detail::CountTrueFull(dw, mask_lo);
const auto lo_compressed_mask = FirstN(du, lo_count);

return BitCast(d, detail::Splice(hi_compress_result, lo_compress_result,
lo_compressed_mask));
}

// Must treat float16_t as integers so we can ConcatEven.
HWY_API svfloat16_t Compress(svfloat16_t v, svbool_t mask16) {
const DFromV<decltype(v)> df;
const RebindToSigned<decltype(df)> di;
return BitCast(df, Compress(BitCast(di, v), mask16));
template <
class D, HWY_IF_T_SIZE_ONE_OF_D(D, (1 << 1) | (1 << 2)),
hwy::EnableIf<!detail::NeedToSplitForCompress8Or16<RemoveCvRef<D>>()>* =
nullptr>
HWY_API VFromD<D> Compress(D d, VFromD<D> v, const svbool_t mask) {
using T = TFromD<D>;
using TU = MakeUnsigned<T>;

using TW =
If<(sizeof(T) == 1 && ((HWY_POW2_D(D) <= -2) ||
(HWY_MAX_LANES_D(D) <= (HWY_MIN_BYTES / 4)))),
uint32_t, MakeWide<TU>>;

const ScalableTag<TW> dw;
const Repartition<TU, decltype(dw)> du;

const auto vu = BitCast(du, v);
return BitCast(d, TruncateTo(du, Compress(dw, PromoteTo(dw, vu),
PromoteMaskTo(dw, du, mask))));
}

template <class V, HWY_IF_T_SIZE_ONE_OF_V(V, (1 << 1) | (1 << 2))>
HWY_API V Compress(V v, const svbool_t mask) {
return Compress(DFromV<V>(), v, mask);
}

// ------------------------------ CompressNot

// 2 or 4 bytes
template <class V, HWY_IF_T_SIZE_ONE_OF_V(V, (1 << 2) | (1 << 4))>
// 1, 2, or 4 bytes
template <class V, HWY_IF_NOT_T_SIZE_V(V, 8)>
HWY_API V CompressNot(V v, const svbool_t mask) {
return Compress(v, Not(mask));
}

template <class D, HWY_IF_NOT_T_SIZE_D(D, 8)>
HWY_API VFromD<D> CompressNot(D d, VFromD<D> v, const svbool_t mask) {
return Compress(d, v, Not(mask));
}

template <class V, HWY_IF_T_SIZE_V(V, 8)>
HWY_API V CompressNot(V v, svbool_t mask) {
#if HWY_TARGET == HWY_SVE2_128 || HWY_IDE
Expand Down Expand Up @@ -4619,11 +4667,24 @@ HWY_API V CompressNot(V v, svbool_t mask) {
2, 0, 1, 3, 0, 1, 2, 3, 1, 0, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};
return TableLookupLanes(v, SetTableIndices(d, table + offset));
#endif // HWY_TARGET == HWY_SVE_256

#if (HWY_TARGET != HWY_SVE2_128 && HWY_TARGET != HWY_SVE_256) || HWY_IDE
return Compress(v, Not(mask));
#endif
}

template <class D, HWY_IF_T_SIZE_D(D, 8)>
HWY_API VFromD<D> CompressNot(D /*d*/, VFromD<D> v, const svbool_t mask) {
return CompressNot(v, mask);
}

// ------------------------------ CompressBlocksNot

#ifdef HWY_NATIVE_COMPRESS_BLOCKS_NOT
#undef HWY_NATIVE_COMPRESS_BLOCKS_NOT
#else
#define HWY_NATIVE_COMPRESS_BLOCKS_NOT
#endif

HWY_API svuint64_t CompressBlocksNot(svuint64_t v, svbool_t mask) {
#if HWY_TARGET == HWY_SVE2_128
(void)mask;
Expand All @@ -4641,24 +4702,26 @@ HWY_API svuint64_t CompressBlocksNot(svuint64_t v, svbool_t mask) {
return TableLookupLanes(v, SetTableIndices(d, table + offset));
#endif

#if (HWY_TARGET != HWY_SVE2_128 && HWY_TARGET != HWY_SVE_256) || HWY_IDE
return CompressNot(v, mask);
#endif
}

// ------------------------------ CompressStore
template <class V, class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class V, class D>
HWY_API size_t CompressStore(const V v, const svbool_t mask, const D d,
TFromD<D>* HWY_RESTRICT unaligned) {
StoreU(Compress(v, mask), d, unaligned);
StoreU(Compress(d, v, mask), d, unaligned);
return CountTrue(d, mask);
}

// ------------------------------ CompressBlendedStore
template <class V, class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class V, class D>
HWY_API size_t CompressBlendedStore(const V v, const svbool_t mask, const D d,
TFromD<D>* HWY_RESTRICT unaligned) {
const size_t count = CountTrue(d, mask);
const svbool_t store_mask = FirstN(d, count);
BlendedStore(Compress(v, mask), store_mask, d, unaligned);
BlendedStore(Compress(d, v, mask), store_mask, d, unaligned);
return count;
}

Expand Down Expand Up @@ -5665,13 +5728,19 @@ HWY_API size_t StoreMaskBits(D d, svbool_t m, uint8_t* bits) {
}

// ------------------------------ CompressBits (LoadMaskBits)
template <class V, HWY_IF_NOT_T_SIZE_V(V, 1)>
HWY_INLINE V CompressBits(V v, const uint8_t* HWY_RESTRICT bits) {
template <class D>
HWY_API VFromD<D> CompressBits(D d, VFromD<D> v,
const uint8_t* HWY_RESTRICT bits) {
return Compress(d, v, LoadMaskBits(d, bits));
}

template <class V>
HWY_API V CompressBits(V v, const uint8_t* HWY_RESTRICT bits) {
return Compress(v, LoadMaskBits(DFromV<V>(), bits));
}

// ------------------------------ CompressBitsStore (LoadMaskBits)
template <class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class D>
HWY_API size_t CompressBitsStore(VFromD<D> v, const uint8_t* HWY_RESTRICT bits,
D d, TFromD<D>* HWY_RESTRICT unaligned) {
return CompressStore(v, LoadMaskBits(d, bits), d, unaligned);
Expand Down
45 changes: 33 additions & 12 deletions hwy/ops/emu128-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1447,10 +1447,10 @@ HWY_API void BlendedStore(VFromD<D> v, MFromD<D> m, D d,
}
}

#ifdef HWY_NATIVE_STORE_N
#undef HWY_NATIVE_STORE_N
#ifdef HWY_NATIVE_STORE_N_IMPL
#undef HWY_NATIVE_STORE_N_IMPL
#else
#define HWY_NATIVE_STORE_N
#define HWY_NATIVE_STORE_N_IMPL
#endif

template <class D>
Expand Down Expand Up @@ -2701,9 +2701,21 @@ HWY_API intptr_t FindLastTrue(D d, MFromD<D> mask) {

// ------------------------------ Compress

#ifdef HWY_NATIVE_COMPRESS8
#undef HWY_NATIVE_COMPRESS8
#else
#define HWY_NATIVE_COMPRESS8
#endif

#ifdef HWY_NATIVE_COMPRESS16_32_64
#undef HWY_NATIVE_COMPRESS16_32_64
#else
#define HWY_NATIVE_COMPRESS16_32_64
#endif

template <typename T>
struct CompressIsPartition {
enum { value = (sizeof(T) != 1) };
enum { value = 1 };
};

template <typename T, size_t N>
Expand All @@ -2724,6 +2736,11 @@ HWY_API Vec128<T, N> Compress(Vec128<T, N> v, Mask128<T, N> mask) {
return ret;
}

template <class D>
HWY_API VFromD<D> Compress(D /*d*/, VFromD<D> v, MFromD<D> m) {
return Compress(v, m);
}

// ------------------------------ Expand

// Could also just allow generic_ops-inl.h to implement these, but use our
Expand Down Expand Up @@ -2784,10 +2801,9 @@ HWY_API Vec128<T, N> CompressNot(Vec128<T, N> v, Mask128<T, N> mask) {
return ret;
}

// ------------------------------ CompressBlocksNot
HWY_API Vec128<uint64_t> CompressBlocksNot(Vec128<uint64_t> v,
Mask128<uint64_t> /* m */) {
return v;
template <class D>
HWY_API VFromD<D> CompressNot(D /*d*/, VFromD<D> v, MFromD<D> m) {
return CompressNot(v, m);
}

// ------------------------------ CompressBits
Expand All @@ -2797,10 +2813,15 @@ HWY_API Vec128<T, N> CompressBits(Vec128<T, N> v,
return Compress(v, LoadMaskBits(Simd<T, N, 0>(), bits));
}

template <class D>
HWY_API VFromD<D> CompressBits(D /*d*/, VFromD<D> v,
const uint8_t* HWY_RESTRICT bits) {
return CompressBits(v, bits);
}

// ------------------------------ CompressStore

// generic_ops-inl defines the 8-bit versions.
template <class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class D>
HWY_API size_t CompressStore(VFromD<D> v, MFromD<D> mask, D d,
TFromD<D>* HWY_RESTRICT unaligned) {
size_t count = 0;
Expand All @@ -2813,14 +2834,14 @@ HWY_API size_t CompressStore(VFromD<D> v, MFromD<D> mask, D d,
}

// ------------------------------ CompressBlendedStore
template <class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class D>
HWY_API size_t CompressBlendedStore(VFromD<D> v, MFromD<D> mask, D d,
TFromD<D>* HWY_RESTRICT unaligned) {
return CompressStore(v, mask, d, unaligned);
}

// ------------------------------ CompressBitsStore
template <class D, HWY_IF_NOT_T_SIZE_D(D, 1)>
template <class D>
HWY_API size_t CompressBitsStore(VFromD<D> v, const uint8_t* HWY_RESTRICT bits,
D d, TFromD<D>* HWY_RESTRICT unaligned) {
const MFromD<D> mask = LoadMaskBits(d, bits);
Expand Down
Loading
Loading