riscv-collab / riscv-gnu-toolchain

GNU toolchain for RISC-V, including GCC
Other
3.56k stars 1.17k forks source link

ZFINX builds libmath with soft-float for float operations #1589

Closed pulkitgupta1217 closed 3 weeks ago

pulkitgupta1217 commented 1 month ago

I am working on a custom 32-bit risc-v based architecture with the following configure command for building the toolchain:

./configure --prefix=$RISCV --with-arch=rv32ima_zfinx --with-abi=ilp32 --with-target-cflags="-ffp-contract=off" --with-target-cxxflags="-ffp-contract=off"

my architecture supports floating point instructions, but does not support fma-type instructions, and as risc-v gcc target does not support -mno-fused-madd like MIPS and other targets do, I found that setting -ffpcontract=off was the only easy way to get the toolchain to build without fma operations.

Per the RISC-V ABI spec, I find that the supported abi for my needs would be ilp32, I believe this is a causing a problem for me. The ilp32 abi uses the soft-float calling convention. This is expected for the most part, and still allows floating point operations to exist in the compiled code but not in the standard libraries.

However, it does not use any float instructions in library code, or for floating point comparisons in compiled code, and instead uses the soft-float libraries to accomplish these tasks. I have found that by passing -fno-math-errno I am able to get float comparisons in compiled code I write to use float compare instructions as I would expect. However, adding -fno-math-errno to the configure command cflags and cxxflags does not appear to remove soft-float arithmetic or compares performed in libmath functions like sin and cos.

Is this expected behavior? I would expect that Zfinx, Zdinx, etc. should not be restricted to only float instructions in compiled code. I understand that the comparison is likely done in soft-float because of the fcsr changes due to zfinx. I am not sure why subtraction and addition within math library functions would use soft-float. If this is not expected behavior, is there a configuration change that could fix this?

TommyMurphyTM1234 commented 1 month ago

Isn't at least part of your issue here that you are specifying --with-arch=rv32ima_zfinx but your target doex not actually support the full M extension? Aren't you effectively implementing a custom floating point extension that happens to be a subset of the ratified standard M extension? And such a choice has implications that flow through into the toolchain support (including compiled support libraries/startup code/etc.) and required capabilities?

pulkitgupta1217 commented 1 month ago

I'm supporting the full M extension, just not the full ZFinx extension. I believe in the past I have tested even without -ffp-contract=off and thus allowed all float instructions to be used in compiled code, but that still uses soft-float in libmath. I can check this again.

I believe the reason for the issue I am seeing is that the ABI is the soft-float ABI despite Zfinx supporting float operations (because the register map for float now uses x-registers instead of f-registers). It seems to me that having the math library fit the soft-float abi is forcing everything to be soft-float, not any architecture restrictions that I am feeding the compiler. Since it is trying to make the library fit the ilp32 abi, it cannot use float instructions even though the isa string supports them. Using ilp32f doesn't work because there are no float registers, and that causes register collisions with zfinx hardware. I think this issue may require having a separate abi for zfinx/zdinx instead of trying to get it to fit into the standard ones.

TommyMurphyTM1234 commented 1 month ago

I'm supporting the full M extension, just not the full ZFinx extension.

Ok - but it's still effectively a custom extension that you're using because it's "some subset of" ZFinx?

I believe the reason for the issue I am seeing is that the ABI is the soft-float ABI despite Zfinx supporting float operations (because the register map for float now uses x-registers instead of f-registers). It seems to me that having the math library fit the soft-float abi is forcing everything to be soft-float, not any architecture restrictions that I am feeding the compiler. Since it is trying to make the library fit the ilp32 abi, it cannot use float instructions even though the isa string supports them. Using ilp32f doesn't work because there are no float registers, and that causes register collisions with zfinx hardware. I think this issue may require having a separate abi for zfinx/zdinx instead of trying to get it to fit into the standard ones.

As far as I know using a float enabled arch but an integer abi should still allow hard float instructions to be generated but only integer registers will be used for passing floating point values. If the use of -mabi=ilp32 is preventing the generation of ZFinx (subset) instructions when -march=rv32ima_zfinx is also used then that does seem odd.

TommyMurphyTM1234 commented 1 month ago

Anything useful here?

TommyMurphyTM1234 commented 1 month ago

I'm confused...

However, it does not use any float instructions in library code, or for floating point comparisons in compiled code, and instead uses the soft-float libraries to accomplish these tasks.

I built the toolchain as follows:

git clone https://github.com/riscv-collab/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=`pwd`/installed-tools --with-arch=rv32ima_zfinx --with-abi=ilp32 --with-target-cflags="-ffp-contract=off" --with-target-cxxflags="-ffp-contract=off" --disable-gdb
make 2>&1 | tee build.log

