微信公众号mindshare思享
此文来源于本人在arm开发者网站发布的英文文章
Hints for running software on V8-A processors without FPU
现用中文讲一遍。
熟悉arm
processor的朋友应该知道arm的Cortex-A是带有FPU和NEON的,FPU用来做浮点数运算的,而NEON是SIMD指令做并行运算的。在现有Cortex-A的设计里,NEON和FPU是不可分的,也就是不能单独只有NEON或是FPU。在比较高性能的Cortex-A
CPU(比如Cortex-A15/A57/A72/A73/A75)中,NEON和FPU是不能在RTL配置里去掉的,在高能效的Cortex-A的CPU(比如Cortex-A7/A53/A55)中NEON和FPU是可以在RTL配置里面配置有或是没有。
NEON和FPU毕竟是占面积的,也许你会认为你的应用可能用不到NEON或是FPU,所以你可以配置RTL没有NEON/FPU,以减少面积die size或功耗。
这在Armv7里可能不是问题,但是在armv8 64位里需要非常小心,也许因为这个配置导致你的芯片称为无用的废片,有些客户因此遭受损失,虽然我们已经尽可能地告知客户们。
问题描述
在 armv8 aarch64中,arm规定了过程调用规范Procedure Call Standard for the ARM 64-bit Architecture’--AAPCS64,这个规范时规定在函数调用过程中怎么传输入和输出参数,哪些寄存器需要调用者保护,哪些寄存器需要被调用者保护。之所以要定义这个规范就是要使不同的compiler (arm compiler, gcc, llvm)生成的库能兼容地被使用,能被链接器链接起来生成一个可执行文件或是库。这保证了arm生态的软件兼容,非常重要。
和我们这次要讨论的话题相关的是怎么来传浮点数的函数输入输出参数。
在Armv7的AAPCS32规范里,我们实际上定义了两种传浮点数的方式:
softfp
hardfp
这两个的区别在于,softfp 是用整形的通用寄存器(r0-r3)来传浮点数参数的,比如
float fadd(float a, float b)
a和b实际上是通过r0,r1传入到被调函数的,结果也是通过r0传出的。
但如果使用hardfp,那么用浮点数寄存器来传参数,以上同样的例子,a和b是通过s0, s1寄存器来传的,结果是通过s0传出的。
在GCC compiler里提供了一下选项来选择你编译的代码是使用哪个方式
-mfloat-abi=softfp/hard
因为使用不同的参数传递方式,所以你不能将一个使用softfp另外一个使用hardfp的库或目标文件链接起来。
正式因为如此,在armv7的时候有些编译好的库(比如glibc使用softfp)没法在使用另一种(使用hardfp)的应用编译中。
有的工具链直接只支持一种方式比如arm-linux-gnueabi,和arm-linux-gnueabihf。
但是维护这种不同的ABI带来了兼容性维护的问题,比如同样一套库可能要提供两个版本。因此很多OS厂商开始只支持一种方式,比如ubuntu从12.04开始只支持hardfp.
正是因为以上原因,armv8 aarch64的AAPCS只支持hardfp,这就需要用到NEON/FPU寄存器。如果你的CPU配置了没有NEON/FPU,可能软件上会带来比较大的问题,导致一些通用的OS分发版本(ubuntu,Redhat)没法正常跑。
对软件和compiler的影响
Compiler和一般的OS分发版本都会假设armv8
aarch64的CPU是带NEON/FPU的,除非你能非常肯定地知道你的软件和库不会使用任何的浮点数运算,这样的话我们之后会提供一些编译选项,以便compiler不生成使用浮点数的指令。这样的话你或许可以在RTL里去掉NEON/FPU.
对Linux kernel的影响
为了可以是之前的32位且使用softfp应用可以跑在arm64kernel上,Linux kernel提供下面的patch,
https://patchwork.kernel.org/patch/9405787/
这个patch允许arm64 Linux kernel本身可以在没有NEON/FPU的CPU上跑。
但是我们不建议去除NEON/FPU,因为你可能需要跑通用的Linux kernel.即使Linux kernel本身可以在没有NEON/FPU的CPU上运行,但是还需要考虑user space的应用和库是使用AAPCS64ABI编译的,他们还是需要NEON/FPU.
Compiler的支持
GCC提供了-mgeneral-regs-onlyoption选项,这个选项禁止生成的代码使用浮点数寄存器,但他不限制汇编使用浮点数寄存器。
Arm compiler 6也提供了-mcpu=name+nofp+nosimd这个选项达到以上目标,但是一旦使用这个选项,你的代码就不能有浮点数的类型和运算。
如果你肯定你的程序和库都只需要整形运算,你可以试以上选项,这样的话就不需要NEON/FPU的硬件支持。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0773g/chr1383143713787.html
Linux分发版本情况
附录
AAPCS32 allows both soft and hard float ABI by defining ‘The base standard’ and ‘The standard variants’.
5 THE BASE PROCEDURE CALL STANDARD
The
base standard defines a machine-level, core-registers-only calling
standard common to the ARM and Thumb instruction sets. It should be used
for systems where there is no floating-point hardware, or where a high
degree of inter-working with Thumb code is required.
6.1 VFP and Advanced SIMD Register Arguments
This
variant alters the manner in which floating-point values are passed
between a subroutine and its caller and allows significantly better
performance when a VFP co-processor or the Advanced SIMD Extension is
present.
6.1.1 Mapping between registers and memory format
Values passed across a procedure call interface in VFP registers are laid out as follows:
• A
half precision floating point type is passed as if it were loaded from
its memory format into the least significant 16 bits of a single
precision register.
• A
single precision floating point type is passed as if it were loaded
from its memory format into a single precision register with VLDR.
• A
double precision floating point type is passed as if it were loaded
from its memory format into a double precision register with VLDR.
See more information in AAPCS32 spec,
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf