@@ -550,23 +550,32 @@ struct TraitsLane : public Base {
550550 // according to Base.
551551 // Corresponds to a conditional swap, which is one "node" of a sorting
552552 // network. Min/Max are cheaper than compare + blend at least for integers.
553- template <class D >
553+ template <PairLayout kLayout = PairLayout:: kIndependent , class D >
554554 HWY_INLINE void Sort2 (D d, Vec<D>& a, Vec<D>& b) const {
555555 const Base* base = static_cast <const Base*>(this );
556556
557557 const Vec<D> a_copy = a;
558- // Prior to AVX3, there is no native 64-bit Min/Max, so they compile to 4
559- // instructions. We can reduce it to a compare + 2 IfThenElse.
558+ // Prior to AVX3 there is no native 64-bit integer Min/Max. Use a compare
559+ // and two blends, except for mirrored floats: selecting opposite inputs
560+ // on a tie there would duplicate one zero after the lanes are merged.
560561#if HWY_AVX3 < HWY_TARGET && HWY_TARGET <= HWY_SSSE3
561- if (sizeof (TFromD<D>) == 8 ) {
562+ if (sizeof (TFromD<D>) == 8 &&
563+ !(kLayout == PairLayout::kMirrored && IsFloat<TFromD<D>>())) {
562564 const Mask<D> cmp = base->Compare (d, a, b);
563565 a = IfThenElse (cmp, a, b);
564566 b = IfThenElse (cmp, b, a_copy);
565567 return ;
566568 }
567569#endif
568570 a = base->First (d, a, b);
569- b = base->Last (d, a_copy, b);
571+ // Some floating-point Min/Max instructions both choose the second operand
572+ // for equal zeros. Keep one of each input. Mirrored lanes already compare
573+ // the same pair in opposite orders before selecting one result per lane.
574+ if constexpr (kLayout == PairLayout::kIndependent && IsFloat<TFromD<D>>()) {
575+ b = base->Last (d, b, a_copy);
576+ } else {
577+ b = base->Last (d, a_copy, b);
578+ }
570579 }
571580
572581 // Conditionally swaps even-numbered lanes with their odd-numbered neighbor.
@@ -580,7 +589,7 @@ struct TraitsLane : public Base {
580589 const Vec<D> cmp = VecFromMask (d, base->Compare (d, v, swapped));
581590 return IfVecThenElse (DupOdd (cmp), swapped, v);
582591#else
583- Sort2 (d, v, swapped);
592+ Sort2<PairLayout:: kMirrored > (d, v, swapped);
584593 return base->OddEvenKeys (swapped, v);
585594#endif
586595 }
@@ -590,7 +599,7 @@ struct TraitsLane : public Base {
590599 HWY_INLINE Vec<D> SortPairsDistance1 (D d, Vec<D> v) const {
591600 const Base* base = static_cast <const Base*>(this );
592601 Vec<D> swapped = base->ReverseKeys2 (d, v);
593- Sort2 (d, v, swapped);
602+ Sort2<PairLayout:: kMirrored > (d, v, swapped);
594603 return base->OddEvenKeys (swapped, v);
595604 }
596605
@@ -599,7 +608,7 @@ struct TraitsLane : public Base {
599608 HWY_INLINE Vec<D> SortPairsReverse4 (D d, Vec<D> v) const {
600609 const Base* base = static_cast <const Base*>(this );
601610 Vec<D> swapped = base->ReverseKeys4 (d, v);
602- Sort2 (d, v, swapped);
611+ Sort2<PairLayout:: kMirrored > (d, v, swapped);
603612 return base->OddEvenPairs (d, swapped, v);
604613 }
605614
@@ -610,7 +619,7 @@ struct TraitsLane : public Base {
610619 Vec<D> swapped = base->SwapAdjacentQuads (d, v);
611620 // Only used in Merge16, so this will not be used on AVX2 (which only has 4
612621 // u64 lanes), so skip the above optimization for 64-bit AVX2.
613- Sort2 (d, v, swapped);
622+ Sort2<PairLayout:: kMirrored > (d, v, swapped);
614623 return base->OddEvenQuads (d, swapped, v);
615624 }
616625};
0 commit comments