Subject: Move MAXIMUM_ALIGNOF definition to c.h

From: Peter Eisentraut <[email-masked]>
Date: Tue, 20 Jan 2026 07:48:59 +0100

As suggested at [0], we can move the computation of MAXIMUM_ALIGNOF from 
configure.ac and meson.build to c.h.  This eliminates code duplication. 
(For example, the AIX patch contemplates some changes, so it would be 
good to make those in only one place.)  Also, it used to be more 
complicated, but now it's not really a computation anymore, it's just a 
#define and two static assertions, so it's just much easier and natural 
this way.  (For example, doing a symbol lookup on MAXIMUM_ALIGNOF will 
take you to a place with useful context, instead of to pg_config.h.)

The only trick is that the there are some ordering problems in c.h. 
MAXIMUM_ALIGNOF is used in the definition of the int128, which comes 
very early, before the alignment handling.  I moved the int128 stuff to 
later in the file, near other types with alignment dependencies.  (If 
you look closer, you will find other opportunities for a more 
comprehensive reshuffling in c.h, but maybe that should be a separate 
project.)

[0]: 
https://www.postgresql.org/message-id/CA%2BhUKGLQUivg-NC7dHdbRAPmG0Hapg1gGnygM5KgDfDM2a_TMg%40mail.gmail.com

From 6f7bcca6fc2736af6ba582aaa4233599f8185756 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email-masked]>
Date: Tue, 20 Jan 2026 07:20:30 +0100
Subject: [PATCH] Move MAXIMUM_ALIGNOF definition to c.h

This combines duplicate code from configure.in and meson.build.

Discussion: https://www.postgresql.org/message-id/CA%2BhUKGLQUivg-NC7dHdbRAPmG0Hapg1gGnygM5KgDfDM2a_TMg%40mail.gmail.com
---
 configure                  | 30 --------------
 configure.ac               | 26 ------------
 meson.build                | 25 +-----------
 src/include/c.h            | 81 +++++++++++++++++++++++++-------------
 src/include/pg_config.h.in |  3 --
 5 files changed, 56 insertions(+), 109 deletions(-)

diff --git a/configure b/configure
index fb6a4914b06..5f6f79c9516 100755
--- a/configure
+++ b/configure
@@ -17110,36 +17110,6 @@ _ACEOF

-# Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
-
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'long' is greater than the alignment of 'double'" "$LINENO" 5
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  as_fn_error $? "alignment of 'int64_t' is greater than the alignment of 'double'" "$LINENO" 5
-fi
-
-cat >>confdefs.h <<_ACEOF
-#define MAXIMUM_ALIGNOF $MAX_ALIGNOF
-_ACEOF
-
-
 # Some compilers offer a 128-bit integer scalar type.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __int128" >&5
 $as_echo_n "checking for __int128... " >&6; }
diff --git a/configure.ac b/configure.ac
index d3febfe58f1..dde7dcfa35f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1995,32 +1995,6 @@ AC_CHECK_ALIGNOF(long)
 AC_CHECK_ALIGNOF(int64_t)
 AC_CHECK_ALIGNOF(double)
 
-# Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that long's alignment is at least as strong as
-# char, short, or int.  Note that we intentionally do not consider any types
-# wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8 would be too
-# much of a penalty for disk and memory space.
-
-MAX_ALIGNOF=$ac_cv_alignof_double
-
-if test $ac_cv_alignof_long -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'long' is greater than the alignment of 'double'])
-fi
-if test $ac_cv_alignof_int64_t -gt $MAX_ALIGNOF ; then
-  AC_MSG_ERROR([alignment of 'int64_t' is greater than the alignment of 'double'])
-fi
-AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
-
 # Some compilers offer a 128-bit integer scalar type.
 PGAC_TYPE_128BIT_INT
 
diff --git a/meson.build b/meson.build
index 6d304f32fb0..c5fc29ed681 100644
--- a/meson.build
+++ b/meson.build
@@ -1771,33 +1771,12 @@ endif
 
 # Determine memory alignment requirements for the basic C data types.
 
