类似于this question ,我想将几个 24 位值收集到 SSE/AVX 寄存器的 32 位双字中。进一步:
- 每个值都位于距基指针不连续的偏移处
- 每个值的偏移量只有 1 字节对齐
- 我可以确保读取超出(或之前)每个值的 vector 是安全的
AVX2(高性能?)收集解决方案可以,但我还需要 AVX 之前的支持。看起来带有指示 1 字节对齐的 SIB 字节的pinsrd 正是我想要的,但我不知道如何让编译器发出此指令编码...
使用标准内在函数:
uint32_t *p = &base[offset];
vec = _mm_insert_epi32(vec, *p, 1); // for each dword...
假设偏移量对齐,产生合理的编码:
660f3a2244_b5_0001 pinsrd $0x1, (%rbp,%rsi,4), %xmm0
但是,我想实际发出:
660f3a2244_35_0001 pinsrd $0x1, (%rbp,%rsi), %xmm0
并手动预乘偏移 3。
这种编码(通过十六进制编辑链接的二进制文件进行测试)似乎工作得很好。但是……我怎样才能发出它呢?没有大量的类型转换或属性
__align__
似乎有效。显而易见的方法:
uint8_t *p = &base[offset*3];
vec = _mm_insert_epi32(vec, *p, 1);
当然,在插入之前会通过零扩展将一个字节取消引用为双字。
我的内联汇编尝试:
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 $0x1, (%rsi,%rdx), %xmm0
这是有希望的,但似乎完全混淆了优化器;所有周围的代码都被扰乱得无法识别。
有没有办法在不使用纯汇编的情况下做到这一点? (我想使用内在...)
最佳答案
@harold,谢谢。
我已经在执行 movd 和几个pinsrd(例如 clang)。但是我在 godbolt 上看到 clang/gcc/icc 使用各种解包模式,所以我将对它们进行分析。
不幸的是,“避免聚集”并不是解决方案。但你是对的,内在确实可以与任意对齐一起工作。简单的指针转换最终会做正确的事情(即产生可能未对齐的地址):
__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类似)
关于c - pinsrd/_mm_insert_epi32 与字节指针对齐?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43694221/