C++ 在叮当声下解决缺少Yz机器约束的问题?

C++ 在叮当声下解决缺少Yz机器约束的问题?,c++,clang,sse,inline-assembly,sha,C++,Clang,Sse,Inline Assembly,Sha,如果未定义\uuuuusha\uuuuu,我们将使用内联汇编使SHA指令可用。根据GCC,我们使用: GCC_INLINE __m128i GCC_INLINE_ATTRIB MM_SHA256RNDS2_EPU32(__m128i a, const __m128i b, const __m128i c) { asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "Yz" (c)); return a; } Clang不使用GCC

如果未定义
\uuuuusha\uuuuu
,我们将使用内联汇编使SHA指令可用。根据GCC,我们使用:

GCC_INLINE __m128i GCC_INLINE_ATTRIB
MM_SHA256RNDS2_EPU32(__m128i a, const __m128i b, const __m128i c)
{
    asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "Yz" (c));
    return a;
}
Clang不使用GCC(请参阅和),这是
sha256rnds2
指令所要求的:

我们为Clang添加了一个
mov

asm ("mov %2, %%xmm0; sha256rnds2 %%xmm0, %1, %0" : "+x"(a) : "xm"(b), "x" (c) : "xmm0");
性能每字节下降约3个周期。在我的2.2 GHz Celeron J3455测试机(Goldmont带SHA扩展)上,大约是230 MiB/s。这不是小事

从反汇编来看,当执行两轮时,Clang并没有围绕SHA的
k
进行优化:

Breakpoint 2, SHA256_SSE_SHA_HashBlocks (state=0xaaa3a0,
    data=0xaaa340, length=0x40) at sha.cpp:1101
1101        STATE1 = _mm_loadu_si128((__m128i*) &state[4]);
(gdb) disass
Dump of assembler code for function SHA256_SSE_SHA_HashBlocks(unsigned int*, unsigned int const*, unsigned long):
   0x000000000068cdd0 <+0>:     sub    $0x308,%rsp
   0x000000000068cdd7 <+7>:     movdqu (%rdi),%xmm0
   0x000000000068cddb <+11>:    movdqu 0x10(%rdi),%xmm1
   ...
   0x000000000068ce49 <+121>:   movq   %xmm2,%xmm0
   0x000000000068ce4d <+125>:   sha256rnds2 %xmm0,0x2f0(%rsp),%xmm1
   0x000000000068ce56 <+134>:   pshufd $0xe,%xmm2,%xmm3
   0x000000000068ce5b <+139>:   movdqa %xmm13,%xmm2
   0x000000000068ce60 <+144>:   movaps %xmm1,0x2e0(%rsp)
   0x000000000068ce68 <+152>:   movq   %xmm3,%xmm0
   0x000000000068ce6c <+156>:   sha256rnds2 %xmm0,0x2e0(%rsp),%xmm2
   0x000000000068ce75 <+165>:   movdqu 0x10(%rsi),%xmm3
   0x000000000068ce7a <+170>:   pshufb %xmm8,%xmm3
   0x000000000068ce80 <+176>:   movaps %xmm2,0x2d0(%rsp)
   0x000000000068ce88 <+184>:   movdqa %xmm3,%xmm4
   0x000000000068ce8c <+188>:   paddd  0x6729c(%rip),%xmm4        # 0x6f4130
   0x000000000068ce94 <+196>:   movq   %xmm4,%xmm0
   0x000000000068ce98 <+200>:   sha256rnds2 %xmm0,0x2d0(%rsp),%xmm1
   ...
我猜我们选择的内联asm指令有点不合适

我们如何解决叮当声下缺少
Yz
机器约束的问题?什么模式可以避免优化代码中的中间移动


正在尝试使用:

结果:

In file included from sha.cpp:24:
./cpu.h:831:22: warning: ignored asm label 'xmm0' on automatic variable
        const __m128i k asm("xmm0") = c;
                            ^
./cpu.h:833:7: error: invalid operand for instruction
        asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k));
             ^
<inline asm>:1:21: note: instantiated into assembly here
        sha256rnds2 %xmm1, 752(%rsp), %xmm0
                           ^~~~~~~~~~
In file included from sha.cpp:24:
./cpu.h:833:7: error: invalid operand for instruction
        asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k));
             ^
<inline asm>:1:21: note: instantiated into assembly here
        sha256rnds2 %xmm3, 736(%rsp), %xmm1
                           ^~~~~~~~~~
...
包含在sha.cpp:24中的文件中:
./cpu.h:831:22:警告:忽略自动变量上的asm标签“xmm0”
常数m128i asm(“xmm0”)=c;
^
./cpu.h:833:7:错误:指令的操作数无效
asm(“sha256rnds2%2、%1、%0”:“+x”(a):“xm”(b),“x”(k));
^
:1:21:注意:此处实例化为程序集
sha256rnds2%xmm1,752%(rsp),%xmm0
^~~~~~~~~~
在sha.cpp:24中包含的文件中:
./cpu.h:833:7:错误:指令的操作数无效
asm(“sha256rnds2%2、%1、%0”:“+x”(a):“xm”(b),“x”(k));
^
:1:21:注意:此处实例化为程序集
sha256rnds2%xmm3,736%(rsp),%xmm1
^~~~~~~~~~
...

我根据标签
内联汇编创建了这个答案,没有提到具体的语言。已经假定使用语言的扩展

