AVX2寄存器位反转

5

有没有一种(快速)的方法在AVX2寄存器中执行32位整数值的位翻转?例如:

_mm256_set1_epi32(2732370386); 
<do something here>
//binary: 10100010110111001010100111010010 => 1001011100101010011101101000101
//register contains 1268071237 which is decimal representation of 1001011100101010011101101000101

你想要在AVX2整数寄存器中反转单个int32的位,还是想要反转这8个整数中的每一个的位? - John Zwinck
1
@JohnZwinck,这并不重要:一旦我明白如何做到这一点,我可以随意在寄存器内洗牌32位值。 - John Smith
旧的方法(反转字节,使用pshufb反转4个字节的组,或者使用OR运算)可以推广到AVX2,但我暂时找不到重复的内容。 - harold
2
你可以使用任何标准的位操作技巧来反转字节,然后重新排列字节(例如参见《黑客的乐趣》和此列表)。 - Paul R
1个回答

9

由于我找不到合适的重复内容,所以我会发布它。

这里的主要想法是利用pshufb的双重用途,即并行16个条目的表查找来翻转每个半字节的位。翻转字节很明显。翻转每个字节中两个半字节的顺序可以通过将其构建到查找表中(保存移位)或通过显式将低部分半字节向上移动(节省LUT)来完成。

总体上类似于以下内容,未经测试:

__m256i rbit32(__m256i x) {
    __m256i shufbytes = _mm256_setr_epi8(3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12);
    __m256i luthigh = _mm256_setr_epi8(0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15, 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15);
    __m256i lutlow = _mm256_slli_epi16(luthigh, 4);
    __m256i lowmask = _mm256_set1_epi8(15);
    __m256i rbytes = _mm256_shuffle_epi8(x, shufbytes);
    __m256i high = _mm256_shuffle_epi8(lutlow, _mm256_and_si256(rbytes, lowmask));
    __m256i low = _mm256_shuffle_epi8(luthigh, _mm256_and_si256(_mm256_srli_epi16(rbytes, 4), lowmask));
    return _mm256_or_si256(low, high);
}

在循环的典型情况下,这些负载应该被提取出来。
有趣的是Clang使用4个洗牌指令,它会复制第一个洗牌指令。

@LưuVĩnhPhúc,您链接中的解决方案翻转字节而不是32位整数。 - wim
1
Gcc7.2在这里也非常不智能。它将lutlow转换成一个独立的内存常量,但是在加载后会对其进行位移操作(没有将其用于其他任何内容)。我认为它只是创建了luthigh的一个副本,但是使用了.value汇编指令(16位块)而不是.byte - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接