From: matoro <mat...@users.noreply.github.com>

My take on implementing bug 820101 as conservatively as possible. This
will append -latomic only when absolutely necessary (even though it's
probably not required to be that conservative due to our use of
--as-needed).

This will take flags into account when testing as well. For example, if
you compile with -march=i386, this would require linking libatomic even
if the same toolchain with a higher -march level would not require it.
So rebuilding the same package with the same flags may change whether
-latomic is linked at build time if the CFLAGS change. Another instance
might be switching from GCC to clang - the former requires explicitly
linking -latomic, while the latter does not even HAVE libatomic (see bug
820095) as all atomic intrinsics are built-in internally. This function
would safely detect this and not append -latomic.

There is an optional parameter [bytes]. You can use this if you want to
be specific about what size atomic support is required. For example,
there are several platforms like MIPS where the 32-bit version has 1-,
2-, and 4-byte atomics builtin but requires libatomic linkage for 8-byte
atomics. If your program only requires, say, 4-byte atomics, you can use
append-atomic-flags 4 and this will then not attempt to link libatomic
on 32-bit MIPS.

I tested using this to solve bug 688574 on 32-bit SPARC.

Closes: https://bugs.gentoo.org/820101
Signed-off-by: matoro <mat...@users.noreply.github.com>
Closes: https://github.com/gentoo/gentoo/pull/26334
Signed-off-by: Sam James <s...@gentoo.org>
---
 eclass/flag-o-matic.eclass | 145 +++++++++++++++++++++++++++++++++++++
 1 file changed, 145 insertions(+)

diff --git a/eclass/flag-o-matic.eclass b/eclass/flag-o-matic.eclass
index 0dd2c1191273..503f23a141a1 100644
--- a/eclass/flag-o-matic.eclass
+++ b/eclass/flag-o-matic.eclass
@@ -875,4 +875,149 @@ no-as-needed() {
        esac
 }
 
