diff mbox series

[fortran] Add random numbers and fix some bugs.

Message ID e06fbf42-356b-480f-b0e0-beede3919524@netcologne.de
State New
Headers show
Series [fortran] Add random numbers and fix some bugs. | expand

Commit Message

Thomas Koenig Sept. 18, 2024, 8:22 p.m. UTC
This patch adds random number support for UNSIGNED, plus fixes
two bugs, with array I/O where the type used to be set to BT_INTEGER,
and for division with the divisor being a constant.

Again, depends on prevous submissions.

OK for trunk?

gcc/fortran/ChangeLog:

	* check.cc (gfc_check_random_number): Adjust for unsigned.
	* iresolve.cc (gfc_resolve_random_number): Handle unsinged.
	* trans-expr.cc (gfc_conv_expr_op): Handle BT_UNSIGNED for divide.
	* trans-types.cc (gfc_get_dtype_rank_type): Handle BT_UNSIGNED.
	* gfortran.texi: Add RANDOM_NUMBER for UNSIGNED.

libgfortran/ChangeLog:

	* gfortran.map: Add _gfortran_random_m1, _gfortran_random_m2,
	_gfortran_random_m4, _gfortran_random_m8 and _gfortran_random_m16.
	* intrinsics/random.c (random_m1): New function.
	(random_m2): New function.
	(random_m4): New function.
	(random_m8): New function.
	(random_m16): New function.
	(arandom_m1): New function.
	(arandom_m2): New function.
	(arandom_m4): New function.
	(arandom_m8): New funciton.
	(arandom_m16): New function.

gcc/testsuite/ChangeLog:

	* gfortran.dg/unsigned_30.f90: New test.
---
  gcc/fortran/check.cc                      |  10 +-
  gcc/fortran/gfortran.texi                 |   1 +
  gcc/fortran/iresolve.cc                   |   6 +-
  gcc/fortran/trans-expr.cc                 |   4 +-
  gcc/fortran/trans-types.cc                |   7 +-
  gcc/testsuite/gfortran.dg/unsigned_30.f90 |  63 ++++
  libgfortran/gfortran.map                  |  10 +
  libgfortran/intrinsics/random.c           | 440 ++++++++++++++++++++++
  8 files changed, 534 insertions(+), 7 deletions(-)
  create mode 100644 gcc/testsuite/gfortran.dg/unsigned_30.f90

Comments

Andre Vehreschild Sept. 19, 2024, 10:16 a.m. UTC | #1
Hi Thomas,

submitting your patch as part of the mail got it corrupted by some mailer
adding line breaks. It does not apply for me. Because I can't test it, I have
more questions, see below:

On Wed, 18 Sep 2024 22:22:15 +0200
Thomas Koenig <tkoenig@netcologne.de> wrote:

> This patch adds random number support for UNSIGNED, plus fixes
> two bugs, with array I/O where the type used to be set to BT_INTEGER,
> and for division with the divisor being a constant.
>
> Again, depends on prevous submissions.
>
> OK for trunk?
>
> gcc/fortran/ChangeLog:
>
> 	* check.cc (gfc_check_random_number): Adjust for unsigned.
> 	* iresolve.cc (gfc_resolve_random_number): Handle unsinged.

Hihi, I do this typo, too, over and over again: s/unsinged/unsigned/

> 	* trans-expr.cc (gfc_conv_expr_op): Handle BT_UNSIGNED for divide.
> 	* trans-types.cc (gfc_get_dtype_rank_type): Handle BT_UNSIGNED.
> 	* gfortran.texi: Add RANDOM_NUMBER for UNSIGNED.
>

<snip>

> diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
> index 533c9d7d343..1851cfb8d4a 100644
> --- a/gcc/fortran/check.cc
> +++ b/gcc/fortran/check.cc
> @@ -7007,8 +7007,14 @@ gfc_check_random_init (gfc_expr *repeatable,
> gfc_expr *image_distinct)
>   bool
>   gfc_check_random_number (gfc_expr *harvest)
>   {
> -  if (!type_check (harvest, 0, BT_REAL))
> -    return false;
> +  if (flag_unsigned)
> +    {
> +      if (!type_check2 (harvest, 0, BT_REAL, BT_UNSIGNED))
> +	return false;

When the second argument is a BT_INTEGER, does this fail here?

> +    }
> +  else
> +    if (!type_check (harvest, 0, BT_REAL))
> +      return false;
>
>     if (!variable_check (harvest, 0, false))
>       return false;

<snip>

Regards,
	Andre
--
Andre Vehreschild * Email: vehre ad gmx dot de
diff mbox series

Patch

diff --git a/gcc/fortran/check.cc b/gcc/fortran/check.cc
index 533c9d7d343..1851cfb8d4a 100644
--- a/gcc/fortran/check.cc
+++ b/gcc/fortran/check.cc
@@ -7007,8 +7007,14 @@  gfc_check_random_init (gfc_expr *repeatable, 
gfc_expr *image_distinct)
  bool
  gfc_check_random_number (gfc_expr *harvest)
  {
-  if (!type_check (harvest, 0, BT_REAL))
-    return false;
+  if (flag_unsigned)
+    {
+      if (!type_check2 (harvest, 0, BT_REAL, BT_UNSIGNED))
+	return false;
+    }
+  else
+    if (!type_check (harvest, 0, BT_REAL))
+      return false;

    if (!variable_check (harvest, 0, false))
      return false;
diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 3eb8039c09f..a5ebadff3bb 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -2790,6 +2790,7 @@  As of now, the following intrinsics take unsigned 
arguments:
  @item @code{TRANSFER}
  @item @code{SUM}, @code{PRODUCT}, @code{MATMUL} and @code{DOT_PRODUCT}
  @item @code{IANY}, @code{IALL} and @code{IPARITY}
+@item @code{RANDOM_NUMBER}.
  @end itemize
  This list will grow in the near future.
  @c ---------------------------------------------------------------------
diff --git a/gcc/fortran/iresolve.cc b/gcc/fortran/iresolve.cc
index 58a1821ef10..a814c9279cf 100644
--- a/gcc/fortran/iresolve.cc
+++ b/gcc/fortran/iresolve.cc
@@ -3452,12 +3452,14 @@  gfc_resolve_random_number (gfc_code *c)
  {
    const char *name;
    int kind;
+  char type;

    kind = gfc_type_abi_kind (&c->ext.actual->expr->ts);
+  type = gfc_type_letter (c->ext.actual->expr->ts.type);
    if (c->ext.actual->expr->rank == 0)
-    name = gfc_get_string (PREFIX ("random_r%d"), kind);
+    name = gfc_get_string (PREFIX ("random_%c%d"), type, kind);
    else
-    name = gfc_get_string (PREFIX ("arandom_r%d"), kind);
+    name = gfc_get_string (PREFIX ("arandom_%c%d"), type, kind);

    c->resolved_sym = gfc_get_intrinsic_sub_symbol (name);
  }
diff --git a/gcc/fortran/trans-expr.cc b/gcc/fortran/trans-expr.cc
index f1dfac4a2be..b39b4450997 100644
--- a/gcc/fortran/trans-expr.cc
+++ b/gcc/fortran/trans-expr.cc
@@ -3982,9 +3982,9 @@  gfc_conv_expr_op (gfc_se * se, gfc_expr * expr)

      case INTRINSIC_DIVIDE:
        /* If expr is a real or complex expr, use an RDIV_EXPR. If op1 is
-         an integer, we must round towards zero, so we use a
+	 an integer or unsigned, we must round towards zero, so we use a
           TRUNC_DIV_EXPR.  */
-      if (expr->ts.type == BT_INTEGER)
+      if (expr->ts.type == BT_INTEGER || expr->ts.type == BT_UNSIGNED)
  	code = TRUNC_DIV_EXPR;
        else
  	code = RDIV_EXPR;
diff --git a/gcc/fortran/trans-types.cc b/gcc/fortran/trans-types.cc
index 3a1ff98b33c..ce7d3027f1b 100644
--- a/gcc/fortran/trans-types.cc
+++ b/gcc/fortran/trans-types.cc
@@ -1651,7 +1651,12 @@  gfc_get_dtype_rank_type (int rank, tree etype)
  	  && TYPE_STRING_FLAG (ptype))
  	n = BT_CHARACTER;
        else
-	n = BT_INTEGER;
+	{
+	  if (TYPE_UNSIGNED (etype))
+	    n = BT_UNSIGNED;
+	  else
+	    n = BT_INTEGER;
+	}
        break;

      case BOOLEAN_TYPE:
diff --git a/gcc/testsuite/gfortran.dg/unsigned_30.f90 
b/gcc/testsuite/gfortran.dg/unsigned_30.f90
new file mode 100644
index 00000000000..b0a15552f1f
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/unsigned_30.f90
@@ -0,0 +1,63 @@ 
+! { dg-do run }
+! { dg-options "-funsigned" }
+
+! The leading bytes of the unsigned sequences should be the same for
+! kinds 1 to 8.  This also tests array I/O for unsigneds.
+
+program memain
+  implicit none
+  integer, dimension(:), allocatable :: seed
+  integer :: n
+  call random_seed (size=n)
+  allocate(seed(n))
+  call test1
+  call test2
+contains
+  subroutine test1
+    unsigned(1) :: u1
+    unsigned(2) :: u2
+    unsigned(4) :: u4
+    unsigned(8) :: u8
+    character (len=16) :: line1, line2, line4, line8
+    integer :: i, n
+    do i=1,10
+       call random_seed(get=seed)
+       call random_number(u1)
+       write (line1,'(Z2.2)') u1
+       call random_seed(put=seed)
+       call random_number(u2)
+       write (line2,'(Z4.4)') u2
+       call random_seed(put=seed)
+       call random_number(u4)
+       write (line4,'(Z8.8)') u4
+       call random_seed(put=seed)
+       call random_number(u8)
+       write (line8,'(Z16.16)') u8
+       if (line8(1:8) /= line4 (1:8)) error stop 1
+       if (line4(1:4) /= line2 (1:4)) error stop 2
+       if (line2(1:2) /= line1 (1:2)) error stop 3
+    end do
+  end subroutine test1
+  subroutine test2
+    unsigned(1), dimension(2,2) :: v1
+    unsigned(2), dimension(2,2) :: v2
+    unsigned(4), dimension(2,2) :: v4
+    unsigned(8), dimension(2,2) :: v8
+    character(len=16), dimension(4) :: c1, c2, c4, c8
+    call random_seed(put=seed)
+    call random_number (v1)
+    write (c1,'(Z2.2)') v1
+    call random_seed(put=seed)
+    call random_number (v2)
+    write (c2,'(Z4.4)') v2
+    call random_seed(put=seed)
+    call random_number (v4)
+    write (c4,'(Z8.8)') v4
+    call random_seed(put=seed)
+    call random_number (v8)
+    write (c8,'(Z16.16)') v8
+    if (any(c8(:)(1:8) /= c4(:)(1:8))) error stop 10
+    if (any(c4(:)(1:4) /= c2(:)(1:4))) error stop 11
+    if (any(c2(:)(1:2) /= c1(:)(1:2))) error stop 12
+  end subroutine test2
+end program memain
diff --git a/libgfortran/gfortran.map b/libgfortran/gfortran.map
index e71cbcf2376..6c42b167430 100644
--- a/libgfortran/gfortran.map
+++ b/libgfortran/gfortran.map
@@ -1777,4 +1777,14 @@  GFORTRAN_15 {
      _gfortran_internal_unpack_class;
      _gfortran_transfer_unsigned;
      _gfortran_transfer_unsigned_write;
+    _gfortran_random_m1;
+    _gfortran_random_m2;
+    _gfortran_random_m4;
+    _gfortran_random_m8;
+    _gfortran_random_m16;
+    _gfortran_arandom_m1;
+    _gfortran_arandom_m2;
+    _gfortran_arandom_m4;
+    _gfortran_arandom_m8;
+    _gfortran_arandom_m16;
  } GFORTRAN_14;
diff --git a/libgfortran/intrinsics/random.c 
b/libgfortran/intrinsics/random.c
index 93de41dc3b3..9922224b687 100644
--- a/libgfortran/intrinsics/random.c
+++ b/libgfortran/intrinsics/random.c
@@ -89,6 +89,43 @@  export_proto(arandom_r17);

  #endif

+extern void random_m1 (GFC_UINTEGER_1 *);
+export_proto (random_m1);
+
+extern void random_m2 (GFC_UINTEGER_2 *);
+export_proto (random_m2);
+
+extern void random_m4 (GFC_UINTEGER_4 *);
+export_proto (random_m4);
+
+extern void random_m8 (GFC_UINTEGER_8 *);
+export_proto (random_m8);
+
+#ifdef  HAVE_GFC_UINTEGER_16
+extern void random_m16 (GFC_UINTEGER_16 *);
+export_proto (random_m16);
+
+#endif
+
+extern void arandom_m1 (gfc_array_m1 *);
+export_proto (arandom_m1);
+
+extern void arandom_m2 (gfc_array_m2 *);
+export_proto (arandom_m2);
+
+extern void arandom_m4 (gfc_array_m4 *);
+export_proto (arandom_m4);
+
+extern void arandom_m8 (gfc_array_m8 *);
+export_proto (arandom_m8);
+
+#ifdef HAVE_GFC_UINTEGER_16
+
+extern void arandom_m16 (gfc_array_m16 *);
+export_proto (arandom_m16);
+
+#endif
+
  #ifdef __GTHREAD_MUTEX_INIT
  static __gthread_mutex_t random_lock = __GTHREAD_MUTEX_INIT;
  #else
@@ -498,6 +535,81 @@  iexport(random_r17);

  #endif

+/* Versions for unsigned numbers.  */
+
+/* Returns a random byte.  */
+
+void
+random_m1 (GFC_UINTEGER_1 *x)
+{
+  prng_state* rs = get_rand_state();
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+  GFC_UINTEGER_8 r = prng_next (rs);
+
+  *x = r >> 56;
+}
+
+/* A random 16-bit number.  */
+
+void
+random_m2 (GFC_UINTEGER_2 *x)
+{
+  prng_state* rs = get_rand_state();
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+  GFC_UINTEGER_8 r = prng_next (rs);
+
+  *x = r >> 48;
+}
+
+/* A random 32-bit number.  */
+
+void
+random_m4 (GFC_UINTEGER_4 *x)
+{
+  prng_state* rs = get_rand_state();
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+  GFC_UINTEGER_8 r = prng_next (rs);
+
+  *x = r >> 32;
+}
+
+/* A random 64-bit number.  */
+
+void
+random_m8 (GFC_UINTEGER_8 *x)
+{
+  prng_state* rs = get_rand_state();
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+  GFC_UINTEGER_8 r = prng_next (rs);
+
+  *x = r;
+}
+
+/* ... and a random 128-bit number, if we have the type.  */
+
+#ifdef HAVE_GFC_UINTEGER_16
+void
+random_m16 (GFC_UINTEGER_16 *x)
+{
+  prng_state* rs = get_rand_state();
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+  GFC_UINTEGER_8 r1 = prng_next (rs);
+  GFC_UINTEGER_8 r2 = prng_next (rs);
+
+  *x = (((GFC_UINTEGER_16) r1) << 64) | (GFC_UINTEGER_16) r2;
+}
+#endif
+
  /*  This function fills a REAL(4) array with values from the uniform
      distribution with range [0,1).  */

@@ -843,6 +955,334 @@  arandom_r17 (gfc_array_r17 *x)

  #endif

+/* Fill an unsigned array with random bytes.  */
+
+void
+arandom_m1 (gfc_array_m1 *x)
+{
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
+  index_type stride[GFC_MAX_DIMENSIONS];
+  index_type stride0;
+  index_type dim;
+  GFC_UINTEGER_1 *dest;
+  prng_state* rs = get_rand_state();
+
+  dest = x->base_addr;
+
+  dim = GFC_DESCRIPTOR_RANK (x);
+
+  for (index_type n = 0; n < dim; n++)
+    {
+      count[n] = 0;
+      stride[n] = GFC_DESCRIPTOR_STRIDE(x,n);
+      extent[n] = GFC_DESCRIPTOR_EXTENT(x,n);
+      if (extent[n] <= 0)
+	return;
+    }
+
+  stride0 = stride[0];
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+
+  while (dest)
+    {
+      /* random_m1 (dest);  */
+      uint64_t r = prng_next (rs);
+      *dest = r >> 56;
+
+      /* Advance to the next element.  */
+      dest += stride0;
+      count[0]++;
+      /* Advance to the next source element.  */
+      index_type n = 0;
+      while (count[n] == extent[n])
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  /* We could precalculate these products, but this is a less
+	     frequently used path so probably not worth it.  */
+	  dest -= stride[n] * extent[n];
+	  n++;
+	  if (n == dim)
+	    {
+	      dest = NULL;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      dest += stride[n];
+	    }
+	}
+    }
+}
+
+/* Fill an unsigned array with random 16-bit unsigneds.  */
+
+void
+arandom_m2 (gfc_array_m2 *x)
+{
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
+  index_type stride[GFC_MAX_DIMENSIONS];
+  index_type stride0;
+  index_type dim;
+  GFC_UINTEGER_2 *dest;
+  prng_state* rs = get_rand_state();
+
+  dest = x->base_addr;
+
+  dim = GFC_DESCRIPTOR_RANK (x);
+
+  for (index_type n = 0; n < dim; n++)
+    {
+      count[n] = 0;
+      stride[n] = GFC_DESCRIPTOR_STRIDE(x,n);
+      extent[n] = GFC_DESCRIPTOR_EXTENT(x,n);
+      if (extent[n] <= 0)
+	return;
+    }
+
+  stride0 = stride[0];
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+
+  while (dest)
+    {
+      /* random_m1 (dest);  */
+      uint64_t r = prng_next (rs);
+      *dest = r >> 48;
+
+      /* Advance to the next element.  */
+      dest += stride0;
+      count[0]++;
+      /* Advance to the next source element.  */
+      index_type n = 0;
+      while (count[n] == extent[n])
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  /* We could precalculate these products, but this is a less
+	     frequently used path so probably not worth it.  */
+	  dest -= stride[n] * extent[n];
+	  n++;
+	  if (n == dim)
+	    {
+	      dest = NULL;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      dest += stride[n];
+	    }
+	}
+    }
+}
+
+/* Fill an array with random 32-bit unsigneds.  */
+
+void
+arandom_m4 (gfc_array_m4 *x)
+{
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
+  index_type stride[GFC_MAX_DIMENSIONS];
+  index_type stride0;
+  index_type dim;
+  GFC_UINTEGER_4 *dest;
+  prng_state* rs = get_rand_state();
+
+  dest = x->base_addr;
+
+  dim = GFC_DESCRIPTOR_RANK (x);
+
+  for (index_type n = 0; n < dim; n++)
+    {
+      count[n] = 0;
+      stride[n] = GFC_DESCRIPTOR_STRIDE(x,n);
+      extent[n] = GFC_DESCRIPTOR_EXTENT(x,n);
+      if (extent[n] <= 0)
+	return;
+    }
+
+  stride0 = stride[0];
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+
+  while (dest)
+    {
+      /* random_m4 (dest);  */
+      uint64_t r = prng_next (rs);
+      *dest = r >> 32;
+
+      /* Advance to the next element.  */
+      dest += stride0;
+      count[0]++;
+      /* Advance to the next source element.  */
+      index_type n = 0;
+      while (count[n] == extent[n])
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  /* We could precalculate these products, but this is a less
+	     frequently used path so probably not worth it.  */
+	  dest -= stride[n] * extent[n];
+	  n++;
+	  if (n == dim)
+	    {
+	      dest = NULL;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      dest += stride[n];
+	    }
+	}
+    }
+}
+
+/* Fill an array with random 64-bit unsigneds.  */
+
+void
+arandom_m8 (gfc_array_m8 *x)
+{
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
+  index_type stride[GFC_MAX_DIMENSIONS];
+  index_type stride0;
+  index_type dim;
+  GFC_UINTEGER_8 *dest;
+  prng_state* rs = get_rand_state();
+
+  dest = x->base_addr;
+
+  dim = GFC_DESCRIPTOR_RANK (x);
+
+  for (index_type n = 0; n < dim; n++)
+    {
+      count[n] = 0;
+      stride[n] = GFC_DESCRIPTOR_STRIDE(x,n);
+      extent[n] = GFC_DESCRIPTOR_EXTENT(x,n);
+      if (extent[n] <= 0)
+	return;
+    }
+
+  stride0 = stride[0];
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+
+  while (dest)
+    {
+      /* random_m8 (dest);  */
+      uint64_t r = prng_next (rs);
+      *dest = r;
+
+      /* Advance to the next element.  */
+      dest += stride0;
+      count[0]++;
+      /* Advance to the next source element.  */
+      index_type n = 0;
+      while (count[n] == extent[n])
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  /* We could precalculate these products, but this is a less
+	     frequently used path so probably not worth it.  */
+	  dest -= stride[n] * extent[n];
+	  n++;
+	  if (n == dim)
+	    {
+	      dest = NULL;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      dest += stride[n];
+	    }
+	}
+    }
+}
+
+#ifdef GFC_HAVE_GFC_UINTEGER_16
+
+/* Fill an unsigned array with random bytes.  */
+
+void
+arandom_m16 (gfc_array_m16 *x)
+{
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
+  index_type stride[GFC_MAX_DIMENSIONS];
+  index_type stride0;
+  index_type dim;
+  GFC_UINTEGER_16 *dest;
+  prng_state* rs = get_rand_state();
+
+  dest = x->base_addr;
+
+  dim = GFC_DESCRIPTOR_RANK (x);
+
+  for (index_type n = 0; n < dim; n++)
+    {
+      count[n] = 0;
+      stride[n] = GFC_DESCRIPTOR_STRIDE(x,n);
+      extent[n] = GFC_DESCRIPTOR_EXTENT(x,n);
+      if (extent[n] <= 0)
+	return;
+    }
+
+  stride0 = stride[0];
+
+  if (unlikely (!rs->init))
+    init_rand_state (rs, false);
+
+  while (dest)
+    {
+      /* random_m16 (dest);  */
+      uint64_t r1 = prng_next (rs), r2 = prng_next (rs);
+      *dest = (((GFC_UINTEGER_16) r1) << 64) | (GFC_UINTEGER_16) r2;
+
+      /* Advance to the next element.  */
+      dest += stride0;
+      count[0]++;
+      /* Advance to the next source element.  */
+      index_type n = 0;
+      while (count[n] == extent[n])
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  /* We could precalculate these products, but this is a less
+	     frequently used path so probably not worth it.  */
+	  dest -= stride[n] * extent[n];
+	  n++;
+	  if (n == dim)
+	    {
+	      dest = NULL;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      dest += stride[n];
+	    }
+	}
+    }
+}
+
+#endif

  /* Number of elements in master_state array.  */
  #define SZU64 (sizeof (master_state.s) / sizeof (uint64_t))