3

In NASM (2.14.02), the instruction add rbx, 0xffffffff leads to

warning: signed dword value exceeds bounds [-w+number-overflow]

I'm aware that arithmetic-logic operations in 64-bit mode only accept constants with 32 bits, but 0xffffffff is still 32 bits wide.

Why does NASM emit a warning, and why does it assume a signed constant? Does it sign-extend the 32-bit -1 to 64-bit -1 (0xffffffffffffffff) and therefore see an overflow? 0x7fffffff works without a warning.

Can I somehow convince NASM that this is not a signed constant, but an unsigned one?

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Ralf
  • 1,203
  • 1
  • 11
  • 20
  • 3
    Related: [why we can't move a 64-bit immediate value to memory?](https://stackoverflow.com/q/62771323) re: why it can't use an `imm64` to encode the `0x00000000FFFFFFFF` 64-bit value you asked for. Asm source code uses values, not bit-patterns for the encoding of the immediate. And [x86-64 assembly (AT&T): movq with 32-bit literal into memory](https://stackoverflow.com/q/71733015), also semi-related: [Max 64bit immediate for mov/movq/movabs](https://stackoverflow.com/q/64289726) – Peter Cordes Nov 21 '22 at 15:00

1 Answers1

6

0x00000000FFFFFFFF can't be encoded as a 32-bit sign-extended immediate, and x86 immediates are always sign-extended when they're narrower than the operand-size.


Your theory is somewhat correct, the instruction adds the value specified to the register. There is no way to encode an instruction that adds 0xffffffff to the register, because the only available encodings for adding an immediate to a 64-bit register are add r/m64, sign_extended_imm32 or add r/m64, sign_extended_imm8. Only one special encoding of mov (with a register destination) can use a 64-bit immediate.

The obvious encoding (stuffing 0xffffffff into an imm32) would in fact add 0xffffffffffffffff to rbx which is not what you requested and would be the same as add rbx, -1

There is no way to encode a value into the 32 bit field that results in 0x00000000ffffffff being added to rbx. Asm source uses values, not bit-patterns for immediates.

You need to do something like this :-

mov rcx, 0xffffffff     ; RCX = 0xFFFFFFFF = 0x00000000FFFFFFFF
add rbx, rcx

mov ecx, 0xffffffff would also set RCX the same way; NASM will optimize this for you to an efficient 5-byte instruction (with implicit zero-extension to 64-bit when writing a 32-bit register) instead of needing a 10-byte instruction with a 64-bit immediate; see another Q&A for more about the different forms of MOV in machine code.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
jcoder
  • 29,554
  • 19
  • 87
  • 130
  • 4
    And NASM will helpfully optimize to `mov ecx, 0xffffffff` (5 bytes), instead of `mov rcx, 0xffffffff` (10 bytes since it would have to use the imm64 form, not the 7-byte sign_extended_imm32 form). For other assemblers like GAS or YASM which don't that for you, you should do it yourself for values that fit in 32-bit-zero-extended but not sign-extended. (GAS will with `as -Os`) – Peter Cordes Nov 21 '22 at 15:03
  • 1
    I see, the Intel manual says: `ADD r/m64, imm32: Add imm32 sign-extended to 64-bits to r/m64". So it is not a feature or problem of nasm, but of the instruction. Btw.: your second code line should be `add rbx, rcx`. Thanks a lot! – Ralf Nov 21 '22 at 15:05
  • 2
    @Ralf: Yes, exactly, an assembler isn't a compiler, so it's limited by what's encodeable in machine code. (It won't turn one source instruction into multiple machine instructions. At least NASM won't; some RISC ISAs have some "pseudo-instructions" for convenience.) And x86-64 only ever has 64-bit immediates for one special form of `mov`. Otherwise imm8 or imm32 are sign-extended when you're using 64-bit operand-size. [Implicit zero-extension when writing a 32-bit register](https://stackoverflow.com/q/11177137) is a separate thing, useful when putting constants into regs. – Peter Cordes Nov 21 '22 at 15:11
  • @jcoder: I edited to change the wording slightly: You were using "the value" to talk about the immediate (which gets sign-extended on decode), but I thought that could be misinterpreted as saying that NASM will sign-extend the value you write in the source code. (As if it was a 32-bit value, which is of course not what happens.) And I guess I kind of added my own wording as a TL:DR and some other bits. If you'd rather I posted some of that as my own answer, let me know. – Peter Cordes Nov 21 '22 at 19:22