在内联asm中使用特定的zmm寄存器

时间:2018-08-25 05:51:42

标签: gcc assembly x86 inline-assembly

我可以告诉gcc-style inline assembly将我的zmm变量放到特定于 zmm31的寄存器中,例如 transform: rotate(var(--rotation-degrees)+'deg'); 吗?

1 个答案:

答案 0 :(得分:3)

就像根本没有特定寄存器约束的目标(如ARM)一样,使用local register variables获取广泛的约束来为asm语句选择特定寄存器。编译器仍然可以进行其他优化,因为寄存器本地唯一记录的保证效果是针对asm输入/输出的。

尽管没有asm,编译器也会首选指定的寄存器。 (因此,您可以使用register int ebx asm("ebx"); return ebx;之类的东西编写看起来可行但不安全的代码。即使当前的gcc更喜欢使用指定的寄存器,GCC文档也可以保证行为/面向未来。当约束与指定的寄存器不兼容时浪费指令,请参见下文。)

无论如何,这种使用注册自动取款机 local 变量是它们保证可以使用的 only

#include <immintrin.h>
__m512i foo() {
    register __m512i z31 asm("zmm31") = _mm512_set1_epi32(123);
    register __m512i z30 asm("zmm30");

    asm("vmovdqa64 %1, %0  # from inline asm"
        : "=v"(z30)
        : "v"(z31)
       );
    return z30;
}

the Godbolt compiler explorer上,使用clang6.0对此进行编译:

    # clang -O3 -march=skylake-avx512
    vbroadcastss    .LCPI0_0(%rip), %zmm31 # zmm31 = [1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43]
    vmovdqa64       %zmm31, %zmm30        # from inline asm
    vmovaps %zmm30, %zmm0
    retq

和gcc8.2:

# gcc -O3 -march=skylake-avx512
foo():
    movl    $123, %eax
    vpbroadcastd    %eax, %zmm31
    vmovdqa64 %zmm31, %zmm30  # from inline asm
    vmovdqa64       %zmm30, %zmm0
    ret

请注意"v"约束,该约束允许任何EVEX向量寄存器(0..31),而"x"仅允许前16个。"x"是记录为“任何SSE寄存器”,但也适用于AVX YMM寄存器。 https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html

为此使用"x"不会产生任何警告,但是在gcc "x"胜过寄存器变量声明的情况下,因此选择了%zmm2和%zmm1(奇怪的不是{{1 }},因此需要采取额外的措施)。因此,register-asm声明确实浪费了我们的效率。

使用clang仍使用zmm31和zmm30,显然违反了zmm0约束,因此如果您在寄存器操作数的XMM或YMM部分使用了没有EVEX版本的指令,它将无法汇编,例如AVX2 vpcmpeqd ymm,ymm,ymm(比较向量,而不比较遮罩)。 (In GNU C inline asm, what're the modifiers for xmm/ymm/zmm for a single operand?)。

"x"

使用clang时,每个操作数都会出错;我猜clang不支持//#ifndef __clang__ __m512i broken_with_clang() { register __m512i z31 asm("zmm31") = _mm512_set1_epi32(123); register __m512i z30 asm("zmm30") = _mm512_setzero_si512(); // notice that gcc still inits these in zmm31 and 30, *then* copies // so register asm costs us efficiency. // AVX512 only has compares into k registers, not into YMM registers. asm("vpcmpeqd %t1, %t0, %t0 # from inline asm. input was %0" : "+x"(z30) : "x"(z31) ); return z30; } //#endif 修饰符来获取寄存器的YMM名称(因为即使我完全删除了t东西,clang6.0也会失败)。

register ... asm()

但是gcc编译就可以了:

<source>:21:9: error: invalid operand in inline asm: 'vpcmpeqd ${1:t}, ${0:t}, ${0:t}  # from inline asm. input was $0'
    asm("vpcmpeqd %t1, %t0, %t0  # from inline asm. input was %0"
        ^
...
<source>:21:9: error: unknown token in expression
<inline asm>:1:11: note: instantiated into assembly here
        vpcmpeqd , ,   # from inline asm. input was %zmm30