While doing some tests on REL_10_STABLE, I was getting run-time exceptions
at int8_avg_combine() at the following line:

state1->sumX = state2->sumX;

After some debugging, I noticed that palloc()’s alignment is 8-bytes, while
this statement (which moves a __int128 from one memory location to another
memory location) expects 16-byte memory alignments. So when either state1
or state2 is not 16-byte aligned, this crashes.

When I disassemble the code, the above statement is translated to a pair of
movdqa and movaps assignments when compiled with -O2:

movdqa  c(%rdx), %xmm0
movaps  %xmm0, c(%rcx)

Looking at “Intel 64 and IA-32 Architectures Software Developer’s Manual,
Volume 2”, both of these instructions expect 16-byte aligned memory
locations, or a general-protection exception will be generated.

I wasn’t getting the exception always. For example, the same query that
crashed in REL_10_STABLE didn’t crash in master. But I think that was just
lucky allocation, and we will eventually see cases of this crash in all
versions that use __int128 assignment in int8_avg_combine().

I’ve attached a patch which sets the MAXIMUM_ALIGNOF correctly when
__int128’s are available, so fixes the crash. This patch is on top of
REL_10_STABLE, which is the branch I was getting the crash at. I can create
patches for other branches if we think this is the proper change.

The sequence for which I got the crash in REL_10_STABLE was the following
sequence of commands after a fresh initdb:

CREATE TABLE t(a BIGINT);
INSERT INTO t SELECT * FROM generate_series(1, 10000000);
SELECT sum(a) FROM t;

I’m not sure if this will crash for everyone, since you can be lucky and
have both states assigned 16-byte aligned locations. This was the case for
me in master branch. "SELECT version()" is "PostgreSQL 10.0 on
x86_64-pc-linux-gnu, compiled by gcc (GCC) 7.2.1 20170915 (Red Hat
7.2.1-2), 64-bit", if that is related.

I notice that there’s a comment in configure.in that says the penalty of
using 16-bit alignment might be too much for disk and memory space. If this
is the case, then we need to modify numeric.c to make sure that it never
use any instructions with 16-byte alignment requirements. I can do that if
that is the consensus.

Or maybe we can create an allocation function for 16-byte aligned
allocations.

Any thoughts?

-- Hadi
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7275ea69fe..9fea911e9c 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -126,6 +126,7 @@ return 1;
 ])],
 [pgac_cv__128bit_int=yes],
 [pgac_cv__128bit_int=no])])
+HAVE_128BIT_INT=$pgac_cv__128bit_int
 if test x"$pgac_cv__128bit_int" = xyes ; then
   AC_DEFINE(PG_INT128_TYPE, __int128, [Define to the name of a signed 128-bit integer type.])
 fi])# PGAC_TYPE_128BIT_INT
