Skip to content

Commit f4c2c1e

Browse files
jan-wassenbergcopybara-github
authored andcommitted
Add XorAndNot, simplify ternlog usage
Avoid duplicating Xor3. PiperOrigin-RevId: 857087019
1 parent 56adfd1 commit f4c2c1e

16 files changed

Lines changed: 405 additions & 408 deletions

g3doc/quick_reference.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,9 @@ them from 2-argument functions:
13051305
* <code>V **Or3**(V o1, V o2, V o3)</code>: returns `o1[i] | o2[i] | o3[i]`.
13061306
This is less efficient than `Xor3` on some targets; use that where possible.
13071307
* <code>V **OrAnd**(V o, V a1, V a2)</code>: returns `o[i] | (a1[i] & a2[i])`.
1308+
* <code>V **XorAndNot**(V x, V a1, V a2)</code>: returns `x[i] ^ (~a1[i] &
1309+
a2[i])`. This is useful for conditionally flipping bits.
1310+
13081311
* <code>V **BitwiseIfThenElse**(V mask, V yes, V no)</code>: returns
13091312
`((mask[i] & yes[i]) | (~mask[i] & no[i]))`. `BitwiseIfThenElse` is
13101313
equivalent to, but potentially more efficient than `Or(And(mask, yes),

hwy/ops/arm_neon-inl.h

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2903,15 +2903,15 @@ HWY_API Vec128<T, N> And(const Vec128<T, N> a, const Vec128<T, N> b) {
29032903
// ------------------------------ AndNot
29042904

29052905
namespace detail {
2906-
// reversed_andnot returns a & ~b.
2907-
HWY_NEON_DEF_FUNCTION_INTS_UINTS(reversed_andnot, vbic, _, 2)
2906+
// AndNotSwap returns a & ~b, whereas AndNot is defined as ~a & b.
2907+
HWY_NEON_DEF_FUNCTION_INTS_UINTS(AndNotSwap, vbic, _, 2)
29082908
} // namespace detail
29092909

29102910
// Returns ~not_mask & mask.
29112911
template <typename T, size_t N, HWY_IF_NOT_FLOAT(T)>
29122912
HWY_API Vec128<T, N> AndNot(const Vec128<T, N> not_mask,
29132913
const Vec128<T, N> mask) {
2914-
return detail::reversed_andnot(mask, not_mask);
2914+
return detail::AndNotSwap(mask, not_mask);
29152915
}
29162916

29172917
// Uses the u32/64 defined above.
@@ -2921,7 +2921,7 @@ HWY_API Vec128<T, N> AndNot(const Vec128<T, N> not_mask,
29212921
const DFromV<decltype(mask)> d;
29222922
const RebindToUnsigned<decltype(d)> du;
29232923
VFromD<decltype(du)> ret =
2924-
detail::reversed_andnot(BitCast(du, mask), BitCast(du, not_mask));
2924+
detail::AndNotSwap(BitCast(du, mask), BitCast(du, not_mask));
29252925
return BitCast(d, ret);
29262926
}
29272927

@@ -2951,6 +2951,13 @@ HWY_API Vec128<T, N> Xor(const Vec128<T, N> a, const Vec128<T, N> b) {
29512951

29522952
// ------------------------------ Xor3
29532953
#if HWY_ARCH_ARM_A64 && defined(__ARM_FEATURE_SHA3)
2954+
2955+
#ifdef HWY_NATIVE_XOR3
2956+
#undef HWY_NATIVE_XOR3
2957+
#else
2958+
#define HWY_NATIVE_XOR3
2959+
#endif
2960+
29542961
HWY_NEON_DEF_FUNCTION_FULL_UI(Xor3, veor3, _, 3)
29552962

29562963
// Half vectors are not natively supported. Two Xor are likely more efficient
@@ -2968,11 +2975,6 @@ HWY_API Vec128<T, N> Xor3(const Vec128<T, N> x1, const Vec128<T, N> x2,
29682975
return BitCast(d, Xor3(BitCast(du, x1), BitCast(du, x2), BitCast(du, x3)));
29692976
}
29702977

2971-
#else
2972-
template <typename T, size_t N>
2973-
HWY_API Vec128<T, N> Xor3(Vec128<T, N> x1, Vec128<T, N> x2, Vec128<T, N> x3) {
2974-
return Xor(x1, Xor(x2, x3));
2975-
}
29762978
#endif
29772979

29782980
// ------------------------------ Or3
@@ -2987,6 +2989,45 @@ HWY_API Vec128<T, N> OrAnd(Vec128<T, N> o, Vec128<T, N> a1, Vec128<T, N> a2) {
29872989
return Or(o, And(a1, a2));
29882990
}
29892991

2992+
// ------------------------------ XorAndNot
2993+
#if HWY_ARCH_ARM_A64 && defined(__ARM_FEATURE_SHA3)
2994+
2995+
#ifdef HWY_NATIVE_BCAX
2996+
#undef HWY_NATIVE_BCAX
2997+
#else
2998+
#define HWY_NATIVE_BCAX
2999+
#endif
3000+
3001+
namespace detail {
3002+
HWY_NEON_DEF_FUNCTION_FULL_UI(XorAndNotSwap, vbcax, _, 3)
3003+
} // namespace detail
3004+
3005+
// As with AndNot, swap the last two arguments because our "negated first"
3006+
// convention mismatches the intrinsics, which have the negated arg last.
3007+
template <class V, HWY_IF_V_SIZE_V(V, 16), HWY_IF_NOT_FLOAT_V(V)>
3008+
HWY_API V XorAndNot(V x, V a1, V a2) {
3009+
return detail::XorAndNotSwap(x, a2, a1);
3010+
}
3011+
3012+
// Half vectors are not natively supported. Two ops are likely more efficient
3013+
// than Combine to 128-bit.
3014+
template <typename T, size_t N, HWY_IF_V_SIZE_LE(T, N, 8), HWY_IF_NOT_FLOAT(T)>
3015+
HWY_API Vec128<T, N> XorAndNot(Vec128<T, N> x, Vec128<T, N> a1,
3016+
Vec128<T, N> a2) {
3017+
return Xor(x, AndNot(a1, a2));
3018+
}
3019+
3020+
template <typename T, size_t N, HWY_IF_FLOAT(T)>
3021+
HWY_API Vec128<T, N> XorAndNot(const Vec128<T, N> x, const Vec128<T, N> a1,
3022+
const Vec128<T, N> a2) {
3023+
const DFromV<decltype(x)> d;
3024+
const RebindToUnsigned<decltype(d)> du;
3025+
return BitCast(d,
3026+
XorAndNot(BitCast(du, x), BitCast(du, a1), BitCast(du, a2)));
3027+
}
3028+
3029+
#endif
3030+
29903031
// ------------------------------ Operator overloads (internal-only if float)
29913032

29923033
template <typename T, size_t N>
@@ -7699,7 +7740,7 @@ HWY_API VFromD<DI32> SumOfMulQuadAccumulate(
76997740
return BitCast(di32, Sub(result_sum0, result_sum1));
77007741
}
77017742

7702-
#endif // __ARM_FEATURE_MATMUL_INT8
7743+
#endif // __ARM_FEATURE_MATMUL_INT8
77037744

77047745
#endif // HWY_TARGET == HWY_NEON_BF16
77057746

hwy/ops/arm_sve-inl.h

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -872,21 +872,47 @@ HWY_API V AndNot(const V a, const V b) {
872872

873873
#if HWY_SVE_HAVE_2
874874

875+
#ifdef HWY_NATIVE_XOR3
876+
#undef HWY_NATIVE_XOR3
877+
#else
878+
#define HWY_NATIVE_XOR3
879+
#endif
880+
881+
#ifdef HWY_NATIVE_BCAX
882+
#undef HWY_NATIVE_BCAX
883+
#else
884+
#define HWY_NATIVE_BCAX
885+
#endif
886+
875887
HWY_SVE_FOREACH_UI(HWY_SVE_RETV_ARGVVV, Xor3, eor3)
876888

889+
// As with AndNot, we follow the x86 convention where the first argument is
890+
// negated, whereas the intrinsic has it last.
891+
#define HWY_SVE_RETV_ARGVVV_SWAP(BASE, CHAR, BITS, HALF, NAME, OP) \
892+
HWY_API HWY_SVE_V(BASE, BITS) \
893+
NAME(HWY_SVE_V(BASE, BITS) a, HWY_SVE_V(BASE, BITS) b, \
894+
HWY_SVE_V(BASE, BITS) c) { \
895+
return sv##OP##_##CHAR##BITS(a, c, b); \
896+
}
897+
HWY_SVE_FOREACH_UI(HWY_SVE_RETV_ARGVVV_SWAP, XorAndNot, bcax)
898+
#undef HWY_SVE_RETV_ARGVVV_SWAP
899+
877900
template <class V, HWY_IF_FLOAT_V(V)>
878901
HWY_API V Xor3(const V x1, const V x2, const V x3) {
879902
const DFromV<V> df;
880903
const RebindToUnsigned<decltype(df)> du;
881904
return BitCast(df, Xor3(BitCast(du, x1), BitCast(du, x2), BitCast(du, x3)));
882905
}
883906

884-
#else
885-
template <class V>
886-
HWY_API V Xor3(V x1, V x2, V x3) {
887-
return Xor(x1, Xor(x2, x3));
907+
template <class V, HWY_IF_FLOAT_V(V)>
908+
HWY_API V XorAndNot(const V x, const V a1, const V a2) {
909+
const DFromV<V> df;
910+
const RebindToUnsigned<decltype(df)> du;
911+
return BitCast(df,
912+
XorAndNot(BitCast(du, x), BitCast(du, a1), BitCast(du, a2)));
888913
}
889-
#endif
914+
915+
#endif // HWY_SVE_HAVE_2
890916

891917
// ------------------------------ Or3
892918
template <class V>

hwy/ops/emu128-inl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,6 @@ HWY_API Vec128<T, N> operator^(Vec128<T, N> a, Vec128<T, N> b) {
316316
return Xor(a, b);
317317
}
318318

319-
// ------------------------------ Xor3
320-
template <typename T, size_t N>
321-
HWY_API Vec128<T, N> Xor3(Vec128<T, N> x1, Vec128<T, N> x2, Vec128<T, N> x3) {
322-
return Xor(x1, Xor(x2, x3));
323-
}
324-
325319
// ------------------------------ Or3
326320
template <typename T, size_t N>
327321
HWY_API Vec128<T, N> Or3(Vec128<T, N> o1, Vec128<T, N> o2, Vec128<T, N> o3) {

hwy/ops/generic_ops-inl.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,40 @@ HWY_API MFromD<D> MaskedIsNaN(const M m, const V v) {
850850
}
851851
#endif // HWY_NATIVE_MASKED_COMP
852852

853+
// ------------------------------ Xor3
854+
855+
#if (defined(HWY_NATIVE_XOR3) == \
856+
defined(HWY_TARGET_TOGGLE))
857+
#ifdef HWY_NATIVE_XOR3
858+
#undef HWY_NATIVE_XOR3
859+
#else
860+
#define HWY_NATIVE_XOR3
861+
#endif
862+
863+
template <class V>
864+
HWY_API V Xor3(V x1, V x2, V x3) {
865+
return Xor(x1, Xor(x2, x3));
866+
}
867+
868+
#endif // HWY_NATIVE_XOR3
869+
870+
// ------------------------------ XorAndNot
871+
872+
#if (defined(HWY_NATIVE_BCAX) == \
873+
defined(HWY_TARGET_TOGGLE))
874+
#ifdef HWY_NATIVE_BCAX
875+
#undef HWY_NATIVE_BCAX
876+
#else
877+
#define HWY_NATIVE_BCAX
878+
#endif
879+
880+
template <class V>
881+
HWY_API V XorAndNot(const V x, const V a1, const V a2) {
882+
return Xor(x, AndNot(a1, a2));
883+
}
884+
885+
#endif // HWY_NATIVE_BCAX
886+
853887
// ------------------------------ IfNegativeThenNegOrUndefIfZero
854888

855889
#if (defined(HWY_NATIVE_INTEGER_IF_NEGATIVE_THEN_NEG) == \

hwy/ops/loongarch_lasx-inl.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,6 @@ HWY_API Vec256<T> Not(const Vec256<T> v) {
389389
BitCast(du, v).raw)});
390390
}
391391

392-
// ------------------------------ Xor3
393-
template <typename T>
394-
HWY_API Vec256<T> Xor3(Vec256<T> x1, Vec256<T> x2, Vec256<T> x3) {
395-
return Xor(x1, Xor(x2, x3));
396-
}
397-
398392
// ------------------------------ Or3
399393
template <typename T>
400394
HWY_API Vec256<T> Or3(Vec256<T> o1, Vec256<T> o2, Vec256<T> o3) {
@@ -2479,7 +2473,7 @@ HWY_API Vec256<T> TableLookupLanes(Vec256<T> v, Indices256<T> idx) {
24792473
// Replicate 64-bit index into upper 32 bits
24802474
const Vec256<TI> dup{__lasx_xvpackev_w(idx.raw, idx.raw)};
24812475
// For each idx64 i, idx32 are 2*i and 2*i+1.
2482-
const Vec256<TI> idx32 = dup + dup + Set(di64, int64_t(1) << 32);
2476+
const Vec256<TI> idx32 = dup + dup + Set(di64, int64_t{1} << 32);
24832477
return BitCast(
24842478
d, TableLookupLanes(BitCast(di32, v), Indices256<int32_t>{idx32.raw}));
24852479
}

hwy/ops/loongarch_lsx-inl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,12 +436,6 @@ HWY_API Vec128<T, N> Not(const Vec128<T, N> v) {
436436
__lsx_vnor_v(BitCast(du, v).raw, BitCast(du, v).raw)});
437437
}
438438

439-
// ------------------------------ Xor3
440-
template <typename T, size_t N>
441-
HWY_API Vec128<T, N> Xor3(Vec128<T, N> x1, Vec128<T, N> x2, Vec128<T, N> x3) {
442-
return Xor(x1, Xor(x2, x3));
443-
}
444-
445439
// ------------------------------ Or3
446440
template <typename T, size_t N>
447441
HWY_API Vec128<T, N> Or3(Vec128<T, N> o1, Vec128<T, N> o2, Vec128<T, N> o3) {

hwy/ops/ppc_vsx-inl.h

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,12 +459,17 @@ HWY_INLINE V TernaryLogic(V a, V b, V c) {
459459
}
460460

461461
} // namespace detail
462-
#endif // HWY_PPC_HAVE_10
463462

464463
// ------------------------------ Xor3
464+
465+
#ifdef HWY_NATIVE_XOR3
466+
#undef HWY_NATIVE_XOR3
467+
#else
468+
#define HWY_NATIVE_XOR3
469+
#endif
470+
465471
template <typename T, size_t N>
466472
HWY_API Vec128<T, N> Xor3(Vec128<T, N> x1, Vec128<T, N> x2, Vec128<T, N> x3) {
467-
#if HWY_PPC_HAVE_10
468473
#if defined(__OPTIMIZE__)
469474
if (static_cast<int>(detail::IsConstantRawAltivecVect(x1.raw)) +
470475
static_cast<int>(detail::IsConstantRawAltivecVect(x2.raw)) +
@@ -476,11 +481,34 @@ HWY_API Vec128<T, N> Xor3(Vec128<T, N> x1, Vec128<T, N> x2, Vec128<T, N> x3) {
476481
{
477482
return detail::TernaryLogic<0x69>(x1, x2, x3);
478483
}
484+
}
485+
486+
// ------------------------------ XorAndNot
487+
488+
#ifdef HWY_NATIVE_BCAX
489+
#undef HWY_NATIVE_BCAX
479490
#else
480-
return Xor(x1, Xor(x2, x3));
491+
#define HWY_NATIVE_BCAX
481492
#endif
493+
494+
template <typename T, size_t N>
495+
HWY_API Vec128<T, N> XorAndNot(Vec128<T, N> x, Vec128<T, N> a1,
496+
Vec128<T, N> a2) {
497+
#if defined(__OPTIMIZE__)
498+
if (static_cast<int>(detail::IsConstantRawAltivecVect(x.raw)) +
499+
static_cast<int>(detail::IsConstantRawAltivecVect(a1.raw)) +
500+
static_cast<int>(detail::IsConstantRawAltivecVect(a2.raw)) >=
501+
2) {
502+
return Xor(x, AndNot(a1, a2));
503+
} else // NOLINT
504+
#endif
505+
{
506+
return detail::TernaryLogic<0x4B>(x, a1, a2);
507+
}
482508
}
483509

510+
#endif // HWY_PPC_HAVE_10
511+
484512
// ------------------------------ Or3
485513
template <typename T, size_t N>
486514
HWY_API Vec128<T, N> Or3(Vec128<T, N> o1, Vec128<T, N> o2, Vec128<T, N> o3) {

hwy/ops/rvv-inl.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,12 +1058,6 @@ HWY_API V AndNot(const V not_a, const V b) {
10581058
return And(Not(not_a), b);
10591059
}
10601060

1061-
// ------------------------------ Xor3
1062-
template <class V>
1063-
HWY_API V Xor3(V x1, V x2, V x3) {
1064-
return Xor(x1, Xor(x2, x3));
1065-
}
1066-
10671061
// ------------------------------ Or3
10681062
template <class V>
10691063
HWY_API V Or3(V o1, V o2, V o3) {

hwy/ops/scalar-inl.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,6 @@ HWY_API Vec1<T> operator^(const Vec1<T> a, const Vec1<T> b) {
253253
return Xor(a, b);
254254
}
255255

256-
// ------------------------------ Xor3
257-
258-
template <typename T>
259-
HWY_API Vec1<T> Xor3(Vec1<T> x1, Vec1<T> x2, Vec1<T> x3) {
260-
return Xor(x1, Xor(x2, x3));
261-
}
262-
263256
// ------------------------------ Or3
264257

265258
template <typename T>

0 commit comments

Comments
 (0)