Hello world, the attached patch implements simplification for cshift completely. It also fixes a bug where compile-time simplification was handled incorrectly for a negative value. For PR 83650 (the wrong simplification) for the other branches I suspect a quite simple fix will be possible, which I will address separately.
Regression-tested. OK for trunk? Regards Thomas 2018-01-02 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/45689 PR fortran/83650 * simplify.c (gfc_simplify_cshift): Re-implement to allow full range of arguments. 2018-01-02 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/45689 PR fortran/83650 * gfortran.dg/simplify_cshift_1.f90: Correct erroneous case. * gfortran.dg/simplify_cshift_4.f90: New test.
Index: fortran/simplify.c =================================================================== --- fortran/simplify.c (Revision 255788) +++ fortran/simplify.c (Arbeitskopie) @@ -1950,92 +1950,212 @@ gfc_simplify_count (gfc_expr *mask, gfc_expr *dim, 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; + 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], + 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]; + ssize_t rsoffset; + int d, n; + bool continue_loop; + gfc_expr **src, **dest; - /* DIM is only useful for rank > 1, but deal with it here as one can - set DIM = 1 for rank = 1. */ + 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 - dm = 1; + which = 0; - /* 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_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++) { - gfc_free_expr (a); - return NULL; + arrayvec[i] = array_ctor->expr; + array_ctor = gfc_constructor_next (array_ctor); } - if (a->rank == 1) + resultvec = XCNEWVEC (gfc_expr *, arraysize); + + extent[0] = 1; + count[0] = 0; + + for (d=0; d < array->rank; d++) { - gfc_constructor *ca, *cr; - mpz_t size; - int i, j, shft, sz; + a_extent[d] = mpz_get_si (array->shape[d]); + a_stride[d] = d == 0 ? 1 : a_stride[d-1] * a_extent[d-1]; + } - if (!gfc_is_constant_expr (shift)) + 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++) { - gfc_free_expr (a); - return NULL; + h_extent[d] = mpz_get_si (shift->shape[d]); + hstride[d] = d == 0 ? 1 : hstride[d-1] * h_extent[d-1]; } + } + else + shiftvec = NULL; + + /* Shut up compiler */ + len = 1; + rsoffset = 1; - shft = mpz_get_si (shift->value.integer); + 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]; + if (shiftvec) + hs_ex[n] = hstride[n] * extent[n]; + n++; + } + } - /* 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)) + if (shiftvec) + { + for (i = 0; i < shiftsize; i++) { - 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); + 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); } - - gfc_free_expr (a); - return result; + shift_val = 0; } else { - /* FIXME: Deal with rank > 1 arrays. For now, don't leak memory. */ + shift_val = mpz_get_si (shift->value.integer); + shift_val = shift_val % len; + if (shift_val < 0) + shift_val += len; + } - /* 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 (); + continue_loop = true; + d = array->rank; + rptr = resultvec; + sptr = arrayvec; + hptr = shiftvec; - gfc_free_expr (a); + 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]; + } + } } - return NULL; + for (i = 0; i < arraysize; i++) + { + gfc_constructor_append_expr (&result->value.constructor, + gfc_copy_expr (resultvec[i]), + NULL); + } + return result; } Index: testsuite/gfortran.dg/simplify_cshift_1.f90 =================================================================== --- testsuite/gfortran.dg/simplify_cshift_1.f90 (Revision 255788) +++ testsuite/gfortran.dg/simplify_cshift_1.f90 (Arbeitskopie) @@ -23,12 +23,12 @@ program foo v = cshift(c, 2) if (any(b /= v)) call abort - ! Special cases shift = 0, size(a), 1-size(a) + ! Special cases shift = 0, size(a), -size(a) b = cshift([1, 2, 3, 4, 5], 0) if (any(b /= a)) call abort b = cshift([1, 2, 3, 4, 5], size(a)) if (any(b /= a)) call abort - b = cshift([1, 2, 3, 4, 5], 1-size(a)) + b = cshift([1, 2, 3, 4, 5], -size(a)) if (any(b /= a)) call abort ! simplification of array arg.
! { dg-do run } program main implicit none integer :: i integer, parameter, dimension(3,3) :: a = & reshape([1,2,3,4,5,6,7,8,9], shape(a)) integer, dimension(3,3) :: b integer, parameter, dimension(3,4,5) :: c = & reshape([(i**2,i=1,3*4*5)],shape(c)) integer, dimension(3,4,5) :: d integer, dimension(4,5), parameter :: sh1 =& reshape([(i**3-12*i**2,i=1,4*5)],shape(sh1)) integer, dimension(3,5), parameter :: sh2 = & reshape([(i**3-7*i**2,i=1,3*5)], shape(sh2)) integer, dimension(3,4), parameter :: sh3 = & reshape([(i**3-3*i**2,i=1,3*4)], shape(sh3)) integer, parameter, dimension(3,4,5) :: c1 = cshift(c,shift=sh1,dim=1) integer, parameter, dimension(3,4,5) :: c2 = cshift(c,shift=sh2,dim=2) integer, parameter, dimension(3,4,5) :: c3 = cshift(c,shift=sh3,dim=3) b = a if (any(cshift(a,1) /= cshift(b,1))) call abort if (any(cshift(a,2) /= cshift(b,2))) call abort if (any(cshift(a,1,dim=2) /= cshift(b,1,dim=2))) call abort d = c if (any(cshift(c,1) /= cshift(d,1))) call abort if (any(cshift(c,2) /= cshift(d,2))) call abort if (any(cshift(c,3) /= cshift(d,3))) call abort if (any(cshift(c,1,dim=2) /= cshift(d,1,dim=2))) call abort if (any(cshift(c,2,dim=2) /= cshift(d,2,dim=2))) call abort if (any(cshift(c,3,dim=3) /= cshift(d,3,dim=3))) call abort if (any(cshift(d,shift=sh1,dim=1) /= c1)) call abort if (any(cshift(d,shift=sh2,dim=2) /= c2)) call abort if (any(cshift(d,shift=sh3,dim=3) /= c3)) call abort end program main