With the complete CM0 library integrated, regression testing showed new failures with the message "compilation failed to produce executable":
gcc.dg/fixed-point/convert-float-1.c gcc.dg/fixed-point/convert-float-3.c gcc.dg/fixed-point/convert-sat.c Investigating, this appears to be caused by the linker. I can't find a comprehensive linker specification to claim this is actually a bug, but it certainly doesn't match my expectations. Investigating, I found issues with the link order of these symbols: * __aeabi_fmul() * __aeabi_f2d() * __aeabi_f2iz() Specifically, I expect the linker to import the _first_ definition of any symbol. This is the basic behavior that allows the soft-float library to supply missing symbols on architectures without optimized routines. Comparing the v6-m multilib with the default, I see symbol exports for all of the affect symbols: gcc-obj/gcc/libgcc.a: // assembly routines _arm_mulsf3.o: 00000000 W __aeabi_fmul 00000000 W __mulsf3 _arm_addsubdf3.o: 00000368 T __aeabi_f2d 00000368 T __extendsfdf2 _arm_fixsfsi.o: 00000000 T __aeabi_f2iz 00000000 T __fixsfsi mulsf3.o: <empty> fixsfsi.o: <empty> extendsfdf2.o.o: <empty> gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a: // assembly routines _arm_mulsf3.o: 00000000 T __aeabi_fmul U __fp_assemble U __fp_exception U __fp_infinity U __fp_zero 00000000 T __mulsf3 U __umulsidi3 _arm_fixsfsi.o: 00000000 T __aeabi_f2iz 00000000 T __fixsfsi 00000002 T __internal_f2iz _arm_f2d.o: 00000000 T __aeabi_f2d 00000000 T __extendsfdf2 U __fp_normalize2 // soft-float library mulsf3.o: 00000000 T __aeabi_fmul fixsfsi.o: 00000000 T __aeabi_f2iz extendsfdf2.o: 00000000 T __aeabi_f2d Given the order of the archive file, I expect the linker to import the affected functions from the _arm_* archive elements. For "convert-sat.c", all is well with -march=armv7-m. ... (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_muldf3.o OK> (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_mulsf3.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_cmpsf2.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_fixsfsi.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_fixunssfsi.o OK> (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_addsubdf3.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_cmpdf2.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_fixdfsi.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_arm_fixunsdfsi.o OK> (/home/mirdan/gcc-obj/gcc/libgcc.a)_fixsfdi.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_fixdfdi.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_fixunssfdi.o (/home/mirdan/gcc-obj/gcc/libgcc.a)_fixunsdfdi.o ... However, with -march=armv6s-m, the linker imports these symbols from the soft- float library. (NOTE: The CM0 library only implements single-precision float operations, so imports from muldf3.o, fixdfsi.o, etc are expected.) ... ??> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)mulsf3.o ??> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)fixsfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)muldf3.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)fixdfsi.o ??> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)extendsfdf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_clzsi2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fcmpge.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fcmple.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixsfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunssfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunssfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_cmpdf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunsdfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixdfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunsdfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)eqdf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)gedf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)ledf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)subdf3.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)floatunsidf.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_cmpsf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixsfsi.o ... It seems that the order in which the linker resolves symbols matters. In the affected test cases, the linker begins searching for fixed-point function symbols first: _subQQ.o, _cmpQQ.o, etc. The fixed-point archive elements appear after the _arm_* archive elements, so the initial definitions of the floating point functions are discarded. However, the fixed-point functions contain unresolved symbol references which the linker registers progressively. Given that the default libgcc.a does not build the soft-point library [1], the linker cannot import any floating point objects until the second pass. However, when v6-m/nofp/libgcc.a _does_ include the soft-point library, the linker proceeds to import some floating point objects during the first pass. To test this theory, add explicit symbol references to convert-sat.c: --- a/gcc/testsuite/gcc.dg/fixed-point/convert-sat.c +++ b/gcc/testsuite/gcc.dg/fixed-point/convert-sat.c @@ -11,6 +11,12 @@ extern void abort (void); int main () { + volatile float a = 1.0; + volatile float b = 2.0; + volatile float c = a * b; + volatile double d = a; + volatile int e = a; + SAT_CONV1 (short _Accum, hk); SAT_CONV1 (_Accum, k); SAT_CONV1 (long _Accum, lk); Afterwards, the linker imports the expected symbols: ... ==> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_mulsf3.o ==> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_muldi3.o ==> (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fixsfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_f2d.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fp_exceptionf.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fp_assemblef.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fp_normalizef.o ... (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)muldf3.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)fixdfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_clzsi2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fixunssfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fcmpge.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fcmple.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fixsfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_fixunssfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_cmpdf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunsdfsi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixdfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_fixunsdfdi.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)eqdf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)gedf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)ledf2.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)subdf3.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)floatunsidf.o (/home/mirdan/gcc-obj/gcc/thumb/v6-m/nofp/libgcc.a)_arm_cmpsf2.o ... At a minimum this behavior results in the use of non-preferred code in an affected application. However, as long as each object exports a single entry point, this does not automatically result in a build failure. Indeed, in the case of __aeabi_fmul() and __aeabi_f2d(), all references seem to resolve uniformly in favor of the soft-float library. The first pass that imports the soft-float version of __aeabi_f2iz() also succeeds. However, the first pass fails to find __aeabi_f2uiz(), since the soft-float library does not implement this variant. So, this symbol remains undefined until the second pass. However, the assembly version of __aeabi_f2uiz() the linker finds happens to be implemented as a branch to __internal_f2iz() [2]. But the linker, importing __internal_f2iz(), also finds the main entry point __aeabi_f2iz(). And, since __aeabi_f2iz() was already found in the soft-float library, the linker throws an error. The solution is two-fold. First, the assembly routines have separately been made robust against this potential error condition (by weakening and splitting symbols). Second, this commit to block single-precision functions from the soft-float library makes it impossible for the linker to select a non-preferred version. Two duplicate symbols remain (extendsfdf2) and (truncdfsf2), but the situation is much improved. [1] softfp_wrap_start = "#if !__ARM_ARCH_ISA_ARM && __ARM_ARCH_ISA_THUMB == 1" [2] (These operations share a substantial portion of their code path, so this choice leads to a size reduction in programs that use both functions.) gcc/libgcc/ChangeLog: 2021-01-13 Daniel Engel <g...@danielengel.com> * config/arm/t-softfp (softfp_float_modes): Added as "df". --- libgcc/config/arm/t-softfp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libgcc/config/arm/t-softfp b/libgcc/config/arm/t-softfp index 554ec9bc47b..bd6a4642e5f 100644 --- a/libgcc/config/arm/t-softfp +++ b/libgcc/config/arm/t-softfp @@ -1,2 +1,4 @@ softfp_wrap_start := '\#if !__ARM_ARCH_ISA_ARM && __ARM_ARCH_ISA_THUMB == 1' softfp_wrap_end := '\#endif' +softfp_float_modes := df + -- 2.25.1