# HG changeset patch
# Parent c5ec823973c104bcfeff872edfe1b02aeff19d6f
Implement test suite for aligned heap management API.

* tests/testsuite.at.in: Include...
* tests/memalign.at: ...this new file; it replaces...
* mingwex/tst-aligned-malloc.c: ...this; delete it.

diff --git a/mingwrt/mingwex/tst-aligned-malloc.c b/mingwrt/mingwex/tst-aligned-malloc.c
deleted file mode 100644
--- a/mingwrt/mingwex/tst-aligned-malloc.c
+++ /dev/null
@@ -1,90 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <string.h>
-#include <errno.h>
-#include <malloc.h>
-
-#ifdef HAVE_STDINT_H
-#  include <stdint.h>		/* uintptr_t */
-#else
-#  define uintptr_t size_t
-#endif
-
-#define NELEM(a) (sizeof(a) / sizeof(a[0]))
-
-static int
-is_aligned (void *p, size_t alignment, size_t offset)
-{
-  return !((((uintptr_t) p) + offset) & (alignment - 1));
-}
-
-#define MAX_SIZE (1 << sizeof(unsigned char))
-#define NP 1000
-#define NTST 100000U
-
-#define ERRMSG fprintf(stderr, "Iteration %u, align = %u, offset = %u, size = %u (oldsize = %u), errno = %d (%s)\n", i, align[j % NELEM(align)], offset[j % NELEM(offset)], size[j], oldsize, errno, strerror(errno))
-
-int
-main (void)
-{
-  unsigned char *p[NP];
-  size_t size[NP];
-  size_t align[] = { 2, 4, 8, 16, 32, 64 };
-  size_t offset[20];
-  unsigned i;
-
-  srand (time (NULL));
-
-  for (i = 0; i < NELEM (p); ++i)
-    {
-      p[i] = 0;
-      size[i] = 0;
-    }
-
-  for (i = 0; i < NELEM (offset); ++i)
-    offset[i] = rand () % 512;
-
-  for (i = 0; i < NTST; ++i)
-    {
-      size_t oldsize;
-      unsigned j, k;
-      j = rand () % NELEM (p);
-      oldsize = size[j];
-      p[j] = __mingw_aligned_offset_realloc (p[j],
-					     size[j] = rand () % MAX_SIZE,
-					     align[j % NELEM (align)],
-					     offset[j % NELEM (offset)]);
-
-      if (size[j] && !p[j])
-	{
-	  fprintf (stderr, "Returned NULL!\n");
-	  ERRMSG;
-	  return EXIT_FAILURE;
-	}
-      if (size[j] && !is_aligned (p[j],
-				  align[j % NELEM (align)],
-				  offset[j % NELEM (offset)]))
-	{
-	  fprintf (stderr, "Misaligned block!\n");
-	  ERRMSG;
-	  return EXIT_FAILURE;
-	}
-      if (oldsize > size[j])
-	oldsize = size[j];
-      for (k = 0; k < oldsize; ++k)
-	if (p[j][k] != k)
-	  {
-	    fprintf (stderr, "Miscopied block!\n");
-	    ERRMSG;
-	    return EXIT_FAILURE;
-	  }
-      for (k = 0; k < size[j]; ++k)
-	p[j][k] = k;
-    }
-
-  for (i = 0; i < NELEM (p); ++i)
-    __mingw_aligned_free (p[i]);
-
-  return EXIT_SUCCESS;
-}
diff --git a/mingwrt/tests/memalign.at b/mingwrt/tests/memalign.at
new file mode 100644
--- /dev/null
+++ b/mingwrt/tests/memalign.at
@@ -0,0 +1,487 @@
+# memalign.at
+#
+# Autotest module to verify correct operation of the MinGW aligned
+# heap memory management API.
+#
+# $Id$
+#
+# Written by Keith Marshall <keith@users.osdn.me>
+# Copyright (C) 2018, MinGW.org Project
+#
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+#
+# All tests specified herein are written in the C language.
+#
+MINGW_AT_LANG([C])
+
+AT_BANNER([Aligned heap memory allocation function checks.])
+#-----------------------------------------------------------
+# Implement a collection of tests to exercise the MinGW aligned memory
+# allocation functions, and confirm that they yield correcly aligned heap
+# memory blocks, or that they fail appropriately.
+
+AT_SETUP([Allocation on valid alignment boundaries])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_malloc(), and __mingw_aligned_offset_malloc()
+ * return non-NULL pointers, which are numerically exact integer multiples of
+ * any specified alignment.  Also gratuitously uses __mingw_free(), but does
+ * not verify its behaviour; this is checked independently.
+ */
+#include <malloc.h>
+int main()
+{ size_t alignment, status = 0;
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { void *ptr = __mingw_aligned_malloc(128, alignment);
+    status |= (ptr != NULL) ? (size_t)(ptr) % alignment : 1;
+    __mingw_free(ptr);
+    ptr = __mingw_aligned_offset_malloc(128, alignment, 5);
+    status |= (ptr != NULL) ? ((size_t)(ptr) + 5) % alignment : 1;
+    __mingw_free(ptr);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Rejection of invalid alignment specifications])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_malloc(), and __mingw_aligned_offset_malloc()
+ * fail, returning a NULL pointer, and setting errno to EINVAL, when called
+ * with an alignment argument which is not an integer power of two.  Again,
+ * this gratuitously uses __mingw_free(), without attempting to verify its
+ * behaviour, which is checked independently.
+ */
+#include <malloc.h>
+#include <errno.h>
+int main()
+{ size_t alignment, status = 0;
+  for( alignment = sizeof (void *); 128 >= alignment; alignment <<= 1 )
+  { void *ptr = __mingw_aligned_malloc(128, alignment - 1);
+    if( (ptr != NULL) || (errno != EINVAL) ) status = 1;
+    __mingw_free(ptr);
+    ptr = __mingw_aligned_offset_malloc(128, alignment - 1, 5);
+    if( (ptr != NULL) || (errno != EINVAL) ) status = 1;
+    __mingw_free(ptr);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Rejection of excessive offset specifications])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_offset_malloc() fails, returning a NULL
+ * pointer, and setting errno to EINVAL, when called with any value for
+ * its offset argument which is non-zero, and greater than or equal to
+ * the requested size argument.
+ */
+#include <malloc.h>
+#include <errno.h>
+int main()
+{ size_t alignment, status = 0;
+  for( alignment = sizeof (void *); 128 >= alignment; alignment <<= 1 )
+  { void *ptr = __mingw_aligned_offset_malloc(128, alignment, 128);
+    if( (ptr != NULL) || (errno != EINVAL) ) status = 1;
+    __mingw_free(ptr);
+    ptr = __mingw_aligned_offset_malloc(128, alignment, 132);
+    if( (ptr != NULL) || (errno != EINVAL) ) status = 1;
+    __mingw_free(ptr);
+    errno = 0; ptr = __mingw_aligned_offset_malloc(0, alignment, 0);
+    if( (ptr == NULL) && (errno == EINVAL) ) status |= 2;
+    __mingw_free(ptr);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_BANNER([Aligned heap memory reallocation function checks.])
+#-------------------------------------------------------------
+# Implement a collection of tests to exercise the MinGW aligned memory
+# specific reallocation functions, and confirm that they yield correcly
+# aligned heap memory blocks, or that they fail appropriately.
+
+AT_SETUP([Reallocation of specifically aligned memory])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_realloc(), and __mingw_aligned_offset_realloc()
+ * correctly reallocate previously allocated aligned memory blocks, preserving
+ * original block content, when called with acceptable arguments.
+ */
+#include <malloc.h>
+#include <string.h>
+int main()
+{ size_t alignment, status = 0;
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size;
+    for( size = 32; 256 >= size; size <<= 1 )
+    { if( (p3 = __mingw_aligned_realloc( p1, size, alignment)) != NULL )
+      { p1 = p3;
+	if( p2 == NULL ) { strcpy( p1, "Sample text." ); p2 = strdup( p1 ); }
+	status |= p2 ? strcmp( p1, p2 ) | ((size_t)(p1) % alignment) : 1;
+      }
+      else status |= 1;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size;
+    for( size = 32; 256 >= size; size <<= 1 )
+    { if( (p3 = __mingw_aligned_offset_realloc( p1, size, alignment, 5)) != NULL )
+      { p1 = p3;
+	if( p2 == NULL ) { strcpy( p1, "Sample text." ); p2 = strdup( p1 ); }
+	status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + 5) % alignment) : 1;
+      }
+      else status |= 1;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Offset constrained rejection of size reduction])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_offset_realloc() correctly rejects an attempt
+ * to reduce the size of a previously offset-aligned memory block, when the
+ * resultant size will be insufficient to accommodate the specified offset;
+ * such rejection must be indicated by returning a NULL pointer, with errno
+ * set to EINVAL.
+ */
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+int main()
+{ size_t align, status = 0;
+  for( align = 1; 128 >= align; align <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size;
+    for( size = 128; size > 0; size >>= 1 )
+    { if( (p3 = __mingw_aligned_offset_realloc( p1, size, align, 16)) != NULL )
+      { if( p3 != p1 ) p1 = p3;
+	if( p2 == NULL ) { strcpy( p1, "Sample text." ); p2 = strdup( p1 ); }
+	status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + 16) % align) : 2;
+	if( 16 >= size ) status |= 1;
+      }
+      else if( (p2 == NULL) || (errno != EINVAL) ) status |= 4;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Rejection of alignment specification changes])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_realloc(), and __mingw_aligned_offset_realloc()
+ * fail, returning a NULL pointer, and setting errno to EINVAL, when called
+ * with an alignment parameter which differs from that originally specified,
+ * when the aligned memory block was first allocated.
+ */
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+int main()
+{ size_t alignment, status = 0;
+  for( alignment = 8; 128 >= alignment; alignment <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size, align;
+    for( size = 128, align = alignment; 256 >= size; size <<= 1, align <<= 1 )
+    { if( (p3 = __mingw_aligned_realloc( p1, size, align)) != NULL )
+      { p1 = p3;
+	if( p2 == NULL )
+	{ strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+	  status |= p2 ? strcmp( p1, p2 ) | ((size_t)(p1) % align) : 2;
+	}
+	else status |= 1;
+      }
+      else if( (p2 == NULL) || (errno != EINVAL) ) status |= 4;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  for( alignment = 8; 128 >= alignment; alignment <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size, align;
+    for( size = 128, align = alignment; 256 >= size; size <<= 1, align <<= 1 )
+    { if( (p3 = __mingw_aligned_offset_realloc( p1, size, align, 5)) != NULL )
+      { p1 = p3;
+	if( p2 == NULL )
+	{ strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+	  status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + 5) % align) : 2;
+	}
+	else status |= 1;
+      }
+      else if( (p2 == NULL) || (errno != EINVAL) ) status |= 4;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Rejection of offset specification changes])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_aligned_offset_realloc() fails, returning a NULL
+ * pointer, and setting errno to EINVAL, when called an offset parameter
+ * which differs from that specified when the offset-aligned memory block
+ * was originally allocated.
+ */
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+int main()
+{ size_t align, status = 0;
+  for( align = 1; 128 >= align; align <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size, offset;
+    for( size = 128, offset = 4; 256 >= size; size <<= 1, offset <<= 1 )
+    { if( (p3 = __mingw_aligned_offset_realloc( p1, size, align, offset)) != NULL )
+      { if( p3 != p1 ) p1 = p3;
+	if( p2 == NULL )
+	{ strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+	  status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + offset) % align) : 2;
+	}
+	else status |= 1;
+      }
+      else if( (p2 == NULL) || (errno != EINVAL) ) status |= 4;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_BANNER([Universal heap memory reallocation function checks.])
+#---------------------------------------------------------------
+# Implement a collection of tests to exercise the MinGW generic memory
+# reallocation function, __mingw_realloc(), to confirm that it correctly
+# discriminates between aligned and regular memory blocks, yields correcly
+# aligned memory blocks, when appropriate, or that it fails correctly,
+# when expected.
+#
+AT_SETUP([Reallocation of specifically aligned memory])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_realloc() can successfully resize an initially
+ * offset-aligned memory block, preserving its alignment, its offset,
+ * and its content.
+ */
+#include <malloc.h>
+#include <string.h>
+int main()
+{ size_t align, status = 0;
+  for( align = 1; 128 >= align; align <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size = 64;
+    if( (p1 = __mingw_aligned_offset_malloc( size, align, 16)) != NULL )
+    { strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+      for( size = 128; 512 >= size; size <<= 1 )
+	if( (p3 = __mingw_realloc( p1, size )) != NULL )
+	{ p1 = p3;
+	  status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + 16) % align) : 2;
+	}
+	else status |= 1;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Reallocation of conventionally aligned memory])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_realloc() can successfully resize a memory block
+ * which was originally allocated by malloc(), preserving * its content,
+ * but with neither alignment nor offset constraint.
+ */
+#include <malloc.h>
+#include <string.h>
+int main()
+{ size_t align, status = 0;
+  for( align = 1; 128 >= align; align <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size = 64;
+    if( (p1 = malloc( size )) != NULL )
+    { strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+      for( size = 128; 512 >= size; size <<= 1 )
+	if( (p3 = __mingw_realloc( p1, size )) != NULL )
+	{ p1 = p3; status |= p2 ? strcmp( p1, p2 ) : 2; }
+	else status |= 1;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Offset constrained rejection of size reduction])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_realloc() will decline to resize an originally
+ * offset-aligned memory block, when the requested size is insufficient
+ * to accommodate any data beyond the original offset; the returned
+ * pointer must be NULL, and errno must be set to EINVAL.
+ */
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+int main()
+{ size_t align, status = 0;
+  for( align = 1; 128 >= align; align <<= 1 )
+  { void *p1 = NULL, *p2 = NULL, *p3; size_t size = 128;
+    if( (p1 = __mingw_aligned_offset_malloc( size, align, 16)) != NULL )
+    { strcpy( p1, "Sample text." ); p2 = strdup( p1 );
+      for( size = 64; size > 0; size >>= 1 )
+	if( (p3 = __mingw_realloc( p1, size )) != NULL )
+	{ p1 = p3;
+	  status |= p2 ? strcmp( p1, p2 ) | (((size_t)(p1) + 16) % align) : 2;
+	  if( 16 >= size ) status |= 1;
+	}
+	else if( (p2 == NULL) || (errno != EINVAL) ) status |= 4;
+    }
+    __mingw_free(p1); __mingw_free(p2);
+  }
+  return status;
+}]]])dnl
+AT_CLEANUP
+
+AT_BANNER([Universal heap memory deallocation function checks.])
+#---------------------------------------------------------------
+# Implement a collection of tests to exercise the MinGW generic memory
+# deallocation function, to confirm that it can successfully return both
+# conventionally allocated and specifically aligned memory to the unused
+# heap memory pool; additionally, confirm that any of the reallocation
+# functions will perform a similar function, when allocation size is
+# reduced to zero.
+
+# MINGW_AT_SKIP_IF_NO_HEAPWALK
+# ----------------------------
+# Check that the host operating system supports Microsoft's _heapwalk()
+# API; (WinNT should do, but Win9x apparently doesn't).  For those which
+# don't, indicate that dependent tests should be skipped.
+#
+m4_define([MINGW_AT_SKIP_IF_NO_HEAPWALK],dnl)
+[ _HEAPINFO hmon = {NULL, 0, 0};
+  errno = 0; int status = _heapwalk( &hmon );
+  if( errno == ENOSYS ) return 77;dnl
+])# MINGW_AT_SKIP_IF_NO_HEAPWALK
+
+# MINGW_AT_VERIFY_ALIGNED_FREE( NAME, METHOD )
+# --------------------------------------------
+# Provide a function, try_NAME(), to walk the heap, identifying the base
+# address of the heap block, if any, which contains a specified (possibly
+# aligned) heap pointer; invoke METHOD on the specified pointer, then walk
+# the heap again, to confirm that the original base address either refers
+# to an unused heap region, or it no longer refers to any heap region,
+# in either used or unused state, which remains addressable.
+#
+m4_define([MINGW_AT_VERIFY_ALIGNED_FREE],[dnl
+int try_$1( void *ptr )
+{ _HEAPINFO hmon = {NULL, 0, 0};
+  if( ptr == NULL ) return 1;
+  while( _heapwalk( &hmon ) == _HEAPOK )
+  { uintptr_t base = (uintptr_t)(hmon._pentry);
+    if( ((uintptr_t)(ptr) >= base) && ((base + hmon._size) >= (uintptr_t)(ptr)) )
+    { if( hmon._useflag != _USEDENTRY ) return 1;
+      $2; ptr = hmon._pentry; hmon._pentry = NULL;
+      while( _heapwalk( &hmon ) == _HEAPOK )
+        if( hmon._pentry == ptr ) return (hmon._useflag == _FREEENTRY) ? 0 : 1;
+      return 0;
+    }
+  }
+  return 1;
+}
+])# MINGW_AT_VERIFY_ALIGNED_FREE
+
+AT_SETUP([Free conventionally aligned heap memory])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_free() can correctly identify a pointer which
+ * was NOT allocated by any MinGW aligned memory allocation function;
+ * when any such pointer can be mapped to a heap block which may have
+ * been allocated by malloc(), or realloc(), free it regardless, and
+ * confirm that it is successfully deallocated.
+ */
+#include <stdint.h>
+#include <malloc.h>
+#include <errno.h>
+]MINGW_AT_VERIFY_ALIGNED_FREE([free],[__mingw_free(ptr)])[
+int main()
+{ size_t alignment;
+ ]MINGW_AT_SKIP_IF_NO_HEAPWALK[
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { if( try_free(malloc(128)) ) return 99; }
+  return 0;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Free specifically aligned heap memory])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_free() can correctly identify a pointer which
+ * WAS allocated by a MinGW aligned memory allocation function; when
+ * any such pointer has been identified, free its underlying heap
+ * memory, and confirm that deallocation is successful.
+ */
+#include <stdint.h>
+#include <malloc.h>
+#include <errno.h>
+]MINGW_AT_VERIFY_ALIGNED_FREE([free],[__mingw_free(ptr)])[
+int main()
+{ size_t alignment;
+ ]MINGW_AT_SKIP_IF_NO_HEAPWALK[
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { if( try_free(__mingw_aligned_malloc(128, alignment)) ) return 99;
+    if( try_free(__mingw_aligned_offset_malloc(128, alignment, 5)) ) return 99;
+  }
+  return 0;
+}]]])dnl
+AT_CLEANUP
+
+AT_SETUP([Free by reallocation to zero size])dnl
+AT_KEYWORDS([C memalign])MINGW_AT_CHECK_RUN([[[
+/* Check that __mingw_realloc() can reproduce the effect of __mingw_free(),
+ * in respect of any heap memory allocation, when called with a requested size
+ * of zero; similarly, check that each of the __mingw_aligned_realloc(), and
+ * __mingw_aligned_offset_realloc() functions reproduce the same effect, when
+ * invoked to request resizing to zero, on blocks with correspondingly matched
+ * alignment, and offset arguments.
+ *
+ * Note that this is a Microsoft compatibility check; hopefully, no user
+ * will ever write code which depends on this anomalous behaviour.
+ */
+#include <stdint.h>
+#include <malloc.h>
+#include <errno.h>
+size_t alignment;
+]MINGW_AT_VERIFY_ALIGNED_FREE([realloc],dnl
+[__mingw_realloc(ptr,0)])[
+]MINGW_AT_VERIFY_ALIGNED_FREE([aligned_realloc],dnl
+[__mingw_aligned_realloc(ptr,0,alignment)])[
+]MINGW_AT_VERIFY_ALIGNED_FREE([offset_realloc],dnl
+[__mingw_aligned_offset_realloc(ptr,0,alignment,5)])[
+int main()
+{]MINGW_AT_SKIP_IF_NO_HEAPWALK[
+  for( alignment = 1; 128 >= alignment; alignment <<= 1 )
+  { if( try_realloc(__mingw_aligned_malloc(128, alignment)) )
+      return 99;
+    if( try_realloc(__mingw_aligned_offset_malloc(128, alignment, 5)) )
+      return 99;
+    if( try_offset_realloc(__mingw_aligned_offset_malloc(128, alignment, 5)) )
+      return 99;
+    if( try_aligned_realloc(__mingw_aligned_malloc(128, alignment)) )
+      return 99;
+    if( try_realloc(malloc(128)) )
+      return 99;
+  }
+  return 0;
+}]]])dnl
+AT_CLEANUP
+
+# vim: filetype=config formatoptions=croql
+# $RCSfile$: end of file
diff --git a/mingwrt/tests/testsuite.at.in b/mingwrt/tests/testsuite.at.in
--- a/mingwrt/tests/testsuite.at.in
+++ b/mingwrt/tests/testsuite.at.in
@@ -89,8 +89,9 @@ MINGW_AT_LANG([C])
 m4_include([headers.at])
 m4_include([ansiprintf.at])
 m4_include([logarithms.at])
 m4_include([powerfunc.at])
 m4_include([clockapi.at])
+m4_include([memalign.at])
 
 # vim: filetype=config formatoptions=croql
 # $RCSfile$: end of file
