1

x64 assembly has a fairly standard calling convention for passing arguments: first argument goes in RDI, second in RSI, and so on. Thus, if I want to print the quotient of dividing two floating-point values with the printf function, I would think to do something like this:

.section .data
    outfloatfmt: .asciz "%lf\n"
.section .text
    .extern printf
    .global main
main:
    pushq %rbp
    movq %rsp, %rbp
    subq $16, %rsp
    movq $7, -16(%rbp)      # x = 7
    movq $2, -8(%rbp)       # y = 2
    fld -16(%rbp)           # Move x into st0 register.
    fld -8(%rbp)            # Move y into st1 register.
    fdivp %st(1)            # Divide st0 by st1 and store quotient in st0.
    fstpl -16(%rsp)         # Store quotient back out to local variable x.
    movq -16(%rsp), %rsi    # Load quotient into rsi.
    leaq outfloatfmt(%rip), %rdi
    movq $0, %rax
    callq printf
    addq $16, %rsp
    movq %rbp, %rsp
    popq %rbp
    ret

This always results in an output of 0.00000. The RSI register seems to have a ridiculously long hex value stored after I perform the relevant movq instruction. I'm not sure if it has something to do with the fact that I'm storing the rest of the floating-point quotient back out to local memory and then into an integer register (but even if this is the case, shouldn't printf interpret these bytes as a floating-point value?).

If I try print/f $st0 or print/f $st1 in GDB, neither contain the correct values. What am I doing wrong?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 5
    `rsi` is only used for integer arguments. Use `xmm0` for floats. You will also need to set `al` to `1`. Consult calling convention documentation for details. Also, since your `x` and `y` are integers you want to use `fild` not `fld` (if you want to keep using the FPU that is). – Jester Dec 10 '22 at 21:37
  • 1
    @Jester: Fun fact: `7` and `2` as bit-patterns for subnormal doubles will actually give the right division result. Or subnormal binary32 floats since this code used `fld` (which defaults to `flds` single-precision, despite bothering to use 8-byte integer stores). Not that it's a good idea; `fildl` or `fildq` and `fidivl` or q are definitely the thing to do, or better SSE2 2x `cvtsi2sd` / `divsd`. But in case anyone's wondering why it would happen to work to just load into XMM0 instead of RSI (and set AX=1) without fixing the integer vs. FP bug. – Peter Cordes Dec 10 '22 at 22:21
  • Oh, also, `fdivp %st(1)` should do `st0 /= st1`, but it's one of the cases of AT&T syntax design bugs for x87 (https://sourceware.org/binutils/docs/as/i386_002dBugs.html documents this). That instruction assembles to Intel-syntax `fdivrp st1`, so you get 0.285714 (2/7) instead of 3.5 (7/2). You actually need AT&T `fdivrp %st(1)` to get the instruction Intel documents as `fdivp`. I would definitely avoid AT&T syntax for x87, or in this case avoid x87 in favour of SSE2 since you're in 64-bit mode and only need 64-bit double, not 80-but long double. – Peter Cordes Dec 10 '22 at 22:34
  • Why did you delete your [previous question](https://stackoverflow.com/questions/74757930/is-it-possible-to-load-a-floating-point-immediate-value-into-a-register-using-lo)? It was a perfectly fine question. Now all the work people put into helping you has been erased. – fuz Dec 11 '22 at 11:01

0 Answers0