-1

I have the following program to print a float in asm with the help of C's printf:

.section .rodata
format: .ascii "Your number is: %f\n\0"
.section .text
.globl main
main:
    lea format(%rip), %rdi
    mov $0b1000000001011001100110011001101, %xmm0 # the number 2.7
    mov $0, %eax
    add $-8, %rsp
    call printf@plt
    add $8, %rsp
    mov $0, %eax
    ret

However, I get an error when assembling it with:

int_c.s:7: Error: unsupported instruction `mov'

Are you not allowed to add immediates into the xmm registers, or what seems to be the issue with the above program?


Update: I got it to compile but then I think my issue is that the movq accepts 8 byte but I'm looking to get a 4-byte float into the fp register:

mov $2.7, %eax # using '2.7' to be more readable
movq %eax, %xmm0

And after stepping through the instructions it looks correct before calling printf:

>>> p $xmm0
$2 = {
  v4_float = {[0] = 2.70000005, [1] = 0, [2] = 0, [3] = 0},
  v2_double = {[0] = 5.3194953090036137e-315, [1] = 0},
...
}
carl.hiass
  • 1,526
  • 1
  • 6
  • 26
  • Indeed that is the problem. Consult an instruction set reference when in doubt. – Jester Mar 26 '21 at 01:15
  • @Jester sure, `movq` works, though it still seems there's an issue when adding that in that the number shows `0`. – carl.hiass Mar 26 '21 at 01:18
  • @Jester updated the question. – carl.hiass Mar 26 '21 at 01:23
  • 1
    `printf` expects a `double` so it's going to be looking at the low 64 bits (`v2_double` to gdb) which is not the value you want. – Nate Eldredge Mar 26 '21 at 01:31
  • @NateEldredge I see. Why does it expect a double instead of a float if I pass it the `%f` format? – carl.hiass Mar 26 '21 at 01:34
  • Does `mov $2.7, %rax; mov %rax, %xmm0` work? – prl Mar 26 '21 at 01:34
  • @prl not using gas, but I just type that in so it's easier to show (the actual value in the text is that binary at the top). – carl.hiass Mar 26 '21 at 01:35
  • @carl.hiass https://stackoverflow.com/questions/28097564/why-does-printf-promote-a-float-to-a-double and also https://stackoverflow.com/questions/4264127/correct-format-specifier-for-double-in-printf?rq=1 – Nate Eldredge Mar 26 '21 at 01:36
  • 1
    Anyway, the most typical way to get a floating point constant into a register is to load from memory. – Nate Eldredge Mar 26 '21 at 01:36
  • Oh, and since you are now passing 1 floating point argument in xmm registers, you need your `mov $0, %eax` to now be `mov $1, %eax`. – Nate Eldredge Mar 26 '21 at 01:39
  • @NateEldredge thanks. What does `eax` represent in the print call? And is there a good reference from a register/low-level how the printf function is called/works? – carl.hiass Mar 26 '21 at 01:44
  • [Where is the x86-64 System V ABI documented?](https://stackoverflow.com/q/18133812) documents the calling convention. ISO C specifies how printf conversions work (and C "default argument promotions" for the variadic args). @Nate: There's an asm-specific [How to print a single-precision float with printf](https://stackoverflow.com/q/37082784) which explains the need to `cvtss2sd` – Peter Cordes Mar 26 '21 at 02:06

1 Answers1

0

Here is a working example of your program, writing the value to memory and then using cvtss2sd to convert your float to a double:

format: .ascii "Your number is: %f\n\0"
.section .text
.globl main
main:
    push %rbp
    mov %rsp, %rbp
    lea format(%rip), %rdi

    # move float to memory and upgrade to 8 bytes
    movl $0b1000000001011001100110011001101, -4(%rbp)
    cvtss2sd -4(%rbp), %xmm0

    mov $1, %eax
    call printf@plt
    mov $0, %eax
    mov %rbp, %rsp
    pop %rbp
    ret

rax will be the number of float arguments in printf. See: Why is %eax zeroed before a call to printf?.

carl.hiass
  • 1,526
  • 1
  • 6
  • 26
  • Normally you'd want `format` in `.section .rodata`. The default section at the top of the file is `.text`, so that's where you put it. Also, the normal way to 0-terminate is with `.asciiz`, rather than `.ascii` with an explicit `\0` – Peter Cordes Mar 26 '21 at 01:59
  • Also, `cvtss2sd` works with registers too so you don't have to go through memory. – Jester Mar 26 '21 at 02:00
  • 1
    Other than that, yeah this is fine for un-optimized code. No real need to set up RBP as a frame pointer, and you could `pushq $0b1000000001011001100110011001101` to align the stack and store the float constant. (Still just read the low 4 bytes of it, ignoring the sign-extended high half.) – Peter Cordes Mar 26 '21 at 02:00
  • @Jester how could I do the above with a register? I tried moving from `rax` to `xmm0` but that was giving me `0` when I tried. – carl.hiass Mar 26 '21 at 02:00
  • 1
    `movl $0b1000000001011001100110011001101, %eax; movd %eax, %xmm0; cvtss2sd %xmm0, %xmm0` – Jester Mar 26 '21 at 02:01
  • @Jester: that would take an extra instruction, for `mov $0b1000000001011001100110011001101, %eax` / `movd %eax, %xmm0` / `cvtss2sd %xmm0, %xmm0`. That's why compilers normally load float constants from memory (in .rodata, same place they'd put format strings and so on), instead of using immediates at all. – Peter Cordes Mar 26 '21 at 02:01