-alignof_types = ['short', 'int', 'long', 'double']
+alignof_types = ['short', 'int', 'long', 'int64_t', 'double']
 foreach t : alignof_types
-  align = cc.alignment(t, args: test_c_args)
+  align = cc.alignment(t, args: test_c_args, prefix: '#include <stdint.h>')
   cdata.set('ALIGNOF_@0@'.format(t.to_upper()), align)
 endforeach
 
-# Compute maximum alignment of any basic type.
-#
-# We require 'double' to have the strictest alignment among the basic types,
-# because otherwise the C ABI might impose 8-byte alignment on some of the
-# other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
-# cause a mismatch between the tuple layout and the C struct layout of a
-# catalog tuple.  We used to carefully order catalog columns such that any
-# fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
-# of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
-# where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
-#
-# We assume without checking that int64_t's alignment is at least as strong
-# as long, char, short, or int.  Note that we intentionally do not consider
-# any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to exceed 8
-# would be too much of a penalty for disk and memory space.
-alignof_double = cdata.get('ALIGNOF_DOUBLE')
-if cc.alignment('int64_t', args: test_c_args, prefix: '#include <stdint.h>') > alignof_double
-  error('alignment of int64_t is greater than the alignment of double')
-endif
-cdata.set('MAXIMUM_ALIGNOF', alignof_double)
-
 cdata.set('SIZEOF_LONG', cc.sizeof('long', args: test_c_args))
 cdata.set('SIZEOF_LONG_LONG', cc.sizeof('long long', args: test_c_args))
 cdata.set('SIZEOF_VOID_P', cc.sizeof('void *', args: test_c_args))
diff --git a/src/include/c.h b/src/include/c.h
index 7136102e5ff..b6a7330c88f 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -565,33 +565,6 @@ typedef uint32 bits32;			/* >= 32 bits */
 #define UINT64_FORMAT "%" PRIu64
 #define OID8_FORMAT "%" PRIu64
 
-/*
- * 128-bit signed and unsigned integers
- *		There currently is only limited support for such types.
- *		E.g. 128bit literals and snprintf are not supported; but math is.
- *		Also, because we exclude such types when choosing MAXIMUM_ALIGNOF,
- *		it must be possible to coerce the compiler to allocate them on no
- *		more than MAXALIGN boundaries.
- */
-#if defined(PG_INT128_TYPE)
-#if defined(pg_attribute_aligned) || ALIGNOF_PG_INT128_TYPE <= MAXIMUM_ALIGNOF
-#define HAVE_INT128 1
-
-typedef PG_INT128_TYPE int128
-#if defined(pg_attribute_aligned)
-			pg_attribute_aligned(MAXIMUM_ALIGNOF)
-#endif
-		   ;
-
-typedef unsigned PG_INT128_TYPE uint128
-#if defined(pg_attribute_aligned)
-			pg_attribute_aligned(MAXIMUM_ALIGNOF)
-#endif
-		   ;
-
-#endif
-#endif
-
 /* Historical names for limits in <stdint.h>. */
 #define PG_INT8_MIN		INT8_MIN
 #define PG_INT8_MAX		INT8_MAX
@@ -802,6 +775,33 @@ typedef NameData *Name;
  */
 #define lengthof(array) (sizeof (array) / sizeof ((array)[0]))
 
