Spi*_*ngo 0 x86 assembly x86-64
我在汇编中编写了一个程序,如下所示:
%macro print 1
push rbp
mov rdi, %1
xor rax, rax
call _printf
pop rbp
%endmacro
section .data
e db 'Equal', 0
l db 'Less than', 0
g db 'Greater than', 0
section .text
global start
extern _printf
start:
mov rax, 5
mov rbx, 5
cmp rax, rbx ; Compare 4 and 5
je _equal ; je = jump if equal
cmp rax, rbx
jl _less ; jl = jump if less
cmp rax, rbx
jg _greater ; jg = jump if greater
ret
_equal:
print e
_less:
print l
_greater:
print g
Run Code Online (Sandbox Code Playgroud)
但是当我运行程序时它跳转到_equal,但它随后跳转到_less, 和_greater。如何禁用此自动跳转?
Run Code Online (Sandbox Code Playgroud)mov rax, 5
这是一种低效的编写方式mov eax, 5。如果您只是将 32 位值移动到 64 位寄存器中,则不需要将 64 位寄存器指定为操作数。只需指定 32 位寄存器,高 32 位将被隐式清零。
同样的事情:
Run Code Online (Sandbox Code Playgroud)xor rax, rax
您可以只写xor eax, eax,并让高 32 位隐式为零。
Run Code Online (Sandbox Code Playgroud)cmp rax, rbx ; Compare 4 and 5
由于您知道您只是在比较 32 位值,因此您可以编写cmp eax, ebx只比较较低的 32 位值。这更小,更有效。仅cmp rax, rbx当您确实想要比较这些寄存器中的整个 64 位值时才需要。
当然,整个代码有点傻,因为您已经知道 4 与 5 的比较——这不需要在运行时执行。
但是让我们假设这些是运行时值,并且您确实需要进行比较。您仍然只需要进行一次比较。所以这段代码:
Run Code Online (Sandbox Code Playgroud)cmp rax, rbx ; Compare 4 and 5 je _equal ; je = jump if equal cmp rax, rbx jl _less ; jl = jump if less cmp rax, rbx jg _greater ; jg = jump if greater
可以简化为:
Run Code Online (Sandbox Code Playgroud)cmp rax, rbx je _equal jl _less jg _greater
因为条件跳转指令不会改变标志。
但是当我运行程序时它跳转到
_equal,但它随后跳转到_less, 和_greater。如何禁用此自动跳转?
正如评论和另一个答案中已经指出的那样,它实际上并没有jumping,因为它没有跳过任何指令。它只是落入下一个标签。
防止这种情况的一种方法(如 user3344003 建议的那样)是在每个 case 之后添加无条件跳转指令。就像是:
cmp rax, rbx
je _equal
jl _less
jg _greater
finished:
ret
_equal:
print e
jmp finished
_less:
print l
jmp finished
_greater:
print g
jmp finished
Run Code Online (Sandbox Code Playgroud)
但是,实际上,您所做的只是跳回返回。单ret条指令的大小比一条jmp指令小,而且效率更高,因为不需要采取分支。如果您有一堆需要在返回之前运行的清理代码,则只能使用此模式。在这种简单的情况下,您可以执行以下操作:
cmp rax, rbx
je _equal
jl _less
jg _greater
_equal:
print e
ret
_less:
print l
ret
_greater:
print g
ret
Run Code Online (Sandbox Code Playgroud)
请注意,我ret在jg指令之后省略了。您不需要它——相等、更少和更大详尽地涵盖了所有可能性,因此保证采用三个分支中的一个。事实上,这意味着您可以重新排列代码以消除一个分支,利用最初让您感到困惑的失败:
cmp rax, rbx
jl _less
jg _greater
; fall through to 'equal' case
print e
ret
_less:
print l
ret
_greater:
print g
ret
Run Code Online (Sandbox Code Playgroud)
有趣的事实:如果你用 C 编写它,这与 GCC 将生成的代码基本相同。