./installed-tools/bin/riscv32-unknown-elf-gcc -v
Using built-in specs.
COLLECT_GCC=./installed-tools/bin/riscv32-unknown-elf-gcc
COLLECT_LTO_WRAPPER=/home/user/issue-1590/installed-tools/libexec/gcc/riscv32-unknown-elf/14.2.0/lto-wrapper
Target: riscv32-unknown-elf
Configured with: /home/user/issue-1590/gcc/configure --target=riscv32-unknown-elf --prefix=/home/user/issue-1590/installed-tools --disable-shared --disable-threads --enable-languages=c,c++ --with-pkgversion=g04696df0963 --with-system-zlib --enable-tls --with-newlib --with-sysroot=/home/user/issue-1590/installed-tools/riscv32-unknown-elf --with-native-system-header-dir=/include --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --disable-tm-clone-registry --src=.././gcc --disable-multilib --with-abi=ilp32 --with-arch=rv32ima_zfinx --with-tune=rocket --with-isa-spec=20191213 'CFLAGS_FOR_TARGET=-Os   -ffp-contract=off -mcmodel=medlow' 'CXXFLAGS_FOR_TARGET=-Os   -ffp-contract=off -mcmodel=medlow'
Thread model: single
Supported LTO compression algorithms: zlib
gcc version 14.2.0 (g04696df0963)

And when I disassemble libm.a I see floating point instructions:

cd installed-tools//riscv32-unknown-elf/lib

../../bin/riscv32-unknown-elf-objdump -d libm.a | grep fadd
 1f0:   00c7f553                fadd.s  a0,a5,a2
 350:   00e67653                fadd.s  a2,a2,a4
 3ac:   00c87853                fadd.s  a6,a6,a2
 60c:   00e57553                fadd.s  a0,a0,a4
...

../../bin/riscv32-unknown-elf-objdump -d libm.a | grep fsub
 1ec:   08e57753                fsub.s  a4,a0,a4
 230:   08a67653                fsub.s  a2,a2,a0
 23c:   08a67653                fsub.s  a2,a2,a0
 478:   08c57653                fsub.s  a2,a0,a2
...

../../bin/riscv32-unknown-elf-objdump -d libm.a | grep feq
 284:   a0a62553                feq.s   a0,a2,a0
 4ac:   a0a62553                feq.s   a0,a2,a0
 260:   a0aa2553                feq.s   a0,s4,a0
 2a4:   a17a27d3                feq.s   a5,s4,s7
...

../../bin/riscv32-unknown-elf-objdump -d libm.a | grep flt
  54:   a0f697d3                flt.s   a5,a3,a5
  8c:   a0f697d3                flt.s   a5,a3,a5
 204:   a0e51753                flt.s   a4,a0,a4
  78:   a0d617d3                flt.s   a5,a2,a3
...

../../bin/riscv32-unknown-elf-objdump -d libm.a | grep fle
 3e4:   a0c78553                fle.s   a0,a5,a2
 4fc:   a0a58653                fle.s   a2,a1,a0
  78:   a0ab87d3                fle.s   a5,s7,a0
 700:   a0f807d3                fle.s   a5,a6,a5
...
pz9115 commented 1 month ago

As Tommy showed, I think when using Zfinx/Zdinx, the behavior should be consistent with the F/D extension (it just replaces the registers for floating point instructions). Which seems to conflict with soft floating point, but this is what Zfinx/Zdinx is designed for ,please check https://github.com/riscvarchive/riscv-zfinx/blob/main/Zfinx_spec.adoc#16-rationale-why-implement-zfinx.

pulkitgupta1217 commented 1 month ago

This is interesting that disassembling libm is using the float operations for you. When I try compiling code that calls the sin and cos functions, I am seeing that there are explicit calls to softfloat routines. I'm going to try building everything from scratch again and see if I can tell what is going on.

One difference I see is that you did make where I do make linux because I need certain features that were not there in the elf build. Could this be causing the difference?

TommyMurphyTM1234 commented 1 month ago

One difference I see is that you did make where I do make linux because I need certain features that were not there in the elf build. Could this be causing the difference?

I have since built the Linux toolchain and see the same sort of floating point instructions in installed-tools/sysroot/usr/lib/libm*, for example. So I still can't seem to reproduce this issue with either the bare-metal or Linux toolchains:

However, it does not use any float instructions in library code, or for floating point comparisons in compiled code, and instead uses the soft-float libraries to accomplish these tasks.

TommyMurphyTM1234 commented 1 month ago

Any update @pulkitgupta1217? As shown above I can't seem to reproduce the issue that you originally reported.

TommyMurphyTM1234 commented 3 weeks ago

Cannot reproduce the original problem so closing this issue.