ARM Cortex-M4 Floating-Point Software Emulation and libgcc Configuration Challenges
When working with ARM Cortex-M4 processors, developers often rely on the GNU Compiler Collection (GCC) to provide essential runtime libraries, including libgcc
, which contains low-level routines for arithmetic operations, exception handling, and other critical functions. One of the key components of libgcc
is the software emulation of floating-point operations, which is crucial for Cortex-M4 devices that lack a hardware Floating-Point Unit (FPU). However, GCC includes optimized versions of these floating-point routines by default, which can complicate performance comparisons between different architectures, such as ARM Cortex-M4 and RISC-V.
The optimized floating-point routines, such as those found in ieee754-sf.S
, are highly efficient but may not be suitable for scenarios where developers need to compare the performance of software-emulated floating-point operations across different architectures. In such cases, it becomes necessary to exclude these optimized routines and use the standard, unoptimized versions instead. This process involves modifying the GCC build configuration and libgcc
source code to ensure that the compiler uses the desired routines.
The core issue arises from the fact that GCC’s build system is designed to include optimized routines by default, and these routines are tightly integrated into the libgcc
library. The optimized routines are often implemented in assembly language for maximum performance, and they are included in the build process through specific Makefile variables and configuration settings. Excluding these routines requires a deep understanding of the GCC build system and the ability to modify its configuration files.
Makefile Variables and Configuration Settings Impacting Floating-Point Routine Inclusion
The inclusion of optimized floating-point routines in libgcc
is controlled by several key components in the GCC build system. These components include Makefile variables, configuration scripts, and source code modifications. Understanding these components is essential for successfully excluding the optimized routines.
LIB1ASMFUNCS and LIB2FUNCS_EXCLUDE Variables
The LIB1ASMFUNCS
variable in the GCC Makefile specifies the list of assembly functions that are included in the libgcc
library. These functions are often optimized for performance and are included by default in the build process. To exclude these optimized routines, developers need to modify the LIB1ASMFUNCS
variable to remove the relevant functions. Additionally, the LIB2FUNCS_EXCLUDE
variable can be used to exclude specific C functions from the build.
For example, the ieee754-sf.S
file contains optimized single-precision floating-point routines that are included in libgcc
through the LIB1ASMFUNCS
variable. By commenting out the lines that add these functions to the libgcc-objects
and libgcc-s-objects
variables in the Makefile, developers can prevent the optimized routines from being included in the final library.
Configuration Scripts and Conditional Compilation
The GCC build system uses configuration scripts to determine which routines are included in libgcc
. These scripts often rely on conditional compilation to include or exclude specific routines based on the target architecture and configuration options. For example, the t-softfp
configuration file in the libgcc/config/arm
directory contains settings that control the inclusion of software floating-point routines.
In the t-softfp
file, the softfp_wrap_start
and softfp_wrap_end
macros define the conditions under which the software floating-point routines are included. By modifying these macros, developers can change the conditions under which the routines are included. For example, changing softfp_wrap_start
from '\#if !__ARM_ARCH_ISA_ARM && __ARM_ARCH_ISA_THUMB == 1'
to '\#if 1'
ensures that the software floating-point routines are always included, regardless of the target architecture.
Source Code Modifications and Function Aliasing
In addition to modifying the Makefile and configuration scripts, developers may need to make changes to the GCC source code to ensure that the compiler uses the desired floating-point routines. One common issue is that the GCC compiler may still reference the optimized routines even after they have been excluded from libgcc
. This occurs because the compiler uses function aliasing to map standard function names to their optimized counterparts.
For example, the __aeabi_fadd
function is an alias for the optimized single-precision floating-point addition routine. Even if the optimized routine is excluded from libgcc
, the compiler may still generate code that references __aeabi_fadd
. To address this issue, developers need to modify the GCC source code to remove the function aliasing. This can be done by commenting out the relevant lines in the arm.c
file, which contains the function aliases for the ARM architecture.
Steps to Exclude Optimized Floating-Point Routines and Ensure Correct Compilation
Excluding optimized floating-point routines from libgcc
and ensuring that the compiler uses the correct routines involves several steps. These steps include modifying the GCC build configuration, updating the Makefile, and making changes to the GCC source code. The following sections provide a detailed guide on how to perform these steps.
Step 1: Modify the GCC Makefile to Exclude Optimized Routines
The first step in excluding optimized floating-point routines is to modify the GCC Makefile to prevent the optimized routines from being included in libgcc
. This involves commenting out the lines that add the optimized routines to the libgcc-objects
and libgcc-s-objects
variables.
For example, in the libgcc/Makefile.in
file, locate the following lines:
libgcc-objects += $(lib1asmfuncs-o)
...
libgcc-s-objects += $(lib1asmfuncs-s-o)
Comment out these lines to prevent the optimized routines from being included:
# libgcc-objects += $(lib1asmfuncs-o)
...
# libgcc-s-objects += $(lib1asmfuncs-s-o)
This change ensures that the optimized routines are not included in the final libgcc
library.
Step 2: Update the Configuration Scripts to Include Unoptimized Routines
The next step is to update the configuration scripts to ensure that the unoptimized floating-point routines are included in libgcc
. This involves modifying the t-softfp
file in the libgcc/config/arm
directory.
In the t-softfp
file, locate the following lines:
softfp_wrap_start := '\#if !__ARM_ARCH_ISA_ARM && __ARM_ARCH_ISA_THUMB == 1'
softfp_wrap_end := '\#endif'
Change these lines to ensure that the software floating-point routines are always included:
softfp_wrap_start := '\#if 1'
softfp_wrap_end := '\#endif'
This change ensures that the unoptimized floating-point routines are included in libgcc
, regardless of the target architecture.
Step 3: Modify the GCC Source Code to Remove Function Aliasing
The final step is to modify the GCC source code to remove function aliasing for the optimized floating-point routines. This involves commenting out the lines in the arm.c
file that define the function aliases.
For example, in the gcc/config/arm/arm.c
file, locate the following line:
set_optab_libfunc (add_optab, SFmode, "__aeabi_fadd");
Comment out this line to prevent the compiler from using the optimized floating-point addition routine:
// set_optab_libfunc (add_optab, SFmode, "__aeabi_fadd");
This change ensures that the compiler uses the standard floating-point routines instead of the optimized ones.
Step 4: Rebuild GCC and libgcc
After making the necessary modifications to the Makefile, configuration scripts, and source code, the final step is to rebuild GCC and libgcc
. This involves running the following commands in the GCC build directory:
make all-target-libgcc
make install-target-libgcc
These commands rebuild libgcc
with the modified configuration and install the updated library. Once the build process is complete, the new libgcc
library will contain the unoptimized floating-point routines, and the compiler will use these routines instead of the optimized ones.
Step 5: Verify the Changes
To verify that the changes have been applied correctly, developers can compile a test program that uses floating-point operations and inspect the generated code. The test program should use the standard floating-point routines, such as __addsf3
for single-precision addition, instead of the optimized routines like __aeabi_fadd
.
For example, compile the following test program:
float add_floats(float a, float b) {
return a + b;
}
Use the -S
option to generate assembly code and inspect the output:
arm-none-eabi-gcc -S -o test.s test.c
Inspect the generated assembly code to ensure that the __addsf3
function is used for floating-point addition:
bl __addsf3
If the generated code uses the standard floating-point routines, the changes have been applied successfully.
Conclusion
Excluding optimized floating-point routines from libgcc
and ensuring that the compiler uses the correct routines is a complex process that requires a deep understanding of the GCC build system and the ability to modify its configuration files. By following the steps outlined in this guide, developers can successfully exclude the optimized routines and use the standard, unoptimized versions for performance comparisons between different architectures. This approach is particularly useful for scenarios where developers need to compare the performance of software-emulated floating-point operations across different architectures, such as ARM Cortex-M4 and RISC-V.