From 14c0c9a9e5eeacc82904f4cc33e0842f5ee9ec44 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Sun, 18 Feb 2024 23:25:15 -0500 Subject: [PATCH] Add configure tests for C11-ish _Alignof and _Alignas. These features are pretty feasible to implement usable macro-based fallbacks on pre-C11 compilers. Add some basic configure probes to check these C11 features and AC_DEFINE reasonable fallbacks where unsupported. If Autoconf already detected C11 support, do nothing (as it tests these features already). Compilers which support these in C11 mode usually also support them in other conformance modes, so it is still useful to probe these specifically. --- m4/align.m4 | 68 ++++++++++++++++++++++++++++ tests/macros.at | 116 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 m4/align.m4 diff --git a/m4/align.m4 b/m4/align.m4 new file mode 100644 index 0000000..03e18a8 --- /dev/null +++ b/m4/align.m4 @@ -0,0 +1,68 @@ +# Copyright © 2024 Nick Bowler +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +# DX_C_ALIGNOF +# +# Probe whether the C compiler understands _Alignof(T). +# +# If not supported, _Alignof(T) is defined as a function-like macro which +# uses offsetof to guess the alignment of T, which must be a type such that +# T x; is a valid declaration of x as an object of type T. It is therefore +# necessary that the caller includes before using _Alignof. +# +# We skip the test if Autoconf has previously determined that the C compiler +# supports C11 or a newer standard, since the C11 test program checks this. +# +# Annoyingly, autoconf-2.71 removed the assignment of ac_cv_prog_cc_c11, +# even though this was actual documented behaviour... +AC_DEFUN([DX_C_ALIGNOF], +[AS_CASE([${ac_cv_prog_cc_c11-no}/${ac_prog_cc_stdc-no}], + [no/no|*/c89|*/c99], + [AC_CACHE_CHECK([if $CC supports _Alignof], [dx_cv_have_alignof], + [AC_COMPUTE_INT([_dx_tmp], [_Alignof(char)], [@&t@], [_dx_tmp=0]) +AS_CASE([$_dx_tmp], [1], [dx_cv_have_alignof=yes], [dx_cv_have_alignof=no])])], + [dx_cv_have_alignof=yes]) +AS_CASE([$dx_cv_have_alignof], [no], [AC_DEFINE([_Alignof(T)], + [offsetof(struct { char a; T b; }, b)], + [Define _Alignof(T) to a suitable fallback if _Alignof is unsupported.])])]) + +# DX_C_ALIGNAS +# +# Probe whether the C compiler understands _Alignas(X). +# +# If not supported, but the compiler supports the GNU __attribute__ or +# Microsoft __declspec methods to set alignment, _Alignas(X) is defined +# as a function-like macro which expands to such syntax. These only +# work if X is an integer constant (as opposed to a type name). +# +# If there is no known method of declaring a variable with increased +# alignment, then _Alignas(X) is defined as a function-like macro which +# expands to nothing, which will compile but has no runtime effect. +# +# We skip the test if Autoconf has previously determined that the C +# compiler supports C11 or a newer standard, since the C11 test program +# checks this. +AC_DEFUN([DX_C_ALIGNAS], +[AS_CASE([${ac_cv_prog_cc_c11-no}/${ac_prog_cc_stdc-no}], + [no/no|*/c89|*/c99], + [AC_CACHE_CHECK([if $CC supports _Alignas], [dx_cv_have_alignas], +[dx_cv_have_alignas=no +for _dx_alignas +in '_Alignas(X)' '__attribute__((__aligned__(X)))' '__declspec(align(X))' +do +AC_COMPUTE_INT([_dx_tmp], + [sizeof (struct { char a; char ALIGNAS_TEST(4) b; }) >= 8], + [#define ALIGNAS_TEST(X) $_dx_alignas +],[_dx_tmp=0]) +AS_CASE([$_dx_tmp/$_dx_alignas], + [1/_Alignas*], [dx_cv_have_alignas=yes; break], + [1/*], [dx_cv_have_alignas=$_dx_alignas; break]) +done])], + [dx_cv_have_alignas=yes]) +AS_CASE([$dx_cv_have_alignas], + [*'(X)'*], [AC_DEFINE_UNQUOTED([_Alignas(X)], [$dx_cv_have_alignas], + [Define _Alignas(X) to a suitable fallback if _Alignas is unsupported.])], + [no], [AC_DEFINE([_Alignas(X)], [/**/])])]) diff --git a/tests/macros.at b/tests/macros.at index 9aadd03..6da14b3 100644 --- a/tests/macros.at +++ b/tests/macros.at @@ -1,4 +1,4 @@ -dnl Copyright © 2014-2015, 2018-2019, 2021-2023 Nick Bowler +dnl Copyright © 2014-2015, 2018-2019, 2021-2024 Nick Bowler dnl dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. dnl This is free software: you are free to do what the fuck you want to. @@ -650,8 +650,7 @@ AT_CHECK([grep HAVE_FOR_DECLS config.h], [0], TEST_CONFIGURE([CC="$CC -std=gnu99"]) AT_CHECK([grep HAVE_FOR_DECLS config.h], [0], [[#define HAVE_FOR_DECLS 1 -]]) -]) +]])]) AT_SETUP([DX_C_FOR_DECLARATIONS (aclocal.m4 +AT_DATA([config.h.in], +[[#undef _Alignof +#undef _Alignas +]]) +TEST_CONFIGURE_AC([m4_ifnblank([$1], [$1]) +[AC_CONFIG_HEADERS([config.h]) +DX_C_ALIGNOF +]]) +AT_CHECK([$AUTOCONF], [0], [], [stderr]) +AT_DATA([test.c], +[[#if __STDC_VERSION__ < 201112 +#error nope +char nope[-1]; +#endif +char yup; +]]) +AT_CHECK([$CC -c -std=gnu11 -pedantic-errors test.c || exit 77], + [0], [ignore], [ignore]) +TEST_CONFIGURE([CC="$CC" CFLAGS="-std=gnu89 -pedantic-errors"]) +AT_CHECK([grep _Align config.h], [0], +[[#define _Alignof(T) offsetof(struct { char a; T b; }, b) +/* #undef _Alignas */ +]]) +TEST_CONFIGURE([CC="$CC -std=gnu11"]) +AT_CHECK([grep _Align config.h], [0], +[[/* #undef _Alignof */ +/* #undef _Alignas */ +]])]) + +AT_SETUP([DX_C_ALIGNOF (autoconf-2.70)]) +AT_KEYWORDS([DX_C_ALIGNOF macro]) +TEST_FIND_AUTOCONF([default 2.71 2.72 2.73 2.74], + [AS_VERSION_COMPARE(["$acver"], [2.70], [continue], [continue], [])]) +TEST_DX_C_ALIGNOF([[AC_PROG_CC]]) +AT_CLEANUP + +m4_define([TEST_DX_C_ALIGNAS], +[cat "$srcdir/m4/align.m4" >aclocal.m4 +AT_DATA([config.h.in], +[[#undef _Alignas +#undef _Alignof +]]) +TEST_CONFIGURE_AC([m4_ifnblank([$1], [$1]) +[AC_CONFIG_HEADERS([config.h]) +AC_COMPUTE_INT([dummy], [0], [@&t@], [dummy=0]) # ensure main CC probes finish +AC_ARG_ENABLE([break-cc], [], [CC=false]) # before we force everything to fail +DX_C_ALIGNAS +]]) +AT_CHECK([$AUTOCONF], [0], [], [stderr]) +AT_DATA([test.c], +[[#if __STDC_VERSION__ < 201112 +#error nope +char nope[-1]; +#endif +char yup; +]]) +AT_CHECK([$CC -c -std=gnu11 -pedantic-errors test.c || exit 77], + [0], [ignore], [ignore]) +TEST_CONFIGURE([CC="$CC" CFLAGS="-std=gnu89 -pedantic-errors"]) +AT_CHECK([grep _Align config.h], [0], +[[#define _Alignas(X) __attribute__((__aligned__(X))) +/* #undef _Alignof */ +]]) +TEST_CONFIGURE([CC="$CC -std=gnu11"]) +AT_CHECK([grep _Align config.h], [0], +[[/* #undef _Alignas */ +/* #undef _Alignof */ +]]) +TEST_CONFIGURE([CC="$CC" CFLAGS="-std=gnu89 -pedantic-errors" --enable-break-cc]) +AT_CHECK([grep _Align config.h], [0], +[[#define _Alignas(X) /**/ +/* #undef _Alignof */ +]])]) + +AT_SETUP([DX_C_ALIGNAS (autoconf-2.70)]) +AT_KEYWORDS([DX_C_ALIGNAS macro]) +TEST_FIND_AUTOCONF([default 2.71 2.72 2.73 2.74], + [AS_VERSION_COMPARE(["$acver"], [2.70], [continue], [continue], [])]) +TEST_DX_C_ALIGNAS([[AC_PROG_CC]]) +AT_CLEANUP -- 2.43.2