使用soft,softfp,hard floating point时IEEE 754格式是否相同?

时间:2017-09-21 09:14:21

标签: floating-point raspberry-pi arm

TL; DR:0x400921f4是如何将IEEE 754表示为3.1415801的?这是一种软浮动吗?我错过了什么?

我目前正在为Raspberry Pi 3和Raspberry Pi Zero W构建自己的交叉编译器时了解crosstool-NG。(* 1)

在阅读遍布论坛和wiki的文档之后,我理解RPi3和RPi0编译器需要分别针对不同的ARM体系结构ARMv7和ARMv6。但是,两个SoC都有一个浮点单元,所以我想使用硬浮点来匹配其余的Raspberry Pi库。

我在RaspberryPi Tool repository的arm {linux-gnueabihf-gcc 4.9.3上测试了我新建的armv6-rpi-linux-gnueabi-gcc 6.4.0。我读了如何确定可执行文件是否使用了硬fp here。当然,当我编译以下最小测试用例时。

我使用gcc -O0 -o main main.c编译两个编译器。在下面:我的工具链的main.rpi,预构建的工具链的main.hf。

#include <stdio.h>

int main(int argc, char** argv) {
        printf("Hello, world %0.7f", 3.14158f);
        return 0;
}
使用我新建的armv6-rpi-linux-gnueabi-gcc时,

readelf -A main.rpi不会返回Tag_ABI_VFP_args: VFP registers,但是在使用预先构建的arm-linux-gnueabihf-gcc时使用{ {1}}。但是,这两个可执行文件在默认的raspbian拉伸图像上运行得很好,该图像据说只支持具有以下输出的硬fp:

readelf -A main.hf

奇怪的是,RPi本身的Hello, world 3.1415801 只返回ldd main.rpi,而它列出了not a dynamic executable的依赖关系。同样,两者都运行并给出预期的输出。 ldd main.hf(在RPi上运行)为两个可执行文件返回相同的内容:

objdump -R

这让我担心我使用了DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 0002101c R_ARM_GLOB_DAT __gmon_start__ 0002100c R_ARM_JUMP_SLOT printf@GLIBC_2.4 00021010 R_ARM_JUMP_SLOT __libc_start_main@GLIBC_2.4 00021014 R_ARM_JUMP_SLOT __gmon_start__ 00021018 R_ARM_JUMP_SLOT abort@GLIBC_2.4 soft而不是softfp浮点数。所以我使用hard(在RPi上运行)查看了两个可执行文件的反汇编列表:

objdump -d

main.rpi:
---------
0001045c <main>:
   1045c:       e92d4800        push    {fp, lr}
   10460:       e28db004        add     fp, sp, #4
   10464:       e24dd008        sub     sp, sp, #8
   10468:       e50b0008        str     r0, [fp, #-8]
   1046c:       e50b100c        str     r1, [fp, #-12]
   10470:       e3a02103        mov     r2, #-1073741824        ; 0xc0000000
   10474:       e59f3014        ldr     r3, [pc, #20]   ; 10490 <main+0x34>
   10478:       e59f0014        ldr     r0, [pc, #20]   ; 10494 <main+0x38>
   1047c:       ebffffa1        bl      10308 <printf@plt>
   10480:       e3a03000        mov     r3, #0
   10484:       e1a00003        mov     r0, r3
   10488:       e24bd004        sub     sp, fp, #4
   1048c:       e8bd8800        pop     {fp, pc}
   10490:       400921f4        .word   0x400921f4 <--- float 3.1415801 here
   10494:       00010508        .word   0x00010508 <--- pointer to format string

令我惊讶的是,两个编译器为main产生了相同的结果,差异很小。 (* 2)两者看起来都像是hardfp。

我检查过是否有一些技巧,编译器将浮动放在实际的字符串中,但没有找到。然后我去了,并随便确保0x400921f4确实是我的浮点值3.1415801被打印。我使用了两个在线工具herehere进行了检查。而当我对浮点数据的理解明显分崩离析时。

所有转换器告诉我3.1415801应为0x40490fa6,两个可执行文件0x400921f4中的文字产生~2.1426973。两个编译器是否都使用特殊的(软件?)浮点格式,即使它们表明它们使用main.hf: -------- 000103f8 <main>: 103f8: e92d4800 push {fp, lr} 103fc: e28db004 add fp, sp, #4 10400: e24dd008 sub sp, sp, #8 10404: e50b0008 str r0, [fp, #-8] 10408: e50b100c str r1, [fp, #-12] 1040c: e59f0018 ldr r0, [pc, #24] ; 1042c <main+0x34> 10410: e3a02103 mov r2, #-1073741824 ; 0xc0000000 10414: e59f3014 ldr r3, [pc, #20] ; 10430 <main+0x38> 10418: ebffffa1 bl 102a4 <printf@plt> 1041c: e3a03000 mov r3, #0 10420: e1a00003 mov r0, r3 10424: e24bd004 sub sp, fp, #4 10428: e8bd8800 pop {fp, pc} 1042c: 000104a4 .word 0x000104a4 <--- pointer to format string 10430: 400921f4 .word 0x400921f4 <--- float 3.1415801 here ?我一直认为软件浮点只是常规的IEEE 754,但是用软件计算而不是不同的格式。

我真的很困惑,如果我成功使用hardfp并且我的推理已经关闭,或者当我甚至看不到正确的浮点常数时如何解释明显正确的行为反汇编。

(* 1)由于这是我第一次使用crosstool-NG,我使用了他们的示例配置Tag_ABI_FP_number_model: IEEE 754,但是将gcc降级到6.4.0并将gdb降级到7.12.1以大约匹配交叉编译器适用于Windows的工具链。我还针对内核4.4.83而不是3.12.74。

(* 2)我检查了printf @ plt中是否隐藏了软浮动操作,但事实并非如此。两者的汇编是(具有不同的地址):

armv6-rpi-linux-gnueabi

2 个答案:

答案 0 :(得分:4)

当float传递给printf()时,它会被提升为double,编译器已经优化了转换。 (double)(3.14158f)的表示形式为0x400921f4c0000000。 MS部分进入r3,LS部分进入r2。

另外,因为printf()是可变参数,所以在两种情况下都使用整数寄存器传递FP值(软浮动样式)。

答案 1 :(得分:0)

float  fun ( float a )
{
    return(a+1.234F);
}

有硬浮动

00000000 <fun>:
   0:   eddf 7a02   vldr    s15, [pc, #8]   ; c <fun+0xc>
   4:   ee30 0a27   vadd.f32    s0, s0, s15
   8:   4770        bx  lr
   a:   bf00        nop
   c:   3f9df3b6    svccc   0x009df3b6

软浮动

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f24f 31b6   movw    r1, #62390  ; 0xf3b6
   6:   f6c3 719d   movt    r1, #16285  ; 0x3f9d
   a:   f7ff fffe   bl  0 <__aeabi_fadd>
   e:   bd08        pop {r3, pc}

相同的常数,这是正确的常数。

但正如我能得到我的答案所指出的那样。除非另有说明,否则C中的浮点数是双倍的,请注意我的常量结尾处的F,而不是编译器必须提升为加倍才能执行操作(因为这就是我告诉它要做的就是不放F,常量被认为是双倍的否则)然后转换回单一返回。

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f7ff fffe   bl  0 <__aeabi_f2d>
   6:   a304        add r3, pc, #16 ; (adr r3, 18 <fun+0x18>)
   8:   e9d3 2300   ldrd    r2, r3, [r3]
   c:   f7ff fffe   bl  0 <__aeabi_dadd>
  10:   f7ff fffe   bl  0 <__aeabi_d2f>
  14:   bd08        pop {r3, pc}
  16:   bf00        nop
  18:   c8b43958    ldmgt   r4!, {r3, r4, r6, r8, r11, r12, sp}
  1c:   3ff3be76    svccc   0x00f3be76