Reputation: 615
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
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?
Upvotes: 1
Views: 2515
Reputation: 244722
mov rax, 5
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.
Same thing for:
xor rax, rax
You can just write xor eax, eax
, and let the upper 32 bits be implicitly zeroed.
cmp rax, rbx ; Compare 4 and 5
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.
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.
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
can be simplified to:
cmp rax, rbx je _equal jl _less jg _greater
since conditional-jump instructions do not alter flags.
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?
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.
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
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
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.
Upvotes: 5
Reputation: 21607
You need to put a jump instruction after each case (print macro expansion). It's just falling through, not jumping.
Upvotes: 1