+/*
+ * Compute maximum alignment of any basic type.
+ *
+ * We require 'double' to have the strictest alignment among the basic types,
+ * because otherwise the C ABI might impose 8-byte alignment on some of the
+ * other C types that correspond to TYPALIGN_DOUBLE SQL types.  That could
+ * cause a mismatch between the tuple layout and the C struct layout of a
+ * catalog tuple.  We used to carefully order catalog columns such that any
+ * fixed-width, attalign=4 columns were at offsets divisible by 8 regardless
+ * of MAXIMUM_ALIGNOF to avoid that, but we no longer support any platforms
+ * where TYPALIGN_DOUBLE != MAXIMUM_ALIGNOF.
+ *
+ * We assume without checking that the alignments of int64_t and long are at
+ * least as strong as char, short, or int.  Note that we intentionally do not
+ * consider any types wider than 64 bits, as allowing MAXIMUM_ALIGNOF to
+ * exceed 8 would be too much of a penalty for disk and memory space.
+ */
+
+static_assert(ALIGNOF_LONG <= ALIGNOF_DOUBLE,
+			  "alignment of 'long' is greater than the alignment of 'double'");
+
+static_assert(ALIGNOF_INT64_T <= ALIGNOF_DOUBLE,
+			  "alignment of 'int64_t' is greater than the alignment of 'double'");
+
+/* The maximum alignment requirement of any C data type */
+#define MAXIMUM_ALIGNOF ALIGNOF_DOUBLE
+
 /* ----------------
  * Alignment macros: align a length or address appropriately for a given type.
  * The fooALIGN() macros round up to a multiple of the required alignment,
@@ -1102,6 +1102,33 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
 #define INVERT_COMPARE_RESULT(var) \
 	((var) = ((var) < 0) ? 1 : -(var))
 
+/*
+ * 128-bit signed and unsigned integers
+ *		There currently is only limited support for such types.
+ *		E.g. 128bit literals and snprintf are not supported; but math is.
+ *		Also, because we exclude such types when choosing MAXIMUM_ALIGNOF,
+ *		it must be possible to coerce the compiler to allocate them on no
+ *		more than MAXALIGN boundaries.
+ */
+#if defined(PG_INT128_TYPE)
+#if defined(pg_attribute_aligned) || ALIGNOF_PG_INT128_TYPE <= MAXIMUM_ALIGNOF
+#define HAVE_INT128 1
+
+typedef PG_INT128_TYPE int128
+#if defined(pg_attribute_aligned)
+			pg_attribute_aligned(MAXIMUM_ALIGNOF)
+#endif
+		   ;
+
+typedef unsigned PG_INT128_TYPE uint128
+#if defined(pg_attribute_aligned)
+			pg_attribute_aligned(MAXIMUM_ALIGNOF)
+#endif
+		   ;
+
+#endif
+#endif
+
 /*
  * Use this, not "char buf[BLCKSZ]", to declare a field or local variable
  * holding a page buffer, if that page might be accessed as a page.  Otherwise
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 339268dc8ef..b42cf0df5cc 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -547,9 +547,6 @@
 /* Define to 1 if you have __get_cpuid_count. */
 #undef HAVE__GET_CPUID_COUNT
 
-/* Define as the maximum alignment requirement of any C data type. */
-#undef MAXIMUM_ALIGNOF
-
 /* Define bytes to use libc memset(). */
 #undef MEMSET_LOOP_LIMIT

base-commit: 7ebb64c557570647e3fcf6f5f1549e882ed26489
-- 
2.52.0

---

From: Tom Lane <[email-masked]>
Date: Thu, 19 Feb 2026 19:23:44 -0500

Peter Eisentraut <[email-masked]> writes:
> good to make those in only one place.)

I looked at this but am pretty underwhelmed.  Yes, it replaces two
copies of that logic with one, but said logic is so trivial that
that's not a big win.  Moreover, I'm finding it hard to believe
that we'll ever need to change that logic again --- the plain
max-of-two-alignment-measurements logic is the way it had been
for a long time and will be again after the AIX patch lands.  I can't
foresee a future platform where it'd need to be any different.

In exchange for that simplification, we are taking a computation
that currently is done once per configure/meson-setup step, and
instead computing it during each .c file build.  I imagine that
at least the autoconf implementation is more expensive than
what the C compiler would need to do, but repeating it over and
over again will surely end up costing more.

So this doesn't seem like a good change to me.

			regards, tom lane