我正在使用Power9并利用称为DARN的硬件随机数生成器指令。我有以下内联汇编:
uint64_t val;
__asm__ __volatile__ (
"xor 3,3,3 \n" // r3 = 0
"addi 4,3,-1 \n" // r4 = -1, failure
"1: \n"
".byte 0xe6, 0x05, 0x61, 0x7c \n" // r3 = darn 3, 1
"cmpd 3,4 \n" // r3 == -1?
"beq 1b \n" // retry on failure
"mr %0,3 \n" // val = r3
: "=g" (val) : : "r3", "r4", "cc"
);
我不得不在mr %0,3
上添加"=g" (val)
,因为我无法让GCC使用"=r3" (val)
来生成期望的代码。另请参见Error: matching constraint not valid in output operand。
反汇编显示:
(gdb) b darn.cpp : 36
(gdb) r v
...
Breakpoint 1, DARN::GenerateBlock (this=<optimized out>,
output=0x7fffffffd990 "\b", size=0x100) at darn.cpp:77
77 DARN64(output+i*8);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.ppc64le libgcc-4.8.5-28.el7_5.1.ppc64le libstdc++-4.8.5-28.el7_5.1.ppc64le
(gdb) disass
Dump of assembler code for function DARN::GenerateBlock(unsigned char*, unsigned long):
...
0x00000000102442b0 <+48>: addi r10,r8,-8
0x00000000102442b4 <+52>: rldicl r10,r10,61,3
0x00000000102442b8 <+56>: addi r10,r10,1
0x00000000102442bc <+60>: mtctr r10
=> 0x00000000102442c0 <+64>: xor r3,r3,r3
0x00000000102442c4 <+68>: addi r4,r3,-1
0x00000000102442c8 <+72>: darn r3,1
0x00000000102442cc <+76>: cmpd r3,r4
0x00000000102442d0 <+80>: beq 0x102442c8 <DARN::GenerateBlock(unsigned char*, unsigned long)+72>
0x00000000102442d4 <+84>: mr r10,r3
0x00000000102442d8 <+88>: stdu r10,8(r9)
通知GCC忠实地复制了以下内容:
0x00000000102442d4 <+84>: mr r10,r3
0x00000000102442d8 <+88>: stdu r10,8(r9)
我如何让GCC将两条说明折叠成:
0x00000000102442d8 <+84>: stdu r3,8(r9)
答案 0 :(得分:2)
GCC永远不会删除属于asm模板的文本;除了代替%operand
之外,它甚至不解析它。实际上,这只是在将asm发送到汇编程序之前的文本替换。
您必须从内联asm模板中删除mr
,并告诉gcc您的输出位于r3
中(或使用内存目标输出操作数,但不要这样做) 。如果您的inline-asm模板以mov
指令开头或结尾,则通常做错了。
在没有特定注册限制的平台上,使用register uint64_t foo asm("r3");
强制"=r"(foo)
选择r3
。
(尽管ISO C ++ 17删除了register
关键字,但此GNU扩展名仍可与-std=c++17
一起使用。如果要避免使用{{1},也可以使用register uint64_t foo __asm__("r3");
}关键字。您可能仍需要将asm
视为使用此扩展名的源中的保留字;没关系,ISO C ++从基本语言中删除它不会强制实现 not 使用它作为扩展的一部分。)
或者更好的是,不要对注册号进行硬编码。使用支持DARN指令的汇编器。 (但显然,它是如此之新,以至于最新的clang也没有它,并且您只希望将此内联asm作为gcc的后备版本,以至于无法支持the __builtin_darn()
intrinsic)
使用这些约束也可以删除寄存器设置,并在内联asm语句之前使用register
/ foo=0
,并使用bar=-1
。
但是请注意,"+r"(foo)
的输出寄存器是只写的。无需先将darn
设为零。我找到了IBM的POWER ISA指令集手册的副本,该手册足够新,可以在此处包含r3
:https://wiki.raptorcs.com/w/images/c/cb/PowerISA_public.v3.0B.pdf#page=96
实际上,您根本不需要在asm中循环,您可以将其留给C并且仅包装一条asm指令,就像设计inline-asm一样。
darn
干净地(on the Godbolt compiler explorer)编译为
uint64_t random_asm() {
register uint64_t val asm("r3");
do {
//__asm__ __volatile__ ("darn 3, 1");
__asm__ __volatile__ (".byte 0x7c, 0x61, 0x05, 0xe6 # gcc asm operand = %0\n" : "=r" (val));
} while(val == -1ULL);
return val;
}
只需更少的设置就可以与循环一样紧密。 (您确定您甚至需要在asm指令之前将random_asm():
.L6: # compiler-generated label, no risk of name clashes
.byte 0x7c, 0x61, 0x05, 0xe6 # gcc asm operand = 3
cmpdi 7,3,-1 # compare-immediate
beq 7,.L6
blr
设为零吗?)
此函数可以内联到您想要的任何位置,从而允许gcc发出直接读取r3
的存储指令。
在实践中,您将按照手册中的建议使用重试计数器:如果硬件RNG损坏,则可能永远导致失败,因此您应该使用PRNG。 (与x86的r3
相同)
提供随机数(
rdrand
)-编程说明获得错误值时,软件为 希望重复该操作。如果没有错误 经过几次尝试仍未获得价值, 软件随机数生成方法 应该使用。推荐数量 尝试可能是特定于实现的。在里面 如果没有其他指导,应尝试十次 足够。
darn
-归零在大多数固定指令宽度的ISA上效率不高,因为移动立即数很短,因此无需检测和特殊化xor 。 (因此,CPU设计不会在其上花费晶体管)。此外,C ++ 11 xor
require 的PPC asm依赖项规则对输入寄存器具有依赖项,因此即使设计人员也不能破坏依赖项想要它。异或归零只是x86上的一件事,也许还有其他一些可变宽度的ISA。
像std::memory_order_consume
https://godbolt.org/z/-gHI4C一样使用li r3, 0
。