Skip to content

Commit 3f17c80

Browse files
committed
perf(avx512vl): native EVEX for int64 sra, signed rotr, compress/expand
Add pure-VL overrides so these stop falling back to AVX2/SSE: - int64 signed >> -> vpsraq/vpsravq (unsigned/32-bit unchanged) - signed rotr -> vprorvq/vprolq (drop is_unsigned guard, mirror rotl) - compress/expand -> EVEX forms; 8/16-bit fall through to common{}
1 parent 24053b5 commit 3f17c80

2 files changed

Lines changed: 254 additions & 40 deletions

File tree

include/xsimd/arch/xsimd_avx512vl_128.hpp

Lines changed: 127 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,48 @@ namespace xsimd
378378
return (typename batch_bool<double, A>::register_type)_mm_cmp_pd_mask(self, self, _CMP_UNORD_Q);
379379
}
380380

381+
// bitwise_rshift — signed int64 uses the native EVEX arithmetic shift
382+
// (VPSRAQ / VPSRAVQ, lat 1 / CPI 0.5). Every other width/sign keeps the
383+
// inherited avx2_128 codegen (srai/srav for 32-bit, srli for unsigned 64).
384+
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
385+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_128>) noexcept
386+
{
387+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
388+
{
389+
return _mm_srai_epi64(self, other);
390+
}
391+
else
392+
{
393+
return bitwise_rshift(self, other, avx2_128 {});
394+
}
395+
}
396+
template <size_t shift, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
397+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, requires_arch<avx512vl_128>) noexcept
398+
{
399+
constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
400+
static_assert(shift < bits, "Shift amount must be less than the number of bits in T");
401+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
402+
{
403+
return _mm_srai_epi64(self, shift);
404+
}
405+
else
406+
{
407+
return bitwise_rshift<shift>(self, avx2_128 {});
408+
}
409+
}
410+
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
411+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
412+
{
413+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
414+
{
415+
return _mm_srav_epi64(self, other);
416+
}
417+
else
418+
{
419+
return bitwise_rshift(self, other, avx2_128 {});
420+
}
421+
}
422+
381423
// rotl
382424
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
383425
XSIMD_INLINE batch<T, A> rotl(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
@@ -423,18 +465,18 @@ namespace xsimd
423465
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
424466
XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_128>) noexcept
425467
{
426-
XSIMD_IF_CONSTEXPR(std::is_unsigned<T>::value)
468+
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
427469
{
428-
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
429-
{
430-
return _mm_rorv_epi32(self, other);
431-
}
432-
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
433-
{
434-
return _mm_rorv_epi64(self, other);
435-
}
470+
return _mm_rorv_epi32(self, other);
471+
}
472+
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
473+
{
474+
return _mm_rorv_epi64(self, other);
475+
}
476+
else
477+
{
478+
return rotr(self, other, avx2_128 {});
436479
}
437-
return rotr(self, other, avx2_128 {});
438480
}
439481
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
440482
XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_128>) noexcept
@@ -447,18 +489,83 @@ namespace xsimd
447489
{
448490
constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
449491
static_assert(count < bits, "Count must be less than the number of bits in T");
450-
XSIMD_IF_CONSTEXPR(std::is_unsigned<T>::value)
492+
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
451493
{
452-
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
453-
{
454-
return _mm_ror_epi32(self, count);
455-
}
456-
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
457-
{
458-
return _mm_ror_epi64(self, count);
459-
}
494+
return _mm_ror_epi32(self, count);
460495
}
461-
return rotr<count>(self, avx2_128 {});
496+
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
497+
{
498+
return _mm_ror_epi64(self, count);
499+
}
500+
else
501+
{
502+
return rotr<count>(self, avx2_128 {});
503+
}
504+
}
505+
506+
// compress — native EVEX VPCOMPRESS{PS,PD,Q,D} for the widths with VL
507+
// forms. 8/16-bit need AVX512_VBMI2, so they fall through to common{}.
508+
template <class A>
509+
XSIMD_INLINE batch<float, A> compress(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_128>) noexcept
510+
{
511+
return _mm_maskz_compress_ps(mask.mask(), self);
512+
}
513+
template <class A>
514+
XSIMD_INLINE batch<double, A> compress(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_128>) noexcept
515+
{
516+
return _mm_maskz_compress_pd(mask.mask(), self);
517+
}
518+
template <class A>
519+
XSIMD_INLINE batch<int32_t, A> compress(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
520+
{
521+
return _mm_maskz_compress_epi32(mask.mask(), self);
522+
}
523+
template <class A>
524+
XSIMD_INLINE batch<uint32_t, A> compress(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
525+
{
526+
return _mm_maskz_compress_epi32(mask.mask(), self);
527+
}
528+
template <class A>
529+
XSIMD_INLINE batch<int64_t, A> compress(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
530+
{
531+
return _mm_maskz_compress_epi64(mask.mask(), self);
532+
}
533+
template <class A>
534+
XSIMD_INLINE batch<uint64_t, A> compress(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
535+
{
536+
return _mm_maskz_compress_epi64(mask.mask(), self);
537+
}
538+
539+
// expand
540+
template <class A>
541+
XSIMD_INLINE batch<float, A> expand(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_128>) noexcept
542+
{
543+
return _mm_maskz_expand_ps(mask.mask(), self);
544+
}
545+
template <class A>
546+
XSIMD_INLINE batch<double, A> expand(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_128>) noexcept
547+
{
548+
return _mm_maskz_expand_pd(mask.mask(), self);
549+
}
550+
template <class A>
551+
XSIMD_INLINE batch<int32_t, A> expand(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
552+
{
553+
return _mm_maskz_expand_epi32(mask.mask(), self);
554+
}
555+
template <class A>
556+
XSIMD_INLINE batch<uint32_t, A> expand(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
557+
{
558+
return _mm_maskz_expand_epi32(mask.mask(), self);
559+
}
560+
template <class A>
561+
XSIMD_INLINE batch<int64_t, A> expand(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
562+
{
563+
return _mm_maskz_expand_epi64(mask.mask(), self);
564+
}
565+
template <class A>
566+
XSIMD_INLINE batch<uint64_t, A> expand(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_128>) noexcept
567+
{
568+
return _mm_maskz_expand_epi64(mask.mask(), self);
462569
}
463570

464571
// all

include/xsimd/arch/xsimd_avx512vl_256.hpp

Lines changed: 127 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,48 @@ namespace xsimd
460460
return (typename batch_bool<double, A>::register_type)_mm256_cmp_pd_mask(self, self, _CMP_UNORD_Q);
461461
}
462462

463+
// bitwise_rshift — signed int64 uses the native EVEX arithmetic shift
464+
// (VPSRAQ / VPSRAVQ, lat 1 / CPI 0.5). Every other width/sign keeps the
465+
// inherited avx2 codegen (srai/srav for 32-bit, srli for unsigned 64).
466+
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
467+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_256>) noexcept
468+
{
469+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
470+
{
471+
return _mm256_srai_epi64(self, other);
472+
}
473+
else
474+
{
475+
return bitwise_rshift(self, other, avx2 {});
476+
}
477+
}
478+
template <size_t shift, class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
479+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, requires_arch<avx512vl_256>) noexcept
480+
{
481+
constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
482+
static_assert(shift < bits, "Shift amount must be less than the number of bits in T");
483+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
484+
{
485+
return _mm256_srai_epi64(self, shift);
486+
}
487+
else
488+
{
489+
return bitwise_rshift<shift>(self, avx2 {});
490+
}
491+
}
492+
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
493+
XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_256>) noexcept
494+
{
495+
XSIMD_IF_CONSTEXPR(std::is_signed<T>::value && sizeof(T) == 8)
496+
{
497+
return _mm256_srav_epi64(self, other);
498+
}
499+
else
500+
{
501+
return bitwise_rshift(self, other, avx2 {});
502+
}
503+
}
504+
463505
// rotl
464506
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
465507
XSIMD_INLINE batch<T, A> rotl(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_256>) noexcept
@@ -505,18 +547,18 @@ namespace xsimd
505547
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
506548
XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512vl_256>) noexcept
507549
{
508-
XSIMD_IF_CONSTEXPR(std::is_unsigned<T>::value)
550+
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
509551
{
510-
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
511-
{
512-
return _mm256_rorv_epi32(self, other);
513-
}
514-
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
515-
{
516-
return _mm256_rorv_epi64(self, other);
517-
}
552+
return _mm256_rorv_epi32(self, other);
553+
}
554+
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
555+
{
556+
return _mm256_rorv_epi64(self, other);
557+
}
558+
else
559+
{
560+
return rotr(self, other, avx2 {});
518561
}
519-
return rotr(self, other, avx2 {});
520562
}
521563
template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
522564
XSIMD_INLINE batch<T, A> rotr(batch<T, A> const& self, int32_t other, requires_arch<avx512vl_256>) noexcept
@@ -529,18 +571,83 @@ namespace xsimd
529571
{
530572
constexpr auto bits = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
531573
static_assert(count < bits, "Count must be less than the number of bits in T");
532-
XSIMD_IF_CONSTEXPR(std::is_unsigned<T>::value)
574+
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
533575
{
534-
XSIMD_IF_CONSTEXPR(sizeof(T) == 4)
535-
{
536-
return _mm256_ror_epi32(self, count);
537-
}
538-
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
539-
{
540-
return _mm256_ror_epi64(self, count);
541-
}
576+
return _mm256_ror_epi32(self, count);
542577
}
543-
return rotr<count>(self, avx2 {});
578+
else XSIMD_IF_CONSTEXPR(sizeof(T) == 8)
579+
{
580+
return _mm256_ror_epi64(self, count);
581+
}
582+
else
583+
{
584+
return rotr<count>(self, avx2 {});
585+
}
586+
}
587+
588+
// compress — native EVEX VPCOMPRESS{PS,PD,Q,D} for the widths with VL
589+
// forms. 8/16-bit need AVX512_VBMI2, so they fall through to common{}.
590+
template <class A>
591+
XSIMD_INLINE batch<float, A> compress(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_256>) noexcept
592+
{
593+
return _mm256_maskz_compress_ps(mask.mask(), self);
594+
}
595+
template <class A>
596+
XSIMD_INLINE batch<double, A> compress(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_256>) noexcept
597+
{
598+
return _mm256_maskz_compress_pd(mask.mask(), self);
599+
}
600+
template <class A>
601+
XSIMD_INLINE batch<int32_t, A> compress(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
602+
{
603+
return _mm256_maskz_compress_epi32(mask.mask(), self);
604+
}
605+
template <class A>
606+
XSIMD_INLINE batch<uint32_t, A> compress(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
607+
{
608+
return _mm256_maskz_compress_epi32(mask.mask(), self);
609+
}
610+
template <class A>
611+
XSIMD_INLINE batch<int64_t, A> compress(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
612+
{
613+
return _mm256_maskz_compress_epi64(mask.mask(), self);
614+
}
615+
template <class A>
616+
XSIMD_INLINE batch<uint64_t, A> compress(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
617+
{
618+
return _mm256_maskz_compress_epi64(mask.mask(), self);
619+
}
620+
621+
// expand
622+
template <class A>
623+
XSIMD_INLINE batch<float, A> expand(batch<float, A> const& self, batch_bool<float, A> const& mask, requires_arch<avx512vl_256>) noexcept
624+
{
625+
return _mm256_maskz_expand_ps(mask.mask(), self);
626+
}
627+
template <class A>
628+
XSIMD_INLINE batch<double, A> expand(batch<double, A> const& self, batch_bool<double, A> const& mask, requires_arch<avx512vl_256>) noexcept
629+
{
630+
return _mm256_maskz_expand_pd(mask.mask(), self);
631+
}
632+
template <class A>
633+
XSIMD_INLINE batch<int32_t, A> expand(batch<int32_t, A> const& self, batch_bool<int32_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
634+
{
635+
return _mm256_maskz_expand_epi32(mask.mask(), self);
636+
}
637+
template <class A>
638+
XSIMD_INLINE batch<uint32_t, A> expand(batch<uint32_t, A> const& self, batch_bool<uint32_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
639+
{
640+
return _mm256_maskz_expand_epi32(mask.mask(), self);
641+
}
642+
template <class A>
643+
XSIMD_INLINE batch<int64_t, A> expand(batch<int64_t, A> const& self, batch_bool<int64_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
644+
{
645+
return _mm256_maskz_expand_epi64(mask.mask(), self);
646+
}
647+
template <class A>
648+
XSIMD_INLINE batch<uint64_t, A> expand(batch<uint64_t, A> const& self, batch_bool<uint64_t, A> const& mask, requires_arch<avx512vl_256>) noexcept
649+
{
650+
return _mm256_maskz_expand_epi64(mask.mask(), self);
544651
}
545652

546653
// all

0 commit comments

Comments
 (0)