Pet*_*des 8 c x86 gcc sse intrinsics
我没有特定的用例; 我问这是否真的是英特尔内在函数中的设计缺陷/限制,或者我是否只是遗漏了某些内容.
如果你想将标量浮点数与现有向量相结合,那么在没有高元素归零或使用英特尔内在函数将标量广播到向量中的情况下似乎没有办法实现.我没有研究过GNU C本机向量扩展和相关的内置函数.
如果额外的内在优化了,这不会太糟糕,但它不与gcc(5.4或6.2).也没有好的方法可以使用pmovzx或insertps作为负载,因为他们的内在函数只采用向量args的相关原因.(并且gcc不会将标量 - >向量加载到asm指令中.)
__m128 replace_lower_two_elements(__m128 v, float x) {
__m128 xv = _mm_set_ss(x); // WANTED: something else for this step, some compilers actually compile this to a separate insn
return _mm_shuffle_ps(v, xv, 0); // lower 2 elements are both x, and the garbage is gone
}
Run Code Online (Sandbox Code Playgroud)
gcc 5.3 -march = nehalem -O3输出,启用SSE4.1并调整该Intel CPU :(没有SSE4.1会更糟;多个指令将上层元素归零).
insertps xmm1, xmm1, 0xe # pointless zeroing of upper elements. shufps only reads the low element of xmm1
shufps xmm0, xmm1, 0 # The function *should* just compile to this.
ret
Run Code Online (Sandbox Code Playgroud)
TL:DR:这个问题的其余部分只是询问你是否真的可以有效地做到这一点,如果不是为什么不这样做.
clang的shuffle-optimizer得到了这个权利,并且不会浪费指令将高元素归零(_mm_set_ss(x)),或者将标量复制到它们中(_mm_set1_ps(x)).不是编写编译器必须优化的东西,而不应该首先在C中"高效"地编写它?即使是最近的gcc 也没有优化它,所以这是一个真正的(但很小的)问题.
如果有一个标量 - > 128b当量的话,这是可能的__m256 _mm256_castps128_ps256 (__m128 a).即__m128,如果标量float/double已经在xmm寄存器中,则在上部元素中生成一个未定义的垃圾,在low元素中生成一个float,编译为零asm指令.
以下内在函数都不存在,但它们应该存在.
_mm256_castps128_ps256上述.标量已存在的最常见解决方案.__m128 _mm_move_ss_scalar (__m128 a, float s):a用标量替换向量的低元素s.如果有一个通用标量 - > __ m128(前一个要点),这实际上并不是必需的.(reg-reg形式的movss合并,与零形式的加载形式不同,并且与movd两种情况下的零上部元素不同.要复制一个没有错误依赖的标量浮点数的寄存器,请使用movaps).__m128i _mm_loadzxbd (const uint8_t *four_bytes)和其他尺寸的PMOVZX/PMOVSX: AFAICT,没有一种安全的方法可以将PMOVZX内在函数用作负载,因为不方便的安全方法不能用gcc优化.__m128 _mm_insertload_ps (__m128 a, float *s, const int imm8). INSERTPS作为一个加载行为不同:imm8的高2位被忽略,它总是将标量置于有效地址(而不是内存中向量的元素).这使得它可以处理非16B对齐的地址,并且如果在float未映射的页面之前的右边,即使没有错误也可以工作.
与PMOVZX一样,gcc无法将上元素归零折叠_mm_load_ss()为INSERTPS的内存操作数.(注意,如果imm8的高2位不是零,那么_mm_insert_ps(xmm0, _mm_load_ss(), imm8)可以编译为insertps xmm0,xmm0,foo,使用不同的imm8,在vec中为零元素,如果src元素实际上是MOVSS从内存生成的零.Clang实际使用在这种情况下XORPS/BLENDPS)
是否有任何可行的解决方法来模拟任何安全的(不要在-O0中断,例如加载16B可能触及下一页和段错误),并且有效(没有浪费的指令在-O3与当前的gcc和clang至少,最好还有其他主要的编译器)?优选地,也以可读的方式,但是如果需要,它可以放在内联包装函数之后__m128 float_to_vec(float a){ something(a); }.
英特尔有没有什么理由不引入这样的内在函数?他们可以添加一个浮点数 - > __ m128,同时添加未定义的上层元素_mm256_castps128_ps256. 这是编译器内部的问题,难以实现吗? 也许特别是ICC内部人员?
x86-64(SysV或MS __vectorcall)上的主要调用约定采用xmm0中的第一个FP arg,并返回xmm0中的标量FP args,其中上部元素未定义.(有关ABI文档,请参阅 x86标记wiki).这意味着编译器在具有未知上层元素的寄存器中具有标量float/double并不罕见.这在矢量化内循环中很少见,因此我认为避免这些无用的指令通常只会节省一些代码大小.
pmovzx情况更严重:这可能是你在内部循环中使用的东西(例如,对于VPERMD shuffle掩码的LUT,在高速缓存占用空间中保存4倍而在存储器中存储填充为32位的每个索引).
pmovzx-as-a-load问题一直困扰着我一段时间,这个问题的原始版本让我想到了在xmm寄存器中使用标量浮点的相关问题.pmovzx作为一个负载可能比标量更多的用例 - > __ m128.
使用 GNU C 内联 asm 是可行的,但是这很丑陋并且无法进行许多优化,包括常量传播(https://gcc.gnu.org/wiki/DontUseInlineAsm)。 这不会是公认的答案。我将此添加为答案而不是问题的一部分,因此问题保持短暂 不是很大。
// don't use this: defeating optimizations is probably worse than an extra instruction
#ifdef __GNUC__
__m128 float_to_vec_inlineasm(float x) {
__m128 retval;
asm ("" : "=x"(retval) : "0"(x)); // matching constraint: provide x in the same xmm reg as retval
return retval;
}
#endif
Run Code Online (Sandbox Code Playgroud)
这确实ret根据需要编译为单个,并将内联以让您shufps将标量转换为向量:
gcc5.3
float_to_vec_and_shuffle_asm(float __vector(4), float):
shufps xmm0, xmm1, 0 # tmp93, xv,
ret
Run Code Online (Sandbox Code Playgroud)
在Godbolt 编译器资源管理器上查看此代码。
这在纯汇编语言中显然是微不足道的,在这种情况下,您不必与编译器进行斗争以使其不发出您不想要或不需要的指令。
我还没有找到任何真正的方法来编写__m128 float_to_vec(float a){ something(a); }只编译为一条ret指令的指令。尝试double使用gcc_mm_undefined_pd()并_mm_move_sd()实际上使代码更糟(请参阅上面的 Godbolt 链接)。没有一个现有的浮动- > __ M128内在的帮助。
题外话:实际 _mm_set_ss() 代码生成策略:当您编写必须将上层元素归零的代码时,编译器会从一系列有趣的策略中进行选择。有些不错,有些奇怪。如您在上面的 Godbolt 链接中所见,同一编译器(gcc 或 clang)上的 double 和 float 之间的策略也有所不同。
一个例子:__m128 float_to_vec(float x){ return _mm_set_ss(x); }编译为:
# gcc5.3 -march=core2
movd eax, xmm0 # movd xmm0,xmm0 would work; IDK why gcc doesn't do that
movd xmm0, eax
ret
Run Code Online (Sandbox Code Playgroud)
# gcc5.3 -march=nehalem
insertps xmm0, xmm0, 0xe
ret
Run Code Online (Sandbox Code Playgroud)
# clang3.8 -march=nehalem
xorps xmm1, xmm1
blendps xmm0, xmm1, 14 # xmm0 = xmm0[0],xmm1[1,2,3]
ret
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
601 次 |
| 最近记录: |