diff gcc/fortran/simplify.c @ 131:84e7813d76e9

gcc-8.2
author mir3636
date Thu, 25 Oct 2018 07:37:49 +0900
parents 04ced10e8804
children 1830386684a0
line wrap: on
line diff
--- a/gcc/fortran/simplify.c	Fri Oct 27 22:46:09 2017 +0900
+++ b/gcc/fortran/simplify.c	Thu Oct 25 07:37:49 2018 +0900
@@ -1,5 +1,5 @@
 /* Simplify intrinsic functions at compile-time.
-   Copyright (C) 2000-2017 Free Software Foundation, Inc.
+   Copyright (C) 2000-2018 Free Software Foundation, Inc.
    Contributed by Andy Vaught & Katherine Holcomb
 
 This file is part of GCC.
@@ -25,10 +25,14 @@
 #include "gfortran.h"
 #include "arith.h"
 #include "intrinsic.h"
+#include "match.h"
 #include "target-memory.h"
 #include "constructor.h"
 #include "version.h"	/* For version_string.  */
 
+/* Prototypes.  */
+
+static int min_max_choose (gfc_expr *, gfc_expr *, int, bool back_val = false);
 
 gfc_expr gfc_bad_expr;
 
@@ -227,7 +231,8 @@
 }
 
 
-/* Test that the expression is an constant array.  */
+/* Test that the expression is a constant array, simplifying if
+   we are dealing with a parameter array.  */
 
 static bool
 is_constant_array_expr (gfc_expr *e)
@@ -237,6 +242,10 @@
   if (e == NULL)
     return true;
 
+  if (e->expr_type == EXPR_VARIABLE && e->rank > 0
+      && e->symtree->n.sym->attr.flavor == FL_PARAMETER)
+    gfc_simplify_expr (e, 1);
+
   if (e->expr_type != EXPR_ARRAY || !gfc_is_constant_expr (e))
     return false;
 
@@ -249,6 +258,31 @@
   return true;
 }
 
+/* Test for a size zero array.  */
+bool
+gfc_is_size_zero_array (gfc_expr *array)
+{
+
+  if (array->rank == 0)
+    return false;
+
+  if (array->expr_type == EXPR_VARIABLE && array->rank > 0
+      && array->symtree->n.sym->attr.flavor == FL_PARAMETER
+      && array->shape != NULL)
+    {
+      for (int i = 0; i < array->rank; i++)
+	if (mpz_cmp_si (array->shape[i], 0) <= 0)
+	  return true;
+
+      return false;
+    }
+
+  if (array->expr_type == EXPR_ARRAY)
+    return array->value.constructor == NULL;
+
+  return false;
+}
+
 
 /* Initialize a transformational result expression with a given value.  */
 