+# @FUNCTION: _test-compile-PROG
+# @USAGE: <language> <code>
+# @INTERNAL
+# @DESCRIPTION:
+# Attempts to compile (and possibly link) the given program.  The first
+# <language> parameter corresponds to the standard -x compiler argument.
+# If the program should additionally be attempted to be linked, the string
+# "+ld" should be added to the <language> parameter.
+_test-compile-PROG() {
+       local lang=$1
+       local code=$2
+       shift 2
+
+       [[ -z "${lang}" ]] && return 1
+       [[ -z "${code}" ]] && return 1
+
+       local compiler filename_in filename_out args=() libs=()
+       case "${lang}" in
+               c)
+                       compiler="$(tc-getCC)"
+                       filename_in="${T}/test.c"
+                       filename_out="${T}/test.o"
+                       args+=(${CFLAGS[@]} -xc -c)
+                       ;;
+               c++)
+                       compiler="$(tc-getCXX)"
+                       filename_in="${T}/test.cc"
+                       filename_out="${T}/test.o"
+                       args+=(${CXXFLAGS[@]} -xc++ -c)
+                       ;;
+               f77)
+                       compiler="$(tc-getF77)"
+                       filename_in="${T}/test.f"
+                       filename_out="${T}/test.o"
+                       args+=(${FFFLAGS[@]} -xf77 -c)
+                       ;;
+               f95)
+                       compiler="$(tc-getFC)"
+                       filename_in="${T}/test.f90"
+                       filename_out="${T}/test.o"
+                       args+=(${FCFLAGS[@]} -xf95 -c)
+                       ;;
+               c+ld)
+                       compiler="$(tc-getCC)"
+                       filename_in="${T}/test.c"
+                       filename_out="${T}/test.exe"
+                       args+=(${CFLAGS[@]} ${LDFLAGS[@]} -xc)
+                       libs+=(${LIBS[@]})
+                       ;;
+               c+++ld)
+                       compiler="$(tc-getCXX)"
+                       filename_in="${T}/test.cc"
+                       filename_out="${T}/test.exe"
+                       args+=(${CXXFLAGS[@]} ${LDFLAGS[@]} -xc++)
+                       libs+=(${LIBS[@]})
+                       ;;
+               f77+ld)
+                       compiler="$(tc-getF77)"
+                       filename_in="${T}/test.f"
+                       filename_out="${T}/test.exe"
+                       args+=(${FFLAGS[@]} ${LDFLAGS[@]} -xf77)
+                       libs+=(${LIBS[@]})
+                       ;;
+               f95+ld)
+                       compiler="$(tc-getFC)"
+                       filename_in="${T}/test.f90"
+                       filename_out="${T}/test.exe"
+                       args+=(${FCFLAGS[@]} ${LDFLAGS[@]} -xf95)
+                       libs+=(${LIBS[@]})
+                       ;;
+               *)
+                       die "Unknown compiled language ${lang}"
+                       ;;
+       esac
+
+       printf "%s\n" "${code}" > "${filename_in}" || die "Failed to create 
'${test_in}'"
+
+       "${compiler}" ${args[@]} "${filename_in}" -o "${filename_out}" 
${libs[@]} &>/dev/null
+}
+
+# @FUNCTION: append-atomic-flags
+# @USAGE: [bytes]
+# @DESCRIPTION: Attempts to detect if appending -latomic is required to use
+# a specific-sized atomic intrinsic, and if so, appends it.  If the bytesize
+# is not specified, then check the four most common byte sizes (1, 2, 4, 8).
+# >=16-byte atomics are not included in this default set and must be explicitly
+# passed if required.  This may require you to add a macro definition like
+# -Duint128_t=__uint128_t to your CFLAGS.
+append-atomic-flags() {
+       # this implementation is as described in bug #820101
+       local code
+
+       # first, ensure we can compile a trivial program
+       # this is because we can't distinguish if _test-compile-PROG
+       # fails because -latomic is actually needed or if we have a
+       # broken toolchain (like due to bad FLAGS)
+       read -r -d '' code <<- EOF
+               int main()
+               {
+                       return 0;
+               }
+       EOF
+
+       # if toolchain is broken, just return silently.  it's better to
+       # let other pieces of the build fail later down the line than to
+       # make people think that something to do with atomic support is the
+       # cause of their problems.
+       _test-compile-PROG "c+ld" "${code}" || return
+
+       local bytesizes
+       [[ "${#}" == "0" ]] && bytesizes=( "1" "2" "4" "8" ) || bytesizes="${@}"
+
+       for bytesize in ${bytesizes[@]}
+       do
+               # this sample program is informed by the great testing from the 
buildroot project:
+               # 
https://github.com/buildroot/buildroot/commit/6856e417da4f3aa77e2a814db2a89429af072f7d
+               read -r -d '' code <<- EOF
+                       #include <stdint.h>
+                       int main()
+                       {
+                               uint$((${bytesize} * 8))_t a = 0;
+                               __atomic_add_fetch(&a, 3, __ATOMIC_RELAXED);
+                               __atomic_compare_exchange_n(&a, &a, 2, 1, 
__ATOMIC_RELAXED, __ATOMIC_RELAXED);
+                               return 0;
+                       }
+               EOF
+
+               # do nothing if test program links fine
+               _test-compile-PROG "c+ld" "${code}" && continue
+
+               # ensure that the toolchain supports -latomic
+               test-flags-CCLD "-latomic" &>/dev/null || die "-latomic is 
required but not supported by $(tc-getCC)"
+
+               append-libs "-latomic"
+
+               # verify that this did indeed fix the problem
+               _test-compile-PROG "c+ld" "${code}" || \
+                       die "libatomic does not include an implementation of 
${bytesize}-byte atomics for this toolchain"
+
+               # if any of the required bytesizes require -latomic, no need to 
continue
+               # checking the others
+               return
+       done
+}
+
 fi
-- 
2.37.1


Reply via email to