pinsrd / _mm_insert_epi32 与字节指针对齐?
pinsrd / _mm_insert_epi32 with byte pointer alignment?
类似于this question,我想将几个24位值收集到一个SSE/AVX寄存器的32位双字中。进一步:
- 每个值都位于距基指针的非连续偏移处
- 每个值的偏移量只有 1 字节对齐
- 我可以确保读取超过(或之前)每个值的向量是安全的
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)
类似于this question,我想将几个24位值收集到一个SSE/AVX寄存器的32位双字中。进一步:
- 每个值都位于距基指针的非连续偏移处
- 每个值的偏移量只有 1 字节对齐
- 我可以确保读取超过(或之前)每个值的向量是安全的
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)