如何最好地模拟 _mm_slli_si128(128 位位移)的逻辑含义,而不是 _mm_bslli_si128

len*_*H01 3 c sse simd intrinsics sse2

查看英特尔内在指南,我看到了这个指令。查看命名模式,含义应该很清楚:“将 128 位寄存器左移固定位数”,但事实并非如此。实际上,它移动了固定数量的字节,这使得它与_mm_bslli_si128.

  • 这是疏忽吗?它不应该像_mm_slli_epi32or那样按位移动_mm_slli_epi64吗?
  • 如果不是,我应该在哪种情况下使用它_mm_bslli_si128
  • 是否有正确执行此操作的汇编指令?
  • 用较小的班次模拟这一点的最佳方法是什么?

Soo*_*nts 5

1 这不是疏忽。该指令确实按字节移动,即 8 位的倍数。

2 无关紧要,_mm_slli_si128并且_mm_bslli_si128是等价的,两者都编译为pslldqSSE2 指令。

至于仿真,假设您有 C++/17,我会这样做。如果您正在编写 C++/14,请替换if constexpr为 normal if,同时在static_assert.

template<int i>
inline __m128i shiftLeftBits( __m128i vec )
{
    static_assert( i >= 0 && i < 128 );
    // Handle couple trivial cases
    if constexpr( 0 == i )
        return vec;
    if constexpr( 0 == ( i % 8 ) )
        return _mm_slli_si128( vec, i / 8 );

    if constexpr( i > 64 )
    {
        // Shifting by more than 8 bytes, the lowest half will be all zeros
        vec = _mm_slli_si128( vec, 8 );
        return _mm_slli_epi64( vec, i - 64 );
    }
    else
    {
        // Shifting by less than 8 bytes.
        // Need to propagate a few bits across 64-bit lanes.
        __m128i low = _mm_slli_si128( vec, 8 );
        __m128i high = _mm_slli_epi64( vec, i );
        low = _mm_srli_epi64( low, 64 - i );
        return _mm_or_si128( low, high );
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我推荐 `_mm_bslli_si128` - 新名称更清楚地暗示它是字节移位。请注意,您实际上并不需要“if constexpr”。如果您的编译器在这种情况下不删除“if(false)”块,那么这可能会帮助您的编译器在调试模式下生成更高效的代码(*咳嗽* MSVC),但仅此而已。使用“i”作为模板参数,它绝对是一个编译时常量,甚至 MSVC 也会在启用优化的情况下删除死代码。 (2认同)