linux - 汇编浮点运算

标签 linux assembly floating-point

我在linux下使用AT&t语法的汇编器。 我需要对 3 个数字(a、b、c)进行除法和乘法。 该操作可能是 a/b*c,我尝试过使用 idiv 和 imul 指令,但当然这些适用于整数,所以我从它们中得到的结果完全不准确。 我还尝试使用 fidiv 和 fimul 指令在计算中使用 float,但我得到了完全错误的结果。所以可能我在错误的寄存器上进行操作。 有人可以给我举一个如何在 AT&T 下使用 fidiv/fimul 的例子吗?这些指令使用哪些寄存器?

提前致谢。

最佳答案

由于 AT&T 是 Gas (GNU Assembler) 的输出语法,所以你不要想太久。只需用 C 语言编写并使用 -S 开关生成汇编器输出即可。

示例:

如果在源文件abc.c中输入以下程序

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

然后使用gcc -S abc.c编译它

我得到以下汇编源代码:

.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

它的作用应该足够清楚:在堆栈上为 3 个 int 保留空间,在其中存储常量值,将它们转换为 double (cvtsi2sd),执行除法(注意:您按 div b, a 的顺序编写 a/b,结果进入第二个寄存器)。等等。显而易见的是,编译器不会费心使用旧的 FPU 8087 指令集,因为现在有更简单的方法来执行浮点计算,而无需使用 FPU 堆栈。由于问题没有说明目标系统上的任何内容,因此我将按照编译器的方式来执行此类计算。

由于某些人可能仍然不清楚(我的答案被否决),我对 gcc 输出进行了一些重新排序(以避免无用的移动)并添加了注释。唯一可能的陷阱是 div 参数的顺序。在哪个寄存器中存放数据并获取结果由读者决定。

    .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

在 Linux 上,您可以编译执行并显示结果(截断为 int 并截断为 256,因为它是进程结果),只需执行以下操作:

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

为了使答案更完整,您可以轻松地使用旧的 FPU 编写一个等效程序(它不费心设置 FPU 的截断模式,因此您可能会得到 5 而不是 4,因为它截断为最接近的整数):

    .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

关于linux - 汇编浮点运算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11488350/

相关文章:

linux - linux 中的 strace init 进程(PID 1)

android - 什么会导致待机模式下key_up和key_down同时报错

C/Linux - 扫描文本文件中的行以查找特定单词

php - 使用php写入属于不同用户的目录中的文件

assembly - 将 64 位常量移动到 x86 程序集中的内存中

c - 浮点打印不准确

c++ - Visual C++ 生成 DIV 而不是 IDIV(x86,整数运算)

c++ - 使用 Visual Studio 10 编译 x64 EMMS

c++ - 在 C/C++ 中可以用 float 准确表示的最大 uint64

mysql - 浮点值未正确保存到数据库