user1526262
user1526262

Reputation: 13

Assembler Floating point operation

I'm using Assembler with AT&t syntax under linux. I need to divide and multiply 3 numbers (a,b,c). The operation is likely a/b*c,i've tried using idiv and imul istruction but of course those works on integer numbers so i'm getting total inaccurate results from them. I also tried to use fidiv and fimul istruction to use float in calculation but i'm getting totally wrong results.So probably i'm doing the operation on the wrong registers. Can someone please do me an example on how to use fidiv/fimul under AT&T? Wich registers do those istructions use?

Thanks in advance.

Upvotes: 0

Views: 1263

Answers (1)

kriss
kriss

Reputation: 24207

As AT&T is the output syntax of gas (GNU Assembler), you shouldn't think too long. Just write it in C and generate the assembler output using -S switch.

Exemple:

If it type the following program in source file abc.c

main(){
    int a = 7;
    int b = 3;
    int c = 2;
    return (double)a/(double)b*(double)c;
}

Then compile it using gcc -S abc.c

I get the following assembly source code:

.file   "abc.c"
.text
.globl  main
.type   main, @function
main:
.LFB0:
.cfi_startproc
pushq   %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq    %rsp, %rbp
.cfi_def_cfa_register 6
movl    $7, -4(%rbp)
movl    $3, -8(%rbp)
movl    $2, -12(%rbp)
cvtsi2sd    -4(%rbp), %xmm0
cvtsi2sd    -8(%rbp), %xmm1
movapd  %xmm0, %xmm2
divsd   %xmm1, %xmm2
movapd  %xmm2, %xmm1
cvtsi2sd    -12(%rbp), %xmm0
mulsd   %xmm1, %xmm0
cvttsd2si   %xmm0, %eax
popq    %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size   main, .-main
.ident  "GCC: (Debian 4.6.3-1) 4.6.3"
.section    .note.GNU-stack,"",@progbits

What it does should be clear enough : reserve space for 3 int on stack, store constant values in them, convert them to double (cvtsi2sd), perform division (caveat: you write a/b in order div b, a, the result goes in second register). Etc. What is obvious is that the compiler does not bother to use old FPU 8087 instruction set as nowaday there is simpler ways to perform floating point computing without having to play with FPU stack. As the question doesn't say anything on the target system, I will do as my compiler do to perform such computing.

As it may still be unclear for some people (my answer was downvoted), I performed a bit of reordering on gcc output (to avoid moving uselessly things around) and added comments. The only likely pitfall is the order of arguments for div. In which register to put datas and get results is up to the reader.

    .file   "abc.c"
    .text
    .globl  main
    .type   main, @function
    main:
    .LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6

    # Load integer 7 (variable a), convert it to double
    movl    $7, -4(%rbp)
    cvtsi2sd    -4(%rbp), %xmm0

    # Load integer 3, (variable b) convert it to double
    movl    $3, -8(%rbp)
    cvtsi2sd    -8(%rbp), %xmm1

    # Load integer 2, (variable c) convert it to double
    movl    $2, -12(%rbp)
    cvtsi2sd    -12(%rbp), %xmm2

    #   a / b -> written "div b, a" result goes in a (%xmm0)
    divsd   %xmm1, %xmm0 

    #   b * c -> result goes in c (%xmm2)
    mulsd   %xmm0, %xmm2 

    # convert result back to integer
    cvttsd2si   %xmm2, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
    .LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.6.3-1) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

On Linux, you can compile execute and show the result (truncated to int and truncated to 256 as it is the process result) simply doing:

gcc abc.s ; ./a.out ; echo $?

To make the answer more complete, you can easily write an equivalent program using old FPU (it didn't bothered setting the truncating mode of FPU, so you may get 5 instead of 4 it its truncating to the nearest integer):

    .file   "abc.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6

    # Load integer 7 (variable a), convert it to double
    movl    $7, -4(%rbp)

    # Load integer 3, (variable b) convert it to double
    movl    $3, -8(%rbp)

    # Load integer 2, (variable c) convert it to double
    movl    $2, -12(%rbp)

    fild    -12(%rbp)
    fild    -8(%rbp)
    fild    -4(%rbp)

    #   a / b -> written "div b, a" result goes in a (%mm0)
    fdivp   %st(0), %st(1) 

    #   b * c -> result goes in c (%mm2)
    fmulp   %st(0), %st(1) 

    # convert result back to integer
    fist    -4(%rbp)
    movl    -4(%rbp), %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.6.3-1) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

Upvotes: 1

Related Questions