assemblyx86x86-64pointer-arithmeticaddressing-mode

Subtracting registers with an LEA instruction?


Does the LEA instruction support negative displacement?

mov rax, 1
lea rsi, [rsp - rax]

When I use the above code in my asm file I got the error:

$ nasm -f macho64 test.asm
$ error: invalid effective address

I Know that we can do pointer arithmetic like this in C:

void foo(char *a, size_t b) {
    *(a - b) = 1;
}

then I assume that:

lea rsi, [rsp - rax]    

will work.

And I also try to see what the GCC compiler do by using:

$ gcc -S foo.c // foo.c has the function foo(above) in it

but my asm knowleage is not enough for me the understand the asm output from the GCC compiler.

Can anyone explain why:

lea rsi, [rsp - rax]    ;; invalid effective address

does not work. And I'm using these to achieve the samething:

;; assume rax has some positive number
neg rax    
lea rsi, [rsp + rax]
neg rax

or

sub rsp, rax
mov rsi, rsp
add rsp, rax

What is a more standard way of doing it?

I'm using NASM version 2.11.08 compiled on Nov 26 2015 on MAC OSX 10.11

Thank you in advance for your help!


Solution

  • The lea instruction doesn't care about the sign of the displacement (integer constant part; it's encoded as 8-bit or 32-bit and sign-extended).
    But you do need to always add the components together.

    mov rax, -1
    lea rsi, [rsp + rax]   ; negative index in a register
    lea rsi, [rsp + -1]    ; negative displacement
    lea rsi, [rsp - 1]     ; assemblers let you write it this way
    
    lea rdi, [rsp + rax*4 - 8] ; With all 3 components, and even scaling the idx
    

    Remember subtracting 1 is the same as adding -1.
    For example, [rax - 1] is actually encoded as [rax + (-1)]
    with a disp8 of 0xFF in the machine code.

    (The scale factor is encoded as a 2-bit left-shift count, so it can't be negative either.)

    See also Referencing the contents of a memory location. (x86 addressing modes) for details on how x86 addressing modes work.