pinsrd / _mm_insert_epi32 与字节指针对齐?

pinsrd / _mm_insert_epi32 with byte pointer alignment?

类似于this question,我想将几​​个24位值收集到一个SSE/AVX寄存器的32位双字中。进一步:

AVX2(高性能?)收集解决方案可以,但我还需要 AVX 前支持。看起来带有指示 1 字节对齐的 SIB 字节的 pinsrd 完全符合我的要求,但我无法弄清楚如何让编译器发出此指令编码...

使用标准内在:

uint32_t *p = &base[offset];
vec = _mm_insert_epi32(vec, *p, 1);  // for each dword...

产生合理的编码,假设偏移量对齐:

660f3a2244_b5_0001 pinsrd   [=12=]x1, (%rbp,%rsi,4), %xmm0

但是,我想实际发出:

660f3a2244_35_0001 pinsrd   [=13=]x1, (%rbp,%rsi), %xmm0

并手动预乘偏移量 3。

这种编码(通过十六进制编辑链接的二进制文件进行测试)似乎工作正常。但是……我怎么能发射它呢?没有多少类型转换或属性 __align__ 似乎有效。显而易见的方法:

uint8_t *p = &base[offset*3];
vec = _mm_insert_epi32(vec, *p, 1);

当然在插入之前取消引用一个带有零扩展的字节到双字。

我的内联 asm 尝试:

static inline __m128i __attribute__((always_inline))
_mm_insertu_epi32(__m128i a, void *b, long o, const int8_t imm8)
{
    __asm__("pinsrd %3, (%1, %2), %0" : "+x"(a) : "r"(b), "r"(o), "i"(imm8));
    return a;
}

产量:

660f3a22041601      pinsrd  [=16=]x1, (%rsi,%rdx), %xmm0

这很有希望,但似乎完全混淆了优化器;周围的所有代码都被扰乱得面目全非。

有没有不用纯 asm 的方法? (我想使用内在...)

另请参阅:Dereference pointers in XMM register

@harold,谢谢。

我已经在执行 movd,然后执行几个 pinsrd(如 clang。)但我在 godbolt 上看到 clang/gcc/icc 使用各种解包模式,所以我将对它们进行分析。

不幸的是,

"Just avoid gather" 不是解决方案。但你是对的,内在确实适用于任意对齐。简单的指针转换最终会做正确的事情(即产生可能未对齐的地址):

__m128i gather32_scale4(int *b, long o0, long o1, long o2, long o3)
{
    return _mm_set_epi32(b[o0], b[o1], b[o2], b[o3]);
    //  movd    xmm0, dword ptr [rdi + 4*r8]
    //  pinsrd  xmm0, dword ptr [rdi + 4*rcx], 1
    //  pinsrd  xmm0, dword ptr [rdi + 4*rdx], 2
    //  pinsrd  xmm0, dword ptr [rdi + 4*rsi], 3
}

__m128i gather32_scale1(int *b, long o0, long o1, long o2, long o3)
{
    return _mm_set_epi32(
        *(int *)&((char *)b)[o0],
        *(int *)&((char *)b)[o1],
        *(int *)&((char *)b)[o2],
        *(int *)&((char *)b)[o3]);
    //  movd    xmm0, dword ptr [rdi + r8]
    //  pinsrd  xmm0, dword ptr [rdi + rcx], 1
    //  pinsrd  xmm0, dword ptr [rdi + rdx], 2
    //  pinsrd  xmm0, dword ptr [rdi + rsi], 3
}

(和手写的类似 _mm_insert_epi32)