diff --git a/configure b/configure
index 5a123e8a8a..1813ea3414 100755
--- a/configure
+++ b/configure
@@ -14624,6 +14624,59 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+# Check for extensions offering the integer scalar type __int128.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __int128" >&5
+$as_echo_n "checking for __int128... " >&6; }
+if ${pgac_cv__128bit_int+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/*
+ * These are globals to discourage the compiler from folding all the
+ * arithmetic tests down to compile-time constants.  We do not have
+ * convenient support for 64bit literals at this point...
+ */
+__int128 a = 48828125;
+__int128 b = 97656255;
+
+int
+main ()
+{
+
+__int128 c,d;
+a = (a << 12) + 1; /* 200000000001 */
+b = (b << 12) + 5; /* 400000000005 */
+/* use the most relevant arithmetic ops */
+c = a * b;
+d = (c + b) / b;
+/* return different values, to prevent optimizations */
+if (d != a+1)
+  return 0;
+return 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  pgac_cv__128bit_int=yes
+else
+  pgac_cv__128bit_int=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__128bit_int" >&5
+$as_echo "$pgac_cv__128bit_int" >&6; }
+HAVE_128BIT_INT=$pgac_cv__128bit_int
+if test x"$pgac_cv__128bit_int" = xyes ; then
+
+$as_echo "#define PG_INT128_TYPE __int128" >>confdefs.h
+
+fi
+
 # Determine memory alignment requirements for the basic C data types.
 
 # The cast to long int works around a bug in the HP C Compiler,
@@ -14767,6 +14820,43 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
+fi
+if test x"$HAVE_128BIT_INT" = x"yes" ; then
+  # The cast to long int works around a bug in the HP C Compiler,
+# see AC_CHECK_SIZEOF for more information.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking alignment of __int128" >&5
+$as_echo_n "checking alignment of __int128... " >&6; }
+if ${ac_cv_alignof___int128+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if ac_fn_c_compute_int "$LINENO" "(long int) offsetof (ac__type_alignof_, y)" "ac_cv_alignof___int128"        "$ac_includes_default
+#ifndef offsetof
+# define offsetof(type, member) ((char *) &((type *) 0)->member - (char *) 0)
+#endif
+typedef struct { char x; __int128 y; } ac__type_alignof_;"; then :
+
+else
+  if test "$ac_cv_type___int128" = yes; then
+     { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute alignment of __int128
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_alignof___int128=0
+   fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_alignof___int128" >&5
+$as_echo "$ac_cv_alignof___int128" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define ALIGNOF___INT128 $ac_cv_alignof___int128
+_ACEOF
+
+
 fi
 # The cast to long int works around a bug in the HP C Compiler,
 # see AC_CHECK_SIZEOF for more information.
@@ -14815,6 +14905,9 @@ fi
 if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $MAX_ALIGNOF -lt $ac_cv_alignof_long_long_int ; then
   MAX_ALIGNOF="$ac_cv_alignof_long_long_int"
 fi
+if test x"$HAVE_128BIT_INT" = xyes && test $MAX_ALIGNOF -lt $ac_cv_alignof___int128 ; then
+  MAX_ALIGNOF="$ac_cv_alignof___int128"
+fi
 
 cat >>confdefs.h <<_ACEOF
 #define MAXIMUM_ALIGNOF $MAX_ALIGNOF
@@ -14866,58 +14959,6 @@ _ACEOF
 fi
 
 
-# Check for extensions offering the integer scalar type __int128.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __int128" >&5
-$as_echo_n "checking for __int128... " >&6; }
-if ${pgac_cv__128bit_int+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/*
- * These are globals to discourage the compiler from folding all the
- * arithmetic tests down to compile-time constants.  We do not have
- * convenient support for 64bit literals at this point...
- */
-__int128 a = 48828125;
-__int128 b = 97656255;
-
-int
-main ()
-{
-
-__int128 c,d;
-a = (a << 12) + 1; /* 200000000001 */
-b = (b << 12) + 5; /* 400000000005 */
-/* use the most relevant arithmetic ops */
-c = a * b;
-d = (c + b) / b;
-/* return different values, to prevent optimizations */
-if (d != a+1)
-  return 0;
-return 1;
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  pgac_cv__128bit_int=yes
-else
-  pgac_cv__128bit_int=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__128bit_int" >&5
-$as_echo "$pgac_cv__128bit_int" >&6; }
-if test x"$pgac_cv__128bit_int" = xyes ; then
-
-$as_echo "#define PG_INT128_TYPE __int128" >>confdefs.h
-
-fi
-
 # Check for various atomic operations now that we have checked how to declare
 # 64bit integers.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for builtin __sync char locking functions" >&5
diff --git a/configure.in b/configure.in
index 68a1e0c184..e4a5ae7a10 100644
--- a/configure.in
+++ b/configure.in
@@ -1814,6 +1814,9 @@ fi
 AC_MSG_RESULT([$enable_float8_byval])
 AC_DEFINE_UNQUOTED([FLOAT8PASSBYVAL], [$float8passbyval], [float8, int8, and related values are passed by value if 'true', by reference if 'false'])
 
+# Check for extensions offering the integer scalar type __int128.
+PGAC_TYPE_128BIT_INT
+
 # Determine memory alignment requirements for the basic C data types.
 
 AC_CHECK_ALIGNOF(short)
@@ -1822,6 +1825,9 @@ AC_CHECK_ALIGNOF(long)
 if test x"$HAVE_LONG_LONG_INT_64" = x"yes" ; then
   AC_CHECK_ALIGNOF(long long int)
 fi
+if test x"$HAVE_128BIT_INT" = x"yes" ; then
+  AC_CHECK_ALIGNOF(__int128)
+fi
 AC_CHECK_ALIGNOF(double)
 
 # Compute maximum alignment of any basic type.
@@ -1835,6 +1841,9 @@ fi
 if test x"$HAVE_LONG_LONG_INT_64" = xyes && test $MAX_ALIGNOF -lt $ac_cv_alignof_long_long_int ; then
   MAX_ALIGNOF="$ac_cv_alignof_long_long_int"
 fi
+if test x"$HAVE_128BIT_INT" = xyes && test $MAX_ALIGNOF -lt $ac_cv_alignof___int128 ; then
+  MAX_ALIGNOF="$ac_cv_alignof___int128"
+fi
 AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignment requirement of any C data type.])
 
 
