我正在阅读 ARM 的SVE 白皮书,遇到了一些让我感到奇怪的事情(在非 SVE 示例中):
mov x8, xzr
我不知道这个xzr
寄存器是什么,所以我查了一下,发现ARM 的一些内容表明它在许多情况下是零的同义词。
所以它看起来x8
被初始化为零,这是有道理的,因为它是在x8
用作循环计数器的循环之前执行的。
我不明白的是,为什么不使用文字0
代替xzr
?例如:
mov x8, 0
总而言之,我的问题是:为什么在这里使用xzr
寄存器而不是文字0
?
I think the mov x8, xzr
vs mov x8, #0
comparison is something of a red herring.
As @old_timer's answer shows, there is no encoding gain to be made, and quite likely (although admittedly I haven't checked) little or no pipeline performance gain.
What xzr
gives us, however - in addition to a dummy register as per @InfinitelyManic's answer - is access to a zero-valued operand without having to load and occupy a real register. This has the dual benefit of one less instruction, and one more register available to hold 'real' data.
I think this is an important characteristic that the original 'some content from ARM' referred to in the OP neglects to point out.
That's what I mean by mov x8, xzr
vs mov x8, #0
being a red herring. If we're zeroing x8
with the intention of then modifying it, then using xzr
or #0
is pretty arbitrary (although I'd tend to favour #0
as the more obvious). But if we're zeroing x8
purely in order to supply a zero operand to a subsequent instruction, then we'd be better off using - where permitted - xzr
instead of x8
as the operand in that instruction, and not zeroing x8
at all.
mov x8,xzr
mov x8,#0
mov x8,0
生产
0000000000000000 <.text>:
0: aa1f03e8 mov x8, xzr
4: d2800008 mov x8, #0x0 // #0
8: d2800008 mov x8, #0x0 // #0
除了它允许没有英镑符号的立即数之外,没有什么真正令人惊讶的。这不是指令大小问题(同样不足为奇,例如 xor rax,rax 比 mov rax,0 便宜),也许有管道性能提升(尽管普遍认为指令需要多个时钟开始完成) .
很可能这是个人喜好,我们有这个很酷的 mips,就像总是零寄存器一样,让我们只是为了好玩而使用它。
这两条指令在效果和预期性能方面应该是相同的。
它们实际上都是更通用指令的别名。
mov x8, 0
被编码为orr x8, xzr, 0
mov x8, xzr
被编码为orr x8, xzr, xzr
别名很有用,因为它们使 ASM 更具可读性。
第二种编码说明了为什么使用零寄存器xzr
会很有用。因为我们知道 xzr 总是为零,所以我们可以将orr
指令重用于mov
. 没有它,mov
将需要不同的编码,因此会浪费编码空间。
这个答案对 OP 来说并不是“全力以赴”。
XZR 可用于丢弃结果;例如,“ldr xzr, [sp], 16”。请参阅下面的 GDB
0x7fffffef40: 0x00000000 0x00000000 0x00400498 0x00000000
0x7fffffef50: 0x00000000 0x00000000 0x00000000 0x00000000
ldr x0,=0xdead
(gdb)
ldr x1,=0xc0de
(gdb)
stp x0, x1, [sp, #-16]!
(gdb) x/8x $sp
0x7fffffef30: 0x0000dead 0x00000000 0x0000c0de 0x00000000
0x7fffffef40: 0x00000000 0x00000000 0x00400498 0x00000000
ldr xzr, [sp], #16
(gdb) x/8x $sp
0x7fffffef40: 0x00000000 0x00000000 0x00400498 0x00000000
0x7fffffef50: 0x00000000 0x00000000 0x00000000 0x00000000
还请记住,在 ARMv8 中,堆栈应该是四字对齐或 SP mod 16 = 0。因此您可以使用 XZR 之一的“推送”或“弹出”对寄存器。
stp x1, xzr, [sp, #-16]!
ldp x10, xzr, [sp], #16
TL;博士
将 64 位文字加载到寄存器中需要多条指令,但使用 zxr 只需一条指令将其设置为 0。因此代码更短更快。
要将文字移动到寄存器,您将使用 MOVL 指令,请参阅 arm 参考:
MOVL 伪指令
使用以下任一方式加载寄存器:
A 32-bit or 64-bit immediate value. Any address.
MOVL 生成两个或四个指令……一个 MOV,MOVK 对。
因此,将文字加载到寄存器中是一个多步骤的过程。如果您只想清除寄存器,那么他们有一个快捷方式。zxr 是一个伪寄存器,始终读取零,这是您需要的常用值,并且可以在一条指令中完成将寄存器移动到寄存器。
在 Microchip 组装中,它们具有类似的概念。要将寄存器设置为文字,您可以执行以下操作:
MOVLW 10 (Move 10 to the working register)
MOVWF 0x1234 (Move the working register to address 0x1234)
但是要设置为零,他们有以下指令:
CLRF 0x1234 (Set 0x1234 to zero)