@@ -267,7 +301,7 @@
   else if (e && e->expr_type == EXPR_CONSTANT)
     {
       int i = gfc_validate_kind (e->ts.type, e->ts.kind, false);
-      int length;
+      HOST_WIDE_INT length;
       gfc_char_t *string;
 
       switch (e->ts.type)
@@ -305,14 +339,14 @@
 	    if (init == INT_MIN)
 	      {
 		gfc_expr *len = gfc_simplify_len (array, NULL);
-		gfc_extract_int (len, &length);
+		gfc_extract_hwi (len, &length);
 		string = gfc_get_wide_string (length + 1);
 		gfc_wide_memset (string, 0, length);
 	      }
 	    else if (init == INT_MAX)
 	      {
 		gfc_expr *len = gfc_simplify_len (array, NULL);
-		gfc_extract_int (len, &length);
+		gfc_extract_hwi (len, &length);
 		string = gfc_get_wide_string (length + 1);
 		gfc_wide_memset (string, 255, length);
 	      }
@@ -346,9 +380,14 @@
 {
   gfc_expr *result, *a, *b, *c;
 
-  result = gfc_get_constant_expr (matrix_a->ts.type, matrix_a->ts.kind,
-				  &matrix_a->where);
-  init_result_expr (result, 0, NULL);
+  /* Set result to an INTEGER(1) 0 for numeric types and .false. for
+     LOGICAL.  Mixed-mode math in the loop will promote result to the
+     correct type and kind.  */
+  if (matrix_a->ts.type == BT_LOGICAL)
+    result = gfc_get_logical_expr (gfc_default_logical_kind, NULL, false);
+  else
+    result = gfc_get_int_expr (1, NULL, 0);
+  result->where = matrix_a->where;
 
   a = gfc_constructor_lookup_expr (matrix_a->value.constructor, offset_a);
   b = gfc_constructor_lookup_expr (matrix_b->value.constructor, offset_b);
@@ -431,7 +470,8 @@
    Interface and implementation mimics arith functions as
    gfc_add, gfc_multiply, etc.  */
 
-static gfc_expr* gfc_count (gfc_expr *op1, gfc_expr *op2)
+static gfc_expr *
+gfc_count (gfc_expr *op1, gfc_expr *op2)
 {
   gfc_expr *result;
 
@@ -587,7 +627,7 @@
       n += 1;
     }
 
-  done = false;
+  done = resultsize <= 0;
   base = arrayvec;
   dest = resultvec;
   while (!done)
@@ -649,8 +689,11 @@
 			 int init_val, transformational_op op)
 {
   gfc_expr *result;
-
-  if (!is_constant_array_expr (array)
+  bool size_zero;
+
+  size_zero = gfc_is_size_zero_array (array);
+
+  if (!(is_constant_array_expr (array) || size_zero)
       || !gfc_is_constant_expr (dim))
     return NULL;
 
@@ -661,7 +704,10 @@
 
   result = transformational_result (array, dim, array->ts.type,
 				    array->ts.kind, &array->where);
-  init_result_expr (result, init_val, NULL);
+  init_result_expr (result, init_val, array);
+
+  if (size_zero)
+    return result;
 
   return !dim || array->rank == 1 ?
     simplify_transformation_to_scalar (result, array, mask, op) :
@@ -1920,8 +1966,11 @@
 gfc_simplify_count (gfc_expr *mask, gfc_expr *dim, gfc_expr *kind)
 {
   gfc_expr *result;
-
-  if (!is_constant_array_expr (mask)
+  bool size_zero;
+
+  size_zero = gfc_is_size_zero_array (mask);
+
+  if (!(is_constant_array_expr (mask) || size_zero)
       || !gfc_is_constant_expr (dim)
       || !gfc_is_constant_expr (kind))
     return NULL;
@@ -1934,6 +1983,9 @@
 
   init_result_expr (result, 0, NULL);
 
+  if (size_zero)
+    return result;
+
   /* Passing MASK twice, once as data array, once as mask.
      Whenever gfc_count is called, '1' is added to the result.  */
   return !dim || mask->rank == 1 ?
@@ -1941,92 +1993,214 @@
     simplify_transformation_to_array (result, mask, dim, mask, gfc_count, NULL);
 }
 
+/* Simplification routine for cshift. This works by copying the array
+   expressions into a one-dimensional array, shuffling the values into another
+   one-dimensional array and creating the new array expression from this.  The
+   shuffling part is basically taken from the library routine.  */
 
 gfc_expr *
 gfc_simplify_cshift (gfc_expr *array, gfc_expr *shift, gfc_expr *dim)
 {
-  gfc_expr *a, *result;
-  int dm;
-
-  /* DIM is only useful for rank > 1, but deal with it here as one can
-     set DIM = 1 for rank = 1.  */
+  gfc_expr *result;
+  int which;
+  gfc_expr **arrayvec, **resultvec;
+  gfc_expr **rptr, **sptr;
+  mpz_t size;
+  size_t arraysize, shiftsize, i;
+  gfc_constructor *array_ctor, *shift_ctor;
+  ssize_t *shiftvec, *hptr;
+  ssize_t shift_val, len;
+  ssize_t count[GFC_MAX_DIMENSIONS], extent[GFC_MAX_DIMENSIONS],
+    hs_ex[GFC_MAX_DIMENSIONS + 1],
+    hstride[GFC_MAX_DIMENSIONS], sstride[GFC_MAX_DIMENSIONS],
+    a_extent[GFC_MAX_DIMENSIONS], a_stride[GFC_MAX_DIMENSIONS],
+    h_extent[GFC_MAX_DIMENSIONS],
+    ss_ex[GFC_MAX_DIMENSIONS + 1];
+  ssize_t rsoffset;
+  int d, n;
+  bool continue_loop;
+  gfc_expr **src, **dest;
+
+  if (!is_constant_array_expr (array))
+    return NULL;
+
+  if (shift->rank > 0)
+    gfc_simplify_expr (shift, 1);
+
+  if (!gfc_is_constant_expr (shift))
+    return NULL;
+
+  /* Make dim zero-based.  */
   if (dim)
     {
       if (!gfc_is_constant_expr (dim))
 	return NULL;
-      dm = mpz_get_si (dim->value.integer);
+      which = mpz_get_si (dim->value.integer) - 1;
+    }
+  else
+    which = 0;
+
+  gfc_array_size (array, &size);
+  arraysize = mpz_get_ui (size);
+  mpz_clear (size);
+
+  result = gfc_get_array_expr (array->ts.type, array->ts.kind, &array->where);
+  result->shape = gfc_copy_shape (array->shape, array->rank);
+  result->rank = array->rank;
+  result->ts.u.derived = array->ts.u.derived;
+
+  if (arraysize == 0)
+    return result;
+
+  arrayvec = XCNEWVEC (gfc_expr *, arraysize);
+  array_ctor = gfc_constructor_first (array->value.constructor);
+  for (i = 0; i < arraysize; i++)
+    {
+      arrayvec[i] = array_ctor->expr;
+      array_ctor = gfc_constructor_next (array_ctor);
+    }
+
+  resultvec = XCNEWVEC (gfc_expr *, arraysize);
+
+  extent[0] = 1;
+  count[0] = 0;
+
+  for (d=0; d < array->rank; d++)
+    {
+      a_extent[d] = mpz_get_si (array->shape[d]);
+      a_stride[d] = d == 0 ? 1 : a_stride[d-1] * a_extent[d-1];
+    }
+
+  if (shift->rank > 0)
+    {
+      gfc_array_size (shift, &size);
+      shiftsize = mpz_get_ui (size);
+      mpz_clear (size);
+      shiftvec = XCNEWVEC (ssize_t, shiftsize);
+      shift_ctor = gfc_constructor_first (shift->value.constructor);
+      for (d = 0; d < shift->rank; d++)
+	{
+	  h_extent[d] = mpz_get_si (shift->shape[d]);
+	  hstride[d] = d == 0 ? 1 : hstride[d-1] * h_extent[d-1];
+	}
     }
   else
-    dm = 1;
-
-  /* Copy array into 'a', simplify it, and then test for a constant array.  */
-  a = gfc_copy_expr (array);
-  gfc_simplify_expr (a, 0);
-  if (!is_constant_array_expr (a))
-    {
-      gfc_free_expr (a);
-      return NULL;
-    }
-
-  if (a->rank == 1)
-    {
-      gfc_constructor *ca, *cr;
-      mpz_t size;
-      int i, j, shft, sz;
-
-      if (!gfc_is_constant_expr (shift))
+    shiftvec = NULL;
+
+  /* Shut up compiler */
+  len = 1;
+  rsoffset = 1;
+
+  n = 0;
+  for (d=0; d < array->rank; d++)
+    {
+      if (d == which)
 	{
-	  gfc_free_expr (a);
-	  return NULL;
+	  rsoffset = a_stride[d];
+	  len = a_extent[d];
 	}
-
-      shft = mpz_get_si (shift->value.integer);
-
-      /*  Case (i):  If ARRAY has rank one, element i of the result is
-	  ARRAY (1 + MODULO (i + SHIFT - 1, SIZE (ARRAY))).  */
-
-      mpz_init (size);
-      gfc_array_size (a, &size);
-      sz = mpz_get_si (size);
-      mpz_clear (size);
-
-      /* Adjust shft to deal with right or left shifts. */
-      shft = shft < 0 ? 1 - shft : shft;
-
-      /* Special case: Shift to the original order!  */
-      if (sz == 0 || shft % sz == 0)
-	return a;
-
-      result = gfc_copy_expr (a);
-      cr = gfc_constructor_first (result->value.constructor);
-      for (i = 0; i < sz; i++, cr = gfc_constructor_next (cr))
+      else
 	{
-	  j = (i + shft) % sz;
-	  ca = gfc_constructor_first (a->value.constructor);
-	  while (j-- > 0)
-	    ca = gfc_constructor_next (ca);
-	  cr->expr = gfc_copy_expr (ca->expr);
+	  count[n] = 0;
+	  extent[n] = a_extent[d];
+	  sstride[n] = a_stride[d];
+	  ss_ex[n] = sstride[n] * extent[n];
+	  if (shiftvec)
+	    hs_ex[n] = hstride[n] * extent[n];
+	  n++;
 	}
-
-      gfc_free_expr (a);
-      return result;
+    }
+  ss_ex[n] = 0;
+  hs_ex[n] = 0;
+
+  if (shiftvec)
+    {
+      for (i = 0; i < shiftsize; i++)
+	{
+	  ssize_t val;
+	  val = mpz_get_si (shift_ctor->expr->value.integer);
+	  val = val % len;
+	  if (val < 0)
+	    val += len;
+	  shiftvec[i] = val;
+	  shift_ctor = gfc_constructor_next (shift_ctor);
+	}
+      shift_val = 0;
     }
   else
     {
-      /* FIXME: Deal with rank > 1 arrays.  For now, don't leak memory.  */
-
-      /* GCC bootstrap is too stupid to realize that the above code for dm
-	 is correct.  First, dim can be specified for a rank 1 array.  It is
-	 not needed in this nor used here.  Second, the code is simply waiting
-	 for someone to implement rank > 1 simplification.   For now, add a
-	 pessimization to the code that has a zero valid reason to be here.  */
-      if (dm > array->rank)
-	gcc_unreachable ();
-
-      gfc_free_expr (a);
-    }
-
-  return NULL;
+      shift_val = mpz_get_si (shift->value.integer);
+      shift_val = shift_val % len;
+      if (shift_val < 0)
+	shift_val += len;
+    }
+
+  continue_loop = true;
+  d = array->rank;
+  rptr = resultvec;
+  sptr = arrayvec;
+  hptr = shiftvec;
+
+  while (continue_loop)
+    {
+      ssize_t sh;
+      if (shiftvec)
+	sh = *hptr;
+      else
+	sh = shift_val;
+
+      src = &sptr[sh * rsoffset];
+      dest = rptr;
+      for (n = 0; n < len - sh; n++)
+	{
+	  *dest = *src;
+	  dest += rsoffset;
+	  src += rsoffset;
+	}
+      src = sptr;
+      for ( n = 0; n < sh; n++)
+	{
+	  *dest = *src;
+	  dest += rsoffset;
+	  src += rsoffset;
+	}
+      rptr += sstride[0];
+      sptr += sstride[0];
+      if (shiftvec)
+	hptr += hstride[0];
+      count[0]++;
+      n = 0;
+      while (count[n] == extent[n])
+	{
+	  count[n] = 0;
+	  rptr -= ss_ex[n];
+	  sptr -= ss_ex[n];
+	  if (shiftvec)
+	    hptr -= hs_ex[n];
+	  n++;
+	  if (n >= d - 1)
+	    {
+	      continue_loop = false;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      rptr += sstride[n];
+	      sptr += sstride[n];
+	      if (shiftvec)
+		hptr += hstride[n];
+	    }
+	}
+    }
+
+  for (i = 0; i < arraysize; i++)
+    {
+      gfc_constructor_append_expr (&result->value.constructor,
+				   gfc_copy_expr (resultvec[i]),
+				   NULL);
+    }
+  return result;
 }
 
 
@@ -2124,23 +2298,20 @@
 gfc_expr*
 gfc_simplify_dot_product (gfc_expr *vector_a, gfc_expr *vector_b)
 {
-
-  gfc_expr temp;
+  /* If vector_a is a zero-sized array, the result is 0 for INTEGER,
+     REAL, and COMPLEX types and .false. for LOGICAL.  */
+  if (vector_a->shape && mpz_get_si (vector_a->shape[0]) == 0)
+    {
+      if (vector_a->ts.type == BT_LOGICAL)
+	return gfc_get_logical_expr (gfc_default_logical_kind, NULL, false);
+      else
+	return gfc_get_int_expr (gfc_default_integer_kind, NULL, 0);
+    }
 
   if (!is_constant_array_expr (vector_a)
       || !is_constant_array_expr (vector_b))
     return NULL;
 
-  gcc_assert (vector_a->rank == 1);
-  gcc_assert (vector_b->rank == 1);
-
-  temp.expr_type = EXPR_OP;
-  gfc_clear_ts (&temp.ts);
-  temp.value.op.op = INTRINSIC_NONE;
-  temp.value.op.op1 = vector_a;
-  temp.value.op.op2 = vector_b;
-  gfc_type_convert_binary (&temp, 1);
-
   return compute_dot_product (vector_a, 1, 0, vector_b, 1, 0, true);
 }
 
@@ -2219,6 +2390,272 @@
 
 
 gfc_expr *
+gfc_simplify_eoshift (gfc_expr *array, gfc_expr *shift, gfc_expr *boundary,
+		   gfc_expr *dim)
+{
+  bool temp_boundary;
+  gfc_expr *bnd;
+  gfc_expr *result;
+  int which;
+  gfc_expr **arrayvec, **resultvec;
+  gfc_expr **rptr, **sptr;
+  mpz_t size;
+  size_t arraysize, i;
+  gfc_constructor *array_ctor, *shift_ctor, *bnd_ctor;
+  ssize_t shift_val, len;
+  ssize_t count[GFC_MAX_DIMENSIONS], extent[GFC_MAX_DIMENSIONS],
+    sstride[GFC_MAX_DIMENSIONS], a_extent[GFC_MAX_DIMENSIONS],
+    a_stride[GFC_MAX_DIMENSIONS], ss_ex[GFC_MAX_DIMENSIONS + 1];
+  ssize_t rsoffset;
+  int d, n;
+  bool continue_loop;
+  gfc_expr **src, **dest;
+  size_t s_len;
+
+  if (!is_constant_array_expr (array))
+    return NULL;
+
+  if (shift->rank > 0)
+    gfc_simplify_expr (shift, 1);
+
+  if (!gfc_is_constant_expr (shift))
+    return NULL;
+
+  if (boundary)
+    {
+      if (boundary->rank > 0)
+	gfc_simplify_expr (boundary, 1);
+
+      if (!gfc_is_constant_expr (boundary))
+	  return NULL;
+    }
+
+  if (dim)
+    {
+      if (!gfc_is_constant_expr (dim))
+	return NULL;
+      which = mpz_get_si (dim->value.integer) - 1;
+    }
+  else
+    which = 0;
+
+  s_len = 0;
+  if (boundary == NULL)
+    {
+      temp_boundary = true;
+      switch (array->ts.type)
+	{
+
+	case BT_INTEGER:
+	  bnd = gfc_get_int_expr (array->ts.kind, NULL, 0);
+	  break;
+
+	case BT_LOGICAL:
+	  bnd = gfc_get_logical_expr (array->ts.kind, NULL, 0);
+	  break;
+
+	case BT_REAL:
+	  bnd = gfc_get_constant_expr (array->ts.type, array->ts.kind, &gfc_current_locus);
+	  mpfr_set_ui (bnd->value.real, 0, GFC_RND_MODE);
+	  break;
+
+	case BT_COMPLEX:
+	  bnd = gfc_get_constant_expr (array->ts.type, array->ts.kind, &gfc_current_locus);
+	  mpc_set_ui (bnd->value.complex, 0, GFC_RND_MODE);
+	  break;
+
+	case BT_CHARACTER:
+	  s_len = mpz_get_ui (array->ts.u.cl->length->value.integer);
+	  bnd = gfc_get_character_expr (array->ts.kind, &gfc_current_locus, NULL, s_len);
+	  break;
+
+	default:
+	  gcc_unreachable();
+
+	}
+    }
+  else
+    {
+      temp_boundary = false;
+      bnd = boundary;
+    }
+
+  gfc_array_size (array, &size);
+  arraysize = mpz_get_ui (size);
+  mpz_clear (size);
+
+  result = gfc_get_array_expr (array->ts.type, array->ts.kind, &array->where);
+  result->shape = gfc_copy_shape (array->shape, array->rank);
+  result->rank = array->rank;
+  result->ts = array->ts;
+
+  if (arraysize == 0)
+    goto final;
+
+  arrayvec = XCNEWVEC (gfc_expr *, arraysize);
+  array_ctor = gfc_constructor_first (array->value.constructor);
+  for (i = 0; i < arraysize; i++)
+    {
+      arrayvec[i] = array_ctor->expr;
+      array_ctor = gfc_constructor_next (array_ctor);
+    }
+
+  resultvec = XCNEWVEC (gfc_expr *, arraysize);
+
+  extent[0] = 1;
+  count[0] = 0;
+
+  for (d=0; d < array->rank; d++)
+    {
+      a_extent[d] = mpz_get_si (array->shape[d]);
+      a_stride[d] = d == 0 ? 1 : a_stride[d-1] * a_extent[d-1];
+    }
+
+  if (shift->rank > 0)
+    {
+      shift_ctor = gfc_constructor_first (shift->value.constructor);
+      shift_val = 0;
+    }
+  else
+    {
+      shift_ctor = NULL;
+      shift_val = mpz_get_si (shift->value.integer);
+    }
+
+  if (bnd->rank > 0)
+    bnd_ctor = gfc_constructor_first (bnd->value.constructor);
+  else
+    bnd_ctor = NULL;
+
+  /* Shut up compiler */
+  len = 1;
+  rsoffset = 1;
+
+  n = 0;
+  for (d=0; d < array->rank; d++)
+    {
+      if (d == which)
+	{
+	  rsoffset = a_stride[d];
+	  len = a_extent[d];
+	}
+      else
+	{
+	  count[n] = 0;
+	  extent[n] = a_extent[d];
+	  sstride[n] = a_stride[d];
+	  ss_ex[n] = sstride[n] * extent[n];
+	  n++;
+	}
+    }
+  ss_ex[n] = 0;
+
+  continue_loop = true;
+  d = array->rank;
+  rptr = resultvec;
+  sptr = arrayvec;
+
+  while (continue_loop)
+    {
+      ssize_t sh, delta;
+
+      if (shift_ctor)
+	sh = mpz_get_si (shift_ctor->expr->value.integer);
+      else
+	sh = shift_val;
+
+      if (( sh >= 0 ? sh : -sh ) > len)
+	{
+	  delta = len;
+	  sh = len;
+	}
+      else
+	delta = (sh >= 0) ? sh: -sh;
+
+      if (sh > 0)
+        {
+          src = &sptr[delta * rsoffset];
+          dest = rptr;
+        }
+      else
+        {
+          src = sptr;
+          dest = &rptr[delta * rsoffset];
+        }
+
+      for (n = 0; n < len - delta; n++)
+	{
+	  *dest = *src;
+	  dest += rsoffset;
+	  src += rsoffset;
+	}
+
+      if (sh < 0)
+        dest = rptr;
+
+      n = delta;
+
+      if (bnd_ctor)
+	{
+	  while (n--)
+	    {
+	      *dest = gfc_copy_expr (bnd_ctor->expr);
+	      dest += rsoffset;
+	    }
+	}
+      else
+	{
+	  while (n--)
+	    {
+	      *dest = gfc_copy_expr (bnd);
+	      dest += rsoffset;
+	    }
+	}
+      rptr += sstride[0];
+      sptr += sstride[0];
+      if (shift_ctor)
+	shift_ctor =  gfc_constructor_next (shift_ctor);
+
+      if (bnd_ctor)
+	bnd_ctor = gfc_constructor_next (bnd_ctor);
+
+      count[0]++;
+      n = 0;
+      while (count[n] == extent[n])
+	{
+	  count[n] = 0;
+	  rptr -= ss_ex[n];
+	  sptr -= ss_ex[n];
+	  n++;
+	  if (n >= d - 1)
+	    {
+	      continue_loop = false;
+	      break;
+	    }
+	  else
+	    {
+	      count[n]++;
+	      rptr += sstride[n];
+	      sptr += sstride[n];
+	    }
+	}
+    }
+
+  for (i = 0; i < arraysize; i++)
+    {
+      gfc_constructor_append_expr (&result->value.constructor,
+				   gfc_copy_expr (resultvec[i]),
+				   NULL);
+    }
+
+ final:
+  if (temp_boundary)
+    gfc_free_expr (bnd);
+
+  return result;
+}
+
+gfc_expr *
 gfc_simplify_erf (gfc_expr *x)
 {
   gfc_expr *result;
@@ -2497,6 +2934,30 @@
 
 
 gfc_expr *
+gfc_simplify_get_team (gfc_expr *level ATTRIBUTE_UNUSED)
+{
+  if (flag_coarray == GFC_FCOARRAY_NONE)
+    {
+      gfc_current_locus = *gfc_current_intrinsic_where;
+      gfc_fatal_error ("Coarrays disabled at %C, use %<-fcoarray=%> to enable");
+      return &gfc_bad_expr;
+    }
+
+  if (flag_coarray == GFC_FCOARRAY_SINGLE)
+    {
+      gfc_expr *result;
+      result = gfc_get_array_expr (BT_INTEGER, gfc_default_integer_kind, &gfc_current_locus);
+      result->rank = 0;
+      return result;
+    }
+
+  /* For fcoarray = lib no simplification is possible, because it is not known
+     what images failed or are stopped at compile time.  */
+  return NULL;
+}
+
+
+gfc_expr *
 gfc_simplify_float (gfc_expr *a)
 {
   gfc_expr *result;
@@ -2662,7 +3123,7 @@
 
 #if MPFR_VERSION < MPFR_VERSION_NUM(3,1,0)
 
-  /* MPFR versions before 3.1.0 do not include mpfr_frexp.  
+  /* MPFR versions before 3.1.0 do not include mpfr_frexp.
      TODO: remove the kludge when MPFR 3.1.0 or newer will be required */
 
   if (mpfr_sgn (x->value.real) == 0)
@@ -4021,7 +4482,7 @@
 gfc_simplify_len_trim (gfc_expr *e, gfc_expr *kind)
 {
   gfc_expr *result;
-  int count, len, i;
+  size_t count, len, i;
   int k = get_kind (BT_INTEGER, kind, "LEN_TRIM", gfc_default_integer_kind);
 
   if (k == -1)
@@ -4196,10 +4657,23 @@
       || !is_constant_array_expr (matrix_b))
     return NULL;
 
-  gcc_assert (gfc_compare_types (&matrix_a->ts, &matrix_b->ts));
-  result = gfc_get_array_expr (matrix_a->ts.type,
-			       matrix_a->ts.kind,
-			       &matrix_a->where);
+  /* MATMUL should do mixed-mode arithmetic.  Set the result type.  */
+  if (matrix_a->ts.type != matrix_b->ts.type)
+    {
+      gfc_expr e;
+      e.expr_type = EXPR_OP;
+      gfc_clear_ts (&e.ts);
+      e.value.op.op = INTRINSIC_NONE;
+      e.value.op.op1 = matrix_a;
+      e.value.op.op2 = matrix_b;
+      gfc_type_convert_binary (&e, 1);
+      result = gfc_get_array_expr (e.ts.type, e.ts.kind, &matrix_a->where);
+    }
+  else
+    {
+      result = gfc_get_array_expr (matrix_a->ts.type, matrix_a->ts.kind,
+				   &matrix_a->where);
+    }
 
   if (matrix_a->rank == 1 && matrix_b->rank == 2)
     {
@@ -4405,25 +4879,34 @@
 
 /* Selects between current value and extremum for simplify_min_max
    and simplify_minval_maxval.  */
-static void
-min_max_choose (gfc_expr *arg, gfc_expr *extremum, int sign)
-{
+static int
+min_max_choose (gfc_expr *arg, gfc_expr *extremum, int sign, bool back_val)
+{
+  int ret;
+
   switch (arg->ts.type)
     {
       case BT_INTEGER:
-	if (mpz_cmp (arg->value.integer,
-			extremum->value.integer) * sign > 0)
-	mpz_set (extremum->value.integer, arg->value.integer);
+	ret = mpz_cmp (arg->value.integer,
+		       extremum->value.integer) * sign;
+	if (ret > 0)
+	  mpz_set (extremum->value.integer, arg->value.integer);
 	break;
 
       case BT_REAL:
-	/* We need to use mpfr_min and mpfr_max to treat NaN properly.  */
-	if (sign > 0)
-	  mpfr_max (extremum->value.real, extremum->value.real,
-		      arg->value.real, GFC_RND_MODE);
+	if (mpfr_nan_p (extremum->value.real))
+	  {
+	    ret = 1;
+	    mpfr_set (extremum->value.real, arg->value.real, GFC_RND_MODE);
+	  }
+	else if (mpfr_nan_p (arg->value.real))
+	  ret = -1;
 	else
-	  mpfr_min (extremum->value.real, extremum->value.real,
-		      arg->value.real, GFC_RND_MODE);
+	  {
+	    ret = mpfr_cmp (arg->value.real, extremum->value.real) * sign;
+	    if (ret > 0)
+	      mpfr_set (extremum->value.real, arg->value.real, GFC_RND_MODE);
+	  }
 	break;
 
       case BT_CHARACTER:
@@ -4442,8 +4925,8 @@
 	    LENGTH(extremum) = LENGTH(arg);
 	    free (tmp);
 	  }
-
-	if (gfc_compare_string (arg, extremum) * sign > 0)
+	ret = gfc_compare_string (arg, extremum) * sign;
+	if (ret > 0)
 	  {
 	    free (STRING(extremum));
 	    STRING(extremum) = gfc_get_wide_string (LENGTH(extremum) + 1);
@@ -4460,6 +4943,10 @@
       default:
 	gfc_internal_error ("simplify_min_max(): Bad type in arglist");
     }
+  if (back_val && ret == 0)
+    ret = 1;
+
+  return ret;
 }
 
 
@@ -4534,69 +5021,436 @@
   return simplify_min_max (e, 1);
 }
 
-
-/* This is a simplified version of simplify_min_max to provide
-   simplification of minval and maxval for a vector.  */
+/* Helper function for gfc_simplify_minval.  */
 
 static gfc_expr *
-simplify_minval_maxval (gfc_expr *expr, int sign)
-{
-  gfc_constructor *c, *extremum;
-  gfc_intrinsic_sym * specific;
-
-  extremum = NULL;
-  specific = expr->value.function.isym;
-
-  for (c = gfc_constructor_first (expr->value.constructor);
-       c; c = gfc_constructor_next (c))
-    {
-      if (c->expr->expr_type != EXPR_CONSTANT)
-	return NULL;
-
-      if (extremum == NULL)
-	{
-	  extremum = c;
-	  continue;
-	}
-
-      min_max_choose (c->expr, extremum->expr, sign);
-     }
-
-  if (extremum == NULL)
-    return NULL;
-
-  /* Convert to the correct type and kind.  */
-  if (expr->ts.type != BT_UNKNOWN)
-    return gfc_convert_constant (extremum->expr,
-	expr->ts.type, expr->ts.kind);
-
-  if (specific->ts.type != BT_UNKNOWN)
-    return gfc_convert_constant (extremum->expr,
-	specific->ts.type, specific->ts.kind);
-
-  return gfc_copy_expr (extremum->expr);
-}
-
+gfc_min (gfc_expr *op1, gfc_expr *op2)
+{
+  min_max_choose (op1, op2, -1);
+  gfc_free_expr (op1);
+  return op2;
+}
+
+/* Simplify minval for constant arrays.  */
 
 gfc_expr *
 gfc_simplify_minval (gfc_expr *array, gfc_expr* dim, gfc_expr *mask)
 {
-  if (array->expr_type != EXPR_ARRAY || array->rank != 1 || dim || mask)
-    return NULL;
-
-  return simplify_minval_maxval (array, -1);
-}
-
+  return simplify_transformation (array, dim, mask, INT_MAX, gfc_min);
+}
+
+/* Helper function for gfc_simplify_maxval.  */
+
+static gfc_expr *
+gfc_max (gfc_expr *op1, gfc_expr *op2)
+{
+  min_max_choose (op1, op2, 1);
+  gfc_free_expr (op1);
+  return op2;
+}
+
+
+/* Simplify maxval for constant arrays.  */
 
 gfc_expr *
 gfc_simplify_maxval (gfc_expr *array, gfc_expr* dim, gfc_expr *mask)
 {
-  if (array->expr_type != EXPR_ARRAY || array->rank != 1 || dim || mask)
-    return NULL;
-
-  return simplify_minval_maxval (array, 1);
-}
-
+  return simplify_transformation (array, dim, mask, INT_MIN, gfc_max);
+}
+
+
+/* Transform minloc or maxloc of an array, according to MASK,
+   to the scalar result.  This code is mostly identical to
+   simplify_transformation_to_scalar.  */
+
+static gfc_expr *
+simplify_minmaxloc_to_scalar (gfc_expr *result, gfc_expr *array, gfc_expr *mask,
+			      gfc_expr *extremum, int sign, bool back_val)
+{
+  gfc_expr *a, *m;
+  gfc_constructor *array_ctor, *mask_ctor;
+  mpz_t count;
+
+  mpz_set_si (result->value.integer, 0);
+
+
+  /* Shortcut for constant .FALSE. MASK.  */
+  if (mask
+      && mask->expr_type == EXPR_CONSTANT
+      && !mask->value.logical)
+    return result;
+
+  array_ctor = gfc_constructor_first (array->value.constructor);
+  if (mask && mask->expr_type == EXPR_ARRAY)
+    mask_ctor = gfc_constructor_first (mask->value.constructor);
+  else
+    mask_ctor = NULL;
+
+  mpz_init_set_si (count, 0);
+  while (array_ctor)
+    {
+      mpz_add_ui (count, count, 1);
+      a = array_ctor->expr;
+      array_ctor = gfc_constructor_next (array_ctor);
+      /* A constant MASK equals .TRUE. here and can be ignored.  */
+      if (mask_ctor)
+	{
+	  m = mask_ctor->expr;
+	  mask_ctor = gfc_constructor_next (mask_ctor);
+	  if (!m->value.logical)
+	    continue;
+	}
+      if (min_max_choose (a, extremum, sign, back_val) > 0)
+	mpz_set (result->value.integer, count);
+    }
+  mpz_clear (count);
+  gfc_free_expr (extremum);
+  return result;
+}
+
+/* Simplify minloc / maxloc in the absence of a dim argument.  */
+
+static gfc_expr *
+simplify_minmaxloc_nodim (gfc_expr *result, gfc_expr *extremum,
+			  gfc_expr *array, gfc_expr *mask, int sign,
+			  bool back_val)
+{
+  ssize_t res[GFC_MAX_DIMENSIONS];
+  int i, n;
+  gfc_constructor *result_ctor, *array_ctor, *mask_ctor;
+  ssize_t count[GFC_MAX_DIMENSIONS], extent[GFC_MAX_DIMENSIONS],
+    sstride[GFC_MAX_DIMENSIONS];
+  gfc_expr *a, *m;
+  bool continue_loop;
+  bool ma;
+
+  for (i = 0; i<array->rank; i++)
+    res[i] = -1;
+
+  /* Shortcut for constant .FALSE. MASK.  */
+  if (mask
+      && mask->expr_type == EXPR_CONSTANT
+      && !mask->value.logical)
+    goto finish;
+
+  for (i = 0; i < array->rank; i++)
+    {
+      count[i] = 0;
+      sstride[i] = (i == 0) ? 1 : sstride[i-1] * mpz_get_si (array->shape[i-1]);
+      extent[i] = mpz_get_si (array->shape[i]);
+      if (extent[i] <= 0)
+	goto finish;
+    }
+
+  continue_loop = true;
+  array_ctor = gfc_constructor_first (array->value.constructor);
+  if (mask && mask->rank > 0)
+    mask_ctor = gfc_constructor_first (mask->value.constructor);
+  else
+    mask_ctor = NULL;
+
+  /* Loop over the array elements (and mask), keeping track of
+     the indices to return.  */
+  while (continue_loop)
+    {
+      do
+	{
+	  a = array_ctor->expr;
+	  if (mask_ctor)
+	    {
+	      m = mask_ctor->expr;
+	      ma = m->value.logical;
+	      mask_ctor = gfc_constructor_next (mask_ctor);
+	    }
+	  else
+	    ma = true;
+
+	  if (ma && min_max_choose (a, extremum, sign, back_val) > 0)
+	    {
+	      for (i = 0; i<array->rank; i++)
+		res[i] = count[i];
+	    }
+	  array_ctor = gfc_constructor_next (array_ctor);
+	  count[0] ++;
+	} while (count[0] != extent[0]);
+      n = 0;
+      do
+	{
+	  /* When we get to the end of a dimension, reset it and increment
+	     the next dimension.  */
+	  count[n] = 0;
+	  n++;
+	  if (n >= array->rank)
+	    {
+	      continue_loop = false;
+	      break;
+	    }
+	  else
+	    count[n] ++;
+	} while (count[n] == extent[n]);
+    }
+
+ finish:
+  gfc_free_expr (extremum);
+  result_ctor = gfc_constructor_first (result->value.constructor);
+  for (i = 0; i<array->rank; i++)
+    {
+      gfc_expr *r_expr;
+      r_expr = result_ctor->expr;
+      mpz_set_si (r_expr->value.integer, res[i] + 1);
+      result_ctor = gfc_constructor_next (result_ctor);
+    }
+  return result;
+}
+
+/* Helper function for gfc_simplify_minmaxloc - build an array
+   expression with n elements.  */
+
+static gfc_expr *
+new_array (bt type, int kind, int n, locus *where)
+{
+  gfc_expr *result;
+  int i;
+
+  result = gfc_get_array_expr (type, kind, where);
+  result->rank = 1;
+  result->shape = gfc_get_shape(1);
+  mpz_init_set_si (result->shape[0], n);
+  for (i = 0; i < n; i++)
+    {
+      gfc_constructor_append_expr (&result->value.constructor,
+				   gfc_get_constant_expr (type, kind, where),
+				   NULL);
+    }
+
+  return result;
+}
+
+/* Simplify minloc and maxloc. This code is mostly identical to
+   simplify_transformation_to_array.  */
+
+static gfc_expr *
+simplify_minmaxloc_to_array (gfc_expr *result, gfc_expr *array,
+			     gfc_expr *dim, gfc_expr *mask,
+			     gfc_expr *extremum, int sign, bool back_val)
+{
+  mpz_t size;
+  int done, i, n, arraysize, resultsize, dim_index, dim_extent, dim_stride;
+  gfc_expr **arrayvec, **resultvec, **base, **src, **dest;
+  gfc_constructor *array_ctor, *mask_ctor, *result_ctor;
+
+  int count[GFC_MAX_DIMENSIONS], extent[GFC_MAX_DIMENSIONS],
+      sstride[GFC_MAX_DIMENSIONS], dstride[GFC_MAX_DIMENSIONS],
+      tmpstride[GFC_MAX_DIMENSIONS];
+
+  /* Shortcut for constant .FALSE. MASK.  */
+  if (mask
+      && mask->expr_type == EXPR_CONSTANT
+      && !mask->value.logical)
+    return result;
+
+  /* Build an indexed table for array element expressions to minimize
+     linked-list traversal. Masked elements are set to NULL.  */
+  gfc_array_size (array, &size);
+  arraysize = mpz_get_ui (size);
+  mpz_clear (size);
+
+  arrayvec = XCNEWVEC (gfc_expr*, arraysize);
+
+  array_ctor = gfc_constructor_first (array->value.constructor);
+  mask_ctor = NULL;
+  if (mask && mask->expr_type == EXPR_ARRAY)
+    mask_ctor = gfc_constructor_first (mask->value.constructor);
+
+  for (i = 0; i < arraysize; ++i)
+    {
+      arrayvec[i] = array_ctor->expr;
+      array_ctor = gfc_constructor_next (array_ctor);
+
+      if (mask_ctor)
+	{
+	  if (!mask_ctor->expr->value.logical)
+	    arrayvec[i] = NULL;
+
+	  mask_ctor = gfc_constructor_next (mask_ctor);
+	}
+    }
+
+  /* Same for the result expression.  */
+  gfc_array_size (result, &size);
+  resultsize = mpz_get_ui (size);
+  mpz_clear (size);
+
+  resultvec = XCNEWVEC (gfc_expr*, resultsize);
+  result_ctor = gfc_constructor_first (result->value.constructor);
+  for (i = 0; i < resultsize; ++i)
+    {
+      resultvec[i] = result_ctor->expr;
+      result_ctor = gfc_constructor_next (result_ctor);
+    }
+
+  gfc_extract_int (dim, &dim_index);
+  dim_index -= 1;               /* zero-base index */
+  dim_extent = 0;
+  dim_stride = 0;
+
+  for (i = 0, n = 0; i < array->rank; ++i)
+    {
+      count[i] = 0;
+      tmpstride[i] = (i == 0) ? 1 : tmpstride[i-1] * mpz_get_si (array->shape[i-1]);
+      if (i == dim_index)
+	{
+	  dim_extent = mpz_get_si (array->shape[i]);
+	  dim_stride = tmpstride[i];
+	  continue;
+	}
+
+      extent[n] = mpz_get_si (array->shape[i]);
+      sstride[n] = tmpstride[i];
+      dstride[n] = (n == 0) ? 1 : dstride[n-1] * extent[n-1];
+      n += 1;
+    }
+
+  done = resultsize <= 0;
+  base = arrayvec;
+  dest = resultvec;
+  while (!done)
+    {
+      gfc_expr *ex;
+      ex = gfc_copy_expr (extremum);
+      for (src = base, n = 0; n < dim_extent; src += dim_stride, ++n)
+	{
+	  if (*src && min_max_choose (*src, ex, sign, back_val) > 0)
+	    mpz_set_si ((*dest)->value.integer, n + 1);
+	}
+
+      count[0]++;
+      base += sstride[0];
+      dest += dstride[0];
+      gfc_free_expr (ex);
+
+      n = 0;
+      while (!done && count[n] == extent[n])
+	{
+	  count[n] = 0;
+	  base -= sstride[n] * extent[n];
+	  dest -= dstride[n] * extent[n];
+
+	  n++;
+	  if (n < result->rank)
+	    {
+	      /* If the nested loop is unrolled GFC_MAX_DIMENSIONS
+		 times, we'd warn for the last iteration, because the
+		 array index will have already been incremented to the
+		 array sizes, and we can't tell that this must make
+		 the test against result->rank false, because ranks
+		 must not exceed GFC_MAX_DIMENSIONS.  */
+	      GCC_DIAGNOSTIC_PUSH_IGNORED (-Warray-bounds)
+	      count[n]++;
+	      base += sstride[n];
+	      dest += dstride[n];
+	      GCC_DIAGNOSTIC_POP
+	    }
+	  else
+	    done = true;
+       }
+    }
+
+  /* Place updated expression in result constructor.  */
+  result_ctor = gfc_constructor_first (result->value.constructor);
+  for (i = 0; i < resultsize; ++i)
+    {
+      result_ctor->expr = resultvec[i];
+      result_ctor = gfc_constructor_next (result_ctor);
+    }
+
+  free (arrayvec);
+  free (resultvec);
+  free (extremum);
+  return result;
+}
+
+/* Simplify minloc and maxloc for constant arrays.  */
+
+gfc_expr *
+gfc_simplify_minmaxloc (gfc_expr *array, gfc_expr *dim, gfc_expr *mask,
+			gfc_expr *kind, gfc_expr *back, int sign)
+{
+  gfc_expr *result;
+  gfc_expr *extremum;
+  int ikind;
+  int init_val;
+  bool back_val = false;
+
+  if (!is_constant_array_expr (array)
+      || !gfc_is_constant_expr (dim))
+    return NULL;
+
+  if (mask
+      && !is_constant_array_expr (mask)
+      && mask->expr_type != EXPR_CONSTANT)
+    return NULL;
+
+  if (kind)
+    {
+      if (gfc_extract_int (kind, &ikind, -1))
+	return NULL;
+    }
+  else
+    ikind = gfc_default_integer_kind;
+
+  if (back)
+    {
+      if (back->expr_type != EXPR_CONSTANT)
+	return NULL;
+
+      back_val = back->value.logical;
+    }
+  
+  if (sign < 0)
+    init_val = INT_MAX;
+  else if (sign > 0)
+    init_val = INT_MIN;
+  else
+    gcc_unreachable();
+
+  extremum = gfc_get_constant_expr (array->ts.type, array->ts.kind, &array->where);
+  init_result_expr (extremum, init_val, array);
+
+  if (dim)
+    {
+      result = transformational_result (array, dim, BT_INTEGER,
+					ikind, &array->where);
+      init_result_expr (result, 0, array);
+
+      if (array->rank == 1)
+	return simplify_minmaxloc_to_scalar (result, array, mask, extremum,
+					     sign, back_val);
+      else
+	return simplify_minmaxloc_to_array (result, array, dim, mask, extremum,
+					    sign, back_val);
+    }
+  else
+    {
+      result = new_array (BT_INTEGER, ikind, array->rank, &array->where);
+      return simplify_minmaxloc_nodim (result, extremum, array, mask,
+				       sign, back_val);
+    }
+}
+
+gfc_expr *
+gfc_simplify_minloc (gfc_expr *array, gfc_expr *dim, gfc_expr *mask, gfc_expr *kind,
+		     gfc_expr *back)
+{
+  return gfc_simplify_minmaxloc (array, dim, mask, kind, back, -1);
+}
+
+gfc_expr *
+gfc_simplify_maxloc (gfc_expr *array, gfc_expr *dim, gfc_expr *mask, gfc_expr *kind,
+		     gfc_expr *back)
+{
+  return gfc_simplify_minmaxloc (array, dim, mask, kind, back, 1);
+}
 
 gfc_expr *
 gfc_simplify_maxexponent (gfc_expr *x)
@@ -4622,43 +5476,48 @@
   gfc_expr *result;
   int kind;
 
-  if (a->expr_type != EXPR_CONSTANT || p->expr_type != EXPR_CONSTANT)
-    return NULL;
-
-  kind = a->ts.kind > p->ts.kind ? a->ts.kind : p->ts.kind;
-  result = gfc_get_constant_expr (a->ts.type, kind, &a->where);
-
-  switch (a->ts.type)
+  /* First check p.  */
+  if (p->expr_type != EXPR_CONSTANT)
+    return NULL;
+
+  /* p shall not be 0.  */
+  switch (p->ts.type)
     {
       case BT_INTEGER:
 	if (mpz_cmp_ui (p->value.integer, 0) == 0)
 	  {
-	    /* Result is processor-dependent.  */
-	    gfc_error ("Second argument MOD at %L is zero", &a->where);
-	    gfc_free_expr (result);
+	    gfc_error ("Argument %qs of MOD at %L shall not be zero",
+			"P", &p->where);
 	    return &gfc_bad_expr;
 	  }
-	mpz_tdiv_r (result->value.integer, a->value.integer, p->value.integer);
 	break;
-
       case BT_REAL:
 	if (mpfr_cmp_ui (p->value.real, 0) == 0)
 	  {
-	    /* Result is processor-dependent.  */
-	    gfc_error ("Second argument of MOD at %L is zero", &p->where);
-	    gfc_free_expr (result);
+	    gfc_error ("Argument %qs of MOD at %L shall not be zero",
+			"P", &p->where);
 	    return &gfc_bad_expr;
 	  }
-
-	gfc_set_model_kind (kind);
-	mpfr_fmod (result->value.real, a->value.real, p->value.real,
-		   GFC_RND_MODE);
 	break;
-
       default:
 	gfc_internal_error ("gfc_simplify_mod(): Bad arguments");
     }
 
+  if (a->expr_type != EXPR_CONSTANT)
+    return NULL;
+
+  kind = a->ts.kind > p->ts.kind ? a->ts.kind : p->ts.kind;
+  result = gfc_get_constant_expr (a->ts.type, kind, &a->where);
+
+  if (a->ts.type == BT_INTEGER)
+    mpz_tdiv_r (result->value.integer, a->value.integer, p->value.integer);
+  else
+    {
+      gfc_set_model_kind (kind);
+      mpfr_fmod (result->value.real, a->value.real, p->value.real,
+		 GFC_RND_MODE);
+    }
+
   return range_check (result, "MOD");
 }
 
@@ -4669,54 +5528,57 @@
   gfc_expr *result;
   int kind;
 
-  if (a->expr_type != EXPR_CONSTANT || p->expr_type != EXPR_CONSTANT)
-    return NULL;
-
-  kind = a->ts.kind > p->ts.kind ? a->ts.kind : p->ts.kind;
-  result = gfc_get_constant_expr (a->ts.type, kind, &a->where);
-
-  switch (a->ts.type)
+  /* First check p.  */
+  if (p->expr_type != EXPR_CONSTANT)
+    return NULL;
+
+  /* p shall not be 0.  */
+  switch (p->ts.type)
     {
       case BT_INTEGER:
 	if (mpz_cmp_ui (p->value.integer, 0) == 0)
 	  {
-	    /* Result is processor-dependent. This processor just opts
-	      to not handle it at all.  */
-	    gfc_error ("Second argument of MODULO at %L is zero", &a->where);
-	    gfc_free_expr (result);
+	    gfc_error ("Argument %qs of MODULO at %L shall not be zero",
+			"P", &p->where);
 	    return &gfc_bad_expr;
 	  }
-	mpz_fdiv_r (result->value.integer, a->value.integer, p->value.integer);
-
 	break;
-
       case BT_REAL:
 	if (mpfr_cmp_ui (p->value.real, 0) == 0)
 	  {
-	    /* Result is processor-dependent.  */
-	    gfc_error ("Second argument of MODULO at %L is zero", &p->where);
-	    gfc_free_expr (result);
+	    gfc_error ("Argument %qs of MODULO at %L shall not be zero",
+			"P", &p->where);
 	    return &gfc_bad_expr;
 	  }
-
-	gfc_set_model_kind (kind);
-	mpfr_fmod (result->value.real, a->value.real, p->value.real,
-		   GFC_RND_MODE);
-	if (mpfr_cmp_ui (result->value.real, 0) != 0)
-	  {
-	    if (mpfr_signbit (a->value.real) != mpfr_signbit (p->value.real))
-	      mpfr_add (result->value.real, result->value.real, p->value.real,
-			GFC_RND_MODE);
-	  }
-	else
-	  mpfr_copysign (result->value.real, result->value.real,
-			 p->value.real, GFC_RND_MODE);
 	break;
-
       default:
 	gfc_internal_error ("gfc_simplify_modulo(): Bad arguments");
     }
 
+  if (a->expr_type != EXPR_CONSTANT)
+    return NULL;
+
+  kind = a->ts.kind > p->ts.kind ? a->ts.kind : p->ts.kind;
+  result = gfc_get_constant_expr (a->ts.type, kind, &a->where);
+
+  if (a->ts.type == BT_INTEGER)
+	mpz_fdiv_r (result->value.integer, a->value.integer, p->value.integer);
+  else
+    {
+      gfc_set_model_kind (kind);
+      mpfr_fmod (result->value.real, a->value.real, p->value.real,
+                 GFC_RND_MODE);
+      if (mpfr_cmp_ui (result->value.real, 0) != 0)
+        {
+          if (mpfr_signbit (a->value.real) != mpfr_signbit (p->value.real))
+            mpfr_add (result->value.real, result->value.real, p->value.real,
+                      GFC_RND_MODE);
+	    }
+	  else
+        mpfr_copysign (result->value.real, result->value.real,
+                       p->value.real, GFC_RND_MODE);
+    }
+
   return range_check (result, "MODULO");
 }
 
@@ -4859,14 +5721,20 @@
 gfc_simplify_norm2 (gfc_expr *e, gfc_expr *dim)
 {
   gfc_expr *result;
-
-  if (!is_constant_array_expr (e)
+  bool size_zero;
+
+  size_zero = gfc_is_size_zero_array (e);
+
+  if (!(is_constant_array_expr (e) || size_zero)
       || (dim != NULL && !gfc_is_constant_expr (dim)))
     return NULL;
 
   result = transformational_result (e, dim, e->ts.type, e->ts.kind, &e->where);
   init_result_expr (result, 0, NULL);
 
+  if (size_zero)
+    return result;
+
   if (!dim || e->rank == 1)
     {
       result = simplify_transformation_to_scalar (result, e, NULL,
@@ -5226,7 +6094,7 @@
 gfc_simplify_repeat (gfc_expr *e, gfc_expr *n)
 {
   gfc_expr *result;
-  int i, j, len, ncop, nlen;
+  gfc_charlen_t len;
   mpz_t ncopies;
   bool have_length = false;
 
@@ -5246,7 +6114,7 @@
   if (e->ts.u.cl && e->ts.u.cl->length
 	&& e->ts.u.cl->length->expr_type == EXPR_CONSTANT)
     {
-      len = mpz_get_si (e->ts.u.cl->length->value.integer);
+      len = gfc_mpz_get_hwi (e->ts.u.cl->length->value.integer);
       have_length = true;
     }
   else if (e->expr_type == EXPR_CONSTANT
@@ -5282,7 +6150,8 @@
 	}
       else
 	{
-	  mpz_init_set_si (mlen, len);
+	  mpz_init (mlen);
+	  gfc_mpz_set_hwi (mlen, len);
 	  mpz_tdiv_q (max, gfc_integer_kinds[i].huge, mlen);
 	  mpz_clear (mlen);
 	}
@@ -5306,11 +6175,12 @@
   if (e->expr_type != EXPR_CONSTANT)
     return NULL;
 
+  HOST_WIDE_INT ncop;
   if (len ||
       (e->ts.u.cl->length &&
        mpz_sgn (e->ts.u.cl->length->value.integer) != 0))
     {
-      bool fail = gfc_extract_int (n, &ncop);
+      bool fail = gfc_extract_hwi (n, &ncop);
       gcc_assert (!fail);
     }
   else
@@ -5320,11 +6190,22 @@
     return gfc_get_character_expr (e->ts.kind, &e->where, NULL, 0);
 
   len = e->value.character.length;
-  nlen = ncop * len;
+  gfc_charlen_t nlen = ncop * len;
+
+  /* Here's a semi-arbitrary limit. If the string is longer than 1 GB
+     (2**28 elements * 4 bytes (wide chars) per element) defer to
+     runtime instead of consuming (unbounded) memory and CPU at
+     compile time.  */
+  if (nlen > 268435456)
+    {
+      gfc_warning_now (0, "Evaluation of string longer than 2**28 at %L"
+		       " deferred to runtime, expect bugs", &e->where);
+      return NULL;
+    }
 
   result = gfc_get_character_expr (e->ts.kind, &e->where, NULL, nlen);
-  for (i = 0; i < ncop; i++)
-    for (j = 0; j < len; j++)
+  for (size_t i = 0; i < (size_t) ncop; i++)
+    for (size_t j = 0; j < (size_t) len; j++)
       result->value.character.string[j+i*len]= e->value.character.string[j];
 
   result->value.character.string[nlen] = '\0';	/* For debugger */
@@ -6560,19 +7441,20 @@
   unsigned char *buffer;
   size_t result_length;
 
-
-  if (!gfc_is_constant_expr (source)
-	|| (gfc_init_expr_flag && !gfc_is_constant_expr (mold))
-	|| !gfc_is_constant_expr (size))
-    return NULL;
-
-  if (!gfc_calculate_transfer_sizes (source, mold, size, &source_size, 
+  if (!gfc_is_constant_expr (source) || !gfc_is_constant_expr (size))
+    return NULL;
+
+  if (!gfc_resolve_expr (mold))
+    return NULL;
+  if (gfc_init_expr_flag && !gfc_is_constant_expr (mold))
+    return NULL;
+
+  if (!gfc_calculate_transfer_sizes (source, mold, size, &source_size,
 				     &result_size, &result_length))
     return NULL;
 
   /* Calculate the size of the source.  */
-  if (source->expr_type == EXPR_ARRAY
-      && !gfc_array_size (source, &tmp))
+  if (source->expr_type == EXPR_ARRAY && !gfc_array_size (source, &tmp))
     gfc_internal_error ("Failure getting length of a constant array.");
 
   /* Create an empty new expression with the appropriate characteristics.  */
@@ -6580,7 +7462,7 @@
 				  &source->where);
   result->ts = mold->ts;
 
-  mold_element = mold->expr_type == EXPR_ARRAY
+  mold_element = (mold->expr_type == EXPR_ARRAY && mold->value.constructor)
 		 ? gfc_constructor_first (mold->value.constructor)->expr
 		 : mold;
 
@@ -7024,8 +7906,8 @@
 gfc_expr *
 gfc_convert_constant (gfc_expr *e, bt type, int kind)
 {
-  gfc_expr *g, *result, *(*f) (gfc_expr *, int);
-  gfc_constructor *c;
+  gfc_expr *result, *(*f) (gfc_expr *, int);
+  gfc_constructor *c, *t;
 
   switch (e->ts.type)
     {
@@ -7126,6 +8008,13 @@
 	}
       break;
 
+    case BT_CHARACTER:
+      if (type == BT_CHARACTER)
+	f = gfc_character2character;
+      else
+	goto oops;
+      break;
+
     default:
     oops:
       gfc_internal_error ("gfc_convert_constant(): Unexpected type");
@@ -7154,26 +8043,25 @@
 	{
 	  gfc_expr *tmp;
 	  if (c->iterator == NULL)
-	    tmp = f (c->expr, kind);
+	    {
+	      if (c->expr->expr_type == EXPR_ARRAY)
+		tmp = gfc_convert_constant (c->expr, type, kind);
+	      else
+		tmp = f (c->expr, kind);
+	    }
 	  else
-	    {
-	      g = gfc_convert_constant (c->expr, type, kind);
-	      if (g == &gfc_bad_expr)
-	        {
-		  gfc_free_expr (result);
-		  return g;
-		}
-	      tmp = g;
-	    }
-
-	  if (tmp == NULL)
+	    tmp = gfc_convert_constant (c->expr, type, kind);
+
+	  if (tmp == NULL || tmp == &gfc_bad_expr)
 	    {
 	      gfc_free_expr (result);
 	      return NULL;
 	    }
 
-	  gfc_constructor_append_expr (&result->value.constructor,
-				       tmp, &c->where);
+	  t = gfc_constructor_append_expr (&result->value.constructor,
+					   tmp, &c->where);
+	  if (c->iterator)
+	    t->iterator = gfc_copy_iterator (c->iterator);
 	}
 
       break;