@@ -1843,9 +1852,6 @@ AC_DEFINE_UNQUOTED(MAXIMUM_ALIGNOF, $MAX_ALIGNOF, [Define as the maximum alignme
 AC_CHECK_TYPES([int8, uint8, int64, uint64], [], [],
 [#include <stdio.h>])
 
-# Check for extensions offering the integer scalar type __int128.
-PGAC_TYPE_128BIT_INT
-
 # Check for various atomic operations now that we have checked how to declare
 # 64bit integers.
 PGAC_HAVE_GCC__SYNC_CHAR_TAS
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index 7033042e2d..9311fb7852 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -68,7 +68,7 @@
  * CAUTION: ALLOC_MINBITS must be large enough so that
  * 1<<ALLOC_MINBITS is at least MAXALIGN,
  * or we may fail to align the smallest chunks adequately.
- * 8-byte alignment is enough on all currently known machines.
+ * 16-byte alignment is enough on all currently known machines.
  *
  * With the current parameters, request sizes up to 8K are treated as chunks,
  * larger requests go into dedicated blocks.  Change ALLOCSET_NUM_FREELISTS
@@ -78,7 +78,7 @@
  *--------------------
  */
 
-#define ALLOC_MINBITS		3	/* smallest chunk size is 8 bytes */
+#define ALLOC_MINBITS		4	/* smallest chunk size is 16 bytes */
 #define ALLOCSET_NUM_FREELISTS	11
 #define ALLOC_CHUNK_LIMIT	(1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
 /* Size of largest chunk that we use a fixed size for */
@@ -166,7 +166,7 @@ typedef struct AllocChunkData
 	/* when debugging memory usage, also store actual requested size */
 	/* this is zero in a free chunk */
 	Size		requested_size;
-#if MAXIMUM_ALIGNOF > 4 && SIZEOF_VOID_P == 4
+#if (MAXIMUM_ALIGNOF > 4 && SIZEOF_VOID_P == 4) || (MAXIMUM_ALIGNOF > 8 && SIZEOF_VOID_P == 8)
 	Size		padding;
 #endif
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index c65dd7db21..3c99af4667 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -30,6 +30,9 @@
 /* The normal alignment of `short', in bytes. */
 #undef ALIGNOF_SHORT
 
+/* The normal alignment of `__int128', in bytes. */
+#undef ALIGNOF___INT128
+
 /* Size of a disk block --- this also limits the size of a tuple. You can set
    it bigger if you need bigger tuples (although TOAST should reduce the need
    to have large tuples, since fields can be spread across multiple tuples).
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index 869c59dc85..2dc59e89cd 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -189,7 +189,7 @@ extern MemoryContext SlabContextCreate(MemoryContext parent,
  * Few callers should be interested in this, but tuplesort/tuplestore need
  * to know it.
  */
-#define ALLOCSET_SEPARATE_THRESHOLD  8192
+#define ALLOCSET_SEPARATE_THRESHOLD  16384
 
 #define SLAB_DEFAULT_BLOCK_SIZE		(8 * 1024)
 #define SLAB_LARGE_BLOCK_SIZE		(8 * 1024 * 1024)

Reply via email to