0

I'm trying to teach myself x86, and I've run across the need to return an immediate, negative number:

.text
.align 4,0x90
.globl _scheme_entry
_scheme_entry:
movl $-42, %eax
ret

When I print the return-value of this function (printf("%" PRIdPTR "\n", scheme_entry()), I get a nonsense number:

$ ./neg                                                 
4294967254

I'm guessing this is because it's a 32-bit negative, 00000000FFFFFFFF, instead of a 64-bit negative, FFFFFFFFFFFFFFFF.

How can I store a constant 64-bit value directly in an assembler function? Do I have to encode it as two separate instructions?

ELLIOTTCABLE
  • 17,185
  • 12
  • 62
  • 78
  • Aren't you printing it wrong? `scheme_entry` seems to return a 32bit integer (if not, then why are you putting it in `eax`?) – harold Oct 18 '17 at 20:37
  • gcc normally aligns function entry points to 16 bytes. There's also no reason to force the padding to be single-byte NOP (because it will never run). Use `.p2align 4` to align to `1<<4`. (For such a tiny function, an 8-byte boundary would cover the whole function, so you might `.p2align 3`.) Modern CPUs aren't as sensitive to code alignment as older CPUs, but it's generally still a good idea when the NOP padding won't execute. – Peter Cordes Oct 18 '17 at 21:22
  • @PeterCordes quite a bit of that is over my head — I'm following along [a compiler-writing tutorial](http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf), which is intentionally having us use `gcc`'s output for tiny C inputs to decide what to emit from our compilers. The boilerplate above was copied basically verbatim, but I haven't yet learned what it does! :x – ELLIOTTCABLE Oct 18 '17 at 21:32
  • I don't know if you can access to 64 bits' registers, as the RAX's register, with your compiler/language (80686+); in case it is true, you could write MOV $-42,%RAX; the function result must be of 64 bits' type anyway. – Paolo Fassin Oct 19 '17 at 16:46

1 Answers1

3

32-bit immediates for 32-bit instructions (like movl) are used as-is, and writing EAX zeros the upper 32 bits of RAX. You're not getting nonsense, you're getting 2^32 - 42, which is exactly what you should expect, because x86 uses 2's complement signed integers.

32-bit immediates for instructions with 64-bit operand-size are sign-extended to 64 bits.

Use mov $-42, %rax to return a negative 64-bit value, instead of a positive 64-bit value just slightly below 2^32. (Using %rax as the destination implies a 64-bit operand size, i.e. movq.)

Use a debugger to see values in registers.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • This is excellent info! Yes, I was aware of two's-complement; but `%rax` (and `mov` vs `movl`, apparently?) were the pieces I was missing here. I'll run with this! Thank you! – ELLIOTTCABLE Oct 18 '17 at 21:14
  • 2
    @ELLIOTTCABLE Rather its movl vs movq. The suffix can be omitted if it is clear from the operands. – fuz Oct 18 '17 at 21:30