2
votes

I was going through assembly code optimization manual section 2.3 Common coding pitfalls - page 9

  1. Unmatched PUSH and POP instructions. The number of PUSH and POP instructions must be equal for all possible paths through a function. Example:

    push ebx
    test ecx, ecx
    jz   Finished
    ...
    pop  ebx
    Finished:       ; Wrong! Label should be before pop ebx
    ret
    

Here, the value of EBX that is pushed is not popped again if ECX is zero. The result is that the RET instruction will pop the former value of EBX and jump to a wrong address.

My doubt is: doesn't the jz instruction store the return address in the stack? What about other instructions like jmp, jg, jge, jl, jle etc?

1
No jump instruction pushes a return address. Only call does. - fuz
It is far more common to transfer control to an address that is not the end of a function, when you definitely don't want to affect the stack, and makes no assumptions about what stack adjustment is needed / registers need to be restored. How many variants of those instructions would be needed, and with which (combination of) registers? - Weather Vane
If you're ever not sure about exactly what an instruction does, look it up in Intel's or AMD's manual. felixcloutier.com/x86/jcc is scraped from Intel's PDFs. - Peter Cordes

1 Answers

5
votes

No, it doesn't. Call instructions push a return address to the stack, jump instructions (including conditional jumps) do not. That is the fundamental difference between calls and jumps.

If you want to know what an instruction does, you should always refer to its description in official documentation, e.g. https://www.felixcloutier.com/x86/ which is an HTML copy of Intel's manuals. The description of jcc (which includes je) makes no mention of pushing a return address, which tells you that it doesn't do it. On the other hand, the description of call explicitly states that the return address is pushed, and explains in detail how it is done:

When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) on the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified by the target operand.

Jump instructions are usually used to transfer control within functions, for constructs like loops (while), conditional execution (if), etc. In most such cases you don't ever want to return to the site of the jump, so pushing a return address would be useless and would just require you to waste extra instructions to get rid of it from the stack.