如果
Yz
约束不可用,您可以尝试创建一个临时变量来告诉CLANG要使用哪个寄存器,而不是约束。您可以通过所谓的:

您可以定义本地寄存器变量并将其与指定寄存器关联,如下所示:

 register int *foo asm ("r12");
这里r12是应该使用的寄存器的名称。请注意,这与用于定义全局寄存器变量的语法相同,但对于局部变量,声明将出现在函数中。register关键字是必需的,不能与static组合。注册表名必须是目标平台的有效注册表名

在您的情况下,您希望强制使用
xmm0
寄存器。您可以使用显式寄存器将
c
参数指定给临时变量,并将该临时变量用作扩展内联程序集的参数。这是GCC/CLANG中显式寄存器的主要用途

编译器现在应该能够提供一些优化,因为它对如何使用
xmm0
寄存器有更多的了解

当您放置
mov%2时,%%xmm0到模板CLANG(和GCC)中,不要对指令进行任何优化。基本部件和扩展部件模板是一个黑盒,它只知道如何基于约束进行基本替换


下面是使用上述方法进行的拆卸。它是用
clang++
-std=c++03
编译的。额外的动作不再出现:

断点1,SHA256_SSE_SHA_散列块(状态=0x7FFFFFAE60,
sha处的数据=0x7FFFFFAE00,长度=0x40)。cpp:1101
1101状态1=_mm_loadu_si128((_m128i*)状态[4]);
(gdb)disass
函数SHA256_SSE_SHA_哈希块的汇编代码转储(无符号int*、无符号int常量*、无符号long):
0x000000000068cf60:低于$0x308,%rsp
0x000000000068cf67:movdqu(%rdi),%xmm0
0x000000000068cf6b:movdqu 0x10(%rdi),%xmm1
...
0x000000000068cfe6:paddd 0x670e2(%rip),%xmm0#0x6f40d0
0x000000000068cfee:sha256rnds2%xmm0,0x2f0(%rsp),%xmm2
0x000000000068cff7:pshufd$0xe,%xmm0,%xmm1
0x000000000068cffc:movdqa%xmm1,%xmm0
0x000000000068d000:movaps%xmm2,0x2e0(%rsp)
0x0000000000068D008:sha256rnds2%xmm0,0x2e0(%rsp),%xmm3
0x000000000068d011:movdqu 0x10(%rsi),%xmm5
0x000000000068d016:pshufb%xmm9,%xmm5
0x000000000068d01c:movaps%xmm3,0x2d0(%rsp)
0x000000000068d024:movdqa%xmm5,%xmm0
0x000000000068d028:padd 0x670b0(%rip),%xmm0#0x6f40e0
0x000000000068d030:sha256rnds2%xmm0,0x2d0(%rsp),%xmm2
...

谢谢你,迈克尔。GCC称之为。Clang不会编译它。我尝试过多种不同的方式使用C++和C转换。我认为这与
\uuu m128i
类型有关。查看您的更新,您缺少临时变量上的
寄存器
类型限定符,这是使用显式寄存器所必需的。尝试
register const\uuu m128i k asm(“xmm0”)=c登记> <代码>,所以我们不能使用它。(我们是C++库,这个代码在头文件中。也许我需要找到一个编译器特定的解决方案,比如<代码>登记寄存器< /COD>或<代码> GCCyReals)。我知道C++中的“代码>寄存器< /C>”被“弃用”,但你说“正在消失”。这已经成为C++17标准的一部分了吗?我一直没有跟上,谷歌只提出了一个建议,没有确定的信息。即使是这个建议,它仍然是“保留”的,所以使用它不应该是个问题。我不会担心C或C++太多。您的回答可能有助于使用Clang、SSE和内联汇编的人员。这些都是重要的部分。你得到了我的支持。我想下一个问题是,我们能把规则放宽到什么程度。在显式寄存器变量的上下文中使用
register
,只是使用扩展,这样就不会违反语言。我可能会问一下Clang和/或GCC开发人员。@jww好吧,如果你使用c++17,它会移到“不保留”
const __m128i k asm("xmm0") = c;
asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k));
return a;
In file included from sha.cpp:24:
./cpu.h:831:22: warning: ignored asm label 'xmm0' on automatic variable
        const __m128i k asm("xmm0") = c;
                            ^
./cpu.h:833:7: error: invalid operand for instruction
        asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k));
             ^
<inline asm>:1:21: note: instantiated into assembly here
        sha256rnds2 %xmm1, 752(%rsp), %xmm0
                           ^~~~~~~~~~
In file included from sha.cpp:24:
./cpu.h:833:7: error: invalid operand for instruction
        asm ("sha256rnds2 %2, %1, %0" : "+x"(a) : "xm"(b), "x" (k));
             ^
<inline asm>:1:21: note: instantiated into assembly here
        sha256rnds2 %xmm3, 736(%rsp), %xmm1
                           ^~~~~~~~~~
...
 register int *foo asm ("r12");
GCC_INLINE __m128i GCC_INLINE_ATTRIB
MM_SHA256RNDS2_EPU32(__m128i a, const __m128i b, const __m128i c)
{
   register const __m128i tmpc asm("xmm0") = c;
   __asm__("sha256rnds2 %2, %1, %0" : "+x"(a) : "x"(b), "x" (tmpc));
    return a;
}