程序集自动跳转到下一个标签 [英] Assembly jumps automatically to next label

查看:106
本文介绍了程序集自动跳转到下一个标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经用汇编语言编写了一个程序,如下所示:

I've written a program in assembly that look like this:

%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

但是,当我运行程序时,它会跳转到_equal,但随后会跳转到_less_greater.如何禁用此自动跳转功能?

But when I run the program it jumps to _equal, but it then jumps to _less, and _greater. How can I disable this auto-jumping?

推荐答案

mov rax, 5

这是编写mov eax, 5的低效方式.如果仅将32位值移动到64位寄存器中,则无需将64位寄存器指定为操作数.只需指定32位寄存器,然后

This is an inefficient way to write mov eax, 5. If you're only moving a 32-bit value into a 64-bit register, then you don't need to specify the 64-bit register as an operand. Just specify the 32-bit register, and the upper 32 bits will be implicitly zeroed.

与以下内容相同:

xor rax, rax

您只需编写xor eax, eax,然后将高32位隐式归零.

You can just write xor eax, eax, and let the upper 32 bits be implicitly zeroed.

cmp rax, rbx ; Compare 4 and 5

由于您知道只比较32位值,因此可以编写cmp eax, ebx来仅比较较低的32位一半.这更小且更有效.如果您确实想比较那些寄存器中的整个64位值,则只需要cmp rax, rbx.

Since you know that you are only comparing 32-bit values, you can write cmp eax, ebx to compare only the lower 32-bit halves. This is smaller and more efficient. You only need cmp rax, rbx if you actually want to compare the entire 64-bit values in those registers.

当然,整个代码有点愚蠢,因为您已经知道4与5的比较-不需要在运行时执行.

Of course, the whole code is a little bit silly, as you already know how 4 compares to 5—this doesn't need to be executed at runtime.

但是让我们假设这些是运行时值,并且您 did 需要进行比较.您仍然只需要进行一次比较.所以这段代码:

But let's assume that these were run-time values, and you did need to do the comparison. You still only need to do the comparison once. So this code:

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

可以简化为:

cmp rax, rbx
je  _equal
jl  _less
jg  _greater

因为条件跳转指令不会更改标志.

since conditional-jump instructions do not alter flags.

但是,当我运行程序时,它会跳转到_equal,但随后会跳转到_less_greater.如何禁用此自动跳转功能?

But when I run the program it jumps to _equal, but it then jumps to _less, and _greater. How can I disable this auto-jumping?

正如注释和另一个答案中已经指出的那样,它实际上并不是 jumping ,因为它没有跳过任何指令.它只是进入下一个标签.

As has already been pointed out in the comments and another answer, it is not actually jumping, since it's not skipping any instructions. It's just falling through to the next label.

防止这种情况的一种方法(如user3344003所建议的)是在每种情况之后添加无条件跳转指令.像这样:

One way to prevent this—as user3344003 advised—is to add an unconditional jump instruction after each case. Something like:

    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

实际上,您要做的只是跳回去.单个ret指令的大小比jmp指令小,并且由于无需执行分支而效率更高.仅当您有一大堆需要在返回之前运行的清理代码时,才使用此模式.在这种简单的情况下,您可以执行以下操作:

Actually, though, all you're doing is jumping back to return. A single ret instruction is smaller in size than a jmp instruction, and also more efficient since no branch needs to be taken. You would only use this pattern if you had a bunch of clean-up code that needed to run before returning. In this simple case, you can do:

    cmp rax, rbx
    je  _equal
    jl  _less
    jg  _greater

_equal:
    print e
    ret
_less:
    print l
    ret
_greater:
    print g
    ret

请注意,我在jg指令后省略了ret.您不需要它-相等,更少和更大地详尽地涵盖了所有可能性,因此可以保证采用三个分支中的一个.实际上,这意味着您可以重新排列代码以消除分支之一,从而充分利用最初会使您感到困惑的缺点:

Notice that I've omitted the ret after the jg instruction. You don't need it—equal, less, and greater exhaustively cover every possibility, so one of the three branches is guaranteed to be taken. In fact, that means you can rearrange the code to eliminate one of the branches, taking advantage of the very fall-through that was originally confusing you:

    cmp rax, rbx
    jl  _less
    jg  _greater

    ; fall through to 'equal' case
    print e
    ret

_less:
    print l
    ret

_greater:
    print g
    ret

有趣的事实:

Fun fact: this is essentially identical to the code that GCC would generate if you'd written this in C.

这篇关于程序集自动跳转到下一个标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