OSDN Git Service

PR c/23228
[pf3gnuchains/gcc-fork.git] / libgfortran / intrinsics / cshift0.c
index 5a2c8ca..199e283 100644 (file)
@@ -1,23 +1,32 @@
 /* Generic implementation of the CSHIFT intrinsic
-   Copyright 2003 Free Software Foundation, Inc.
+   Copyright 2003, 2005 Free Software Foundation, Inc.
    Contributed by Feng Wang <wf_cs@yahoo.com>
 
 This file is part of the GNU Fortran 95 runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
+modify it under the terms of the GNU General Public
 License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
+version 2 of the License, or (at your option) any later version.
 
-Ligbfor is distributed in the hope that it will be useful,
+In addition to the permissions in the GNU General Public License, the
+Free Software Foundation gives you unlimited permission to link the
+compiled version of this file into combinations with other programs,
+and to distribute those combinations without any restriction coming
+from the use of this file.  (The General Public License restrictions
+do apply in other respects; for example, they cover modification of
+the file, and distribution when not linked into a combine
+executable.)
+
+Libgfortran is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
+GNU General Public License for more details.
 
-You should have received a copy of the GNU Lesser General Public
-License along with libgfor; see the file COPYING.LIB.  If not,
-write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+You should have received a copy of the GNU General Public
+License along with libgfortran; see the file COPYING.  If not,
+write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 #include <stdlib.h>
@@ -25,50 +34,132 @@ Boston, MA 02111-1307, USA.  */
 #include <string.h>
 #include "libgfortran.h"
 
-/* TODO: make this work for large shifts when
-   sizeof(int) < sizeof (index_type).  */
+
+/* "Templatized" helper function for the inner shift loop.  */
+
+#define DEF_COPY_LOOP(NAME, TYPE)                                      \
+static inline void                                                     \
+copy_loop_##NAME (void *xdest, const void *xsrc,                       \
+                 size_t roff, size_t soff,                             \
+                 index_type len, index_type shift)                     \
+{                                                                      \
+  TYPE *dest = xdest;                                                  \
+  const TYPE *src;                                                     \
+  index_type i;                                                                \
+                                                                       \
+  roff /= sizeof (TYPE);                                               \
+  soff /= sizeof (TYPE);                                               \
+                                                                       \
+  src = xsrc;                                                          \
+  src += shift * soff;                                                 \
+  for (i = 0; i < len - shift; ++i)                                    \
+    {                                                                  \
+      *dest = *src;                                                    \
+      dest += roff;                                                    \
+      src += soff;                                                     \
+    }                                                                  \
+                                                                       \
+  src = xsrc;                                                          \
+  for (i = 0; i < shift; ++i)                                          \
+    {                                                                  \
+      *dest = *src;                                                    \
+      dest += roff;                                                    \
+      src += soff;                                                     \
+    }                                                                  \
+}
+
+DEF_COPY_LOOP(int, int)
+DEF_COPY_LOOP(long, long)
+DEF_COPY_LOOP(double, double)
+DEF_COPY_LOOP(ldouble, long double)
+DEF_COPY_LOOP(cfloat, _Complex float)
+DEF_COPY_LOOP(cdouble, _Complex double)
+
 
 static void
-__cshift0 (const gfc_array_char * ret, const gfc_array_char * array,
-    int shift, int which)
+cshift0 (gfc_array_char * ret, const gfc_array_char * array,
+        ssize_t shift, int which, index_type size)
 {
   /* r.* indicates the return array.  */
-  index_type rstride[GFC_MAX_DIMENSIONS - 1];
+  index_type rstride[GFC_MAX_DIMENSIONS];
   index_type rstride0;
   index_type roffset;
   char *rptr;
-  char *dest;
+
   /* s.* indicates the source array.  */
-  index_type sstride[GFC_MAX_DIMENSIONS - 1];
+  index_type sstride[GFC_MAX_DIMENSIONS];
   index_type sstride0;
   index_type soffset;
   const char *sptr;
-  const char *src;
 
-  index_type count[GFC_MAX_DIMENSIONS - 1];
-  index_type extent[GFC_MAX_DIMENSIONS - 1];
+  index_type count[GFC_MAX_DIMENSIONS];
+  index_type extent[GFC_MAX_DIMENSIONS];
   index_type dim;
-  index_type size;
   index_type len;
   index_type n;
+  int whichloop;
 
   if (which < 1 || which > GFC_DESCRIPTOR_RANK (array))
     runtime_error ("Argument 'DIM' is out of range in call to 'CSHIFT'");
 
-  size = GFC_DESCRIPTOR_SIZE (ret);
-
   which = which - 1;
 
   extent[0] = 1;
   count[0] = 0;
-  size = GFC_DESCRIPTOR_SIZE (array);
   n = 0;
 
-/* Initialized for avoiding compiler warnings.  */
+  /* The values assigned here must match the cases in the inner loop.  */
+  whichloop = 0;
+  switch (GFC_DESCRIPTOR_TYPE (array))
+    {
+    case GFC_DTYPE_LOGICAL:
+    case GFC_DTYPE_INTEGER:
+    case GFC_DTYPE_REAL:
+      if (size == sizeof (int))
+       whichloop = 1;
+      else if (size == sizeof (long))
+       whichloop = 2;
+      else if (size == sizeof (double))
+       whichloop = 3;
+      else if (size == sizeof (long double))
+       whichloop = 4;
+      break;
+
+    case GFC_DTYPE_COMPLEX:
+      if (size == sizeof (_Complex float))
+       whichloop = 5;
+      else if (size == sizeof (_Complex double))
+       whichloop = 6;
+      break;
+
+    default:
+      break;
+    }
+
+  /* Initialized for avoiding compiler warnings.  */
   roffset = size;
   soffset = size;
   len = 0;
 
+  if (ret->data == NULL)
+    {
+      int i;
+
+      ret->data = internal_malloc_size (size * size0 ((array_t *)array));
+      ret->offset = 0;
+      ret->dtype = array->dtype;
+      for (i = 0; i < GFC_DESCRIPTOR_RANK (array); i++)
+        {
+          ret->dim[i].lbound = 0;
+          ret->dim[i].ubound = array->dim[i].ubound - array->dim[i].lbound;
+
+          if (i == 0)
+            ret->dim[i].stride = 1;
+          else
+            ret->dim[i].stride = (ret->dim[i-1].ubound + 1) * ret->dim[i-1].stride;
+        }
+    }
+
   for (dim = 0; dim < GFC_DESCRIPTOR_RANK (array); dim++)
     {
       if (dim == which)
@@ -101,24 +192,78 @@ __cshift0 (const gfc_array_char * ret, const gfc_array_char * array,
   rptr = ret->data;
   sptr = array->data;
 
-  shift = (div (shift, len)).rem;
+  shift = shift % (ssize_t)len;
   if (shift < 0)
     shift += len;
 
   while (rptr)
     {
       /* Do the shift for this dimension.  */
-      src = &sptr[shift * soffset];
-      dest = rptr;
-      for (n = 0; n < len; n++)
-        {
-          memcpy (dest, src, size);
-          dest += roffset;
-          if (n == len - shift - 1)
-            src = sptr;
-          else
-            src += soffset;
-        }
+
+      /* If elements are contiguous, perform the operation
+        in two block moves.  */
+      if (soffset == size && roffset == size)
+       {
+         size_t len1 = shift * size;
+         size_t len2 = (len - shift) * size;
+         memcpy (rptr, sptr + len1, len2);
+         memcpy (rptr + len2, sptr, len1);
+       }
+      else
+       {
+         /* Otherwise, we'll have to perform the copy one element at
+            a time.  We can speed this up a tad for common cases of 
+            fundamental types.  */
+         switch (whichloop)
+           {
+           case 0:
+             {
+               char *dest = rptr;
+               const char *src = &sptr[shift * soffset];
+
+               for (n = 0; n < len - shift; n++)
+                 {
+                   memcpy (dest, src, size);
+                   dest += roffset;
+                   src += soffset;
+                 }
+               for (src = sptr, n = 0; n < shift; n++)
+                 {
+                   memcpy (dest, src, size);
+                   dest += roffset;
+                   src += soffset;
+                 }
+             }
+             break;
+
+           case 1:
+             copy_loop_int (rptr, sptr, roffset, soffset, len, shift);
+             break;
+
+           case 2:
+             copy_loop_long (rptr, sptr, roffset, soffset, len, shift);
+             break;
+
+           case 3:
+             copy_loop_double (rptr, sptr, roffset, soffset, len, shift);
+             break;
+
+           case 4:
+             copy_loop_ldouble (rptr, sptr, roffset, soffset, len, shift);
+             break;
+
+           case 5:
+             copy_loop_cfloat (rptr, sptr, roffset, soffset, len, shift);
+             break;
+             
+           case 6:
+             copy_loop_cdouble (rptr, sptr, roffset, soffset, len, shift);
+             break;
+
+           default:
+             abort ();
+           }
+       }
 
       /* Advance to the next section.  */
       rptr += rstride0;
@@ -151,19 +296,37 @@ __cshift0 (const gfc_array_char * ret, const gfc_array_char * array,
     }
 }
 
+#define DEFINE_CSHIFT(N)                                                     \
+  extern void cshift0_##N (gfc_array_char *, const gfc_array_char *,         \
+                          const GFC_INTEGER_##N *, const GFC_INTEGER_##N *); \
+  export_proto(cshift0_##N);                                                 \
+                                                                             \
+  void                                                                       \
+  cshift0_##N (gfc_array_char *ret, const gfc_array_char *array,             \
+              const GFC_INTEGER_##N *pshift, const GFC_INTEGER_##N *pdim)    \
+  {                                                                          \
+    cshift0 (ret, array, *pshift, pdim ? *pdim : 1,                          \
+            GFC_DESCRIPTOR_SIZE (array));                                    \
+  }                                                                          \
+                                                                             \
+  extern void cshift0_##N##_char (gfc_array_char *, GFC_INTEGER_4,           \
+                                 const gfc_array_char *,                     \
+                                 const GFC_INTEGER_##N *,                    \
+                                 const GFC_INTEGER_##N *, GFC_INTEGER_4);    \
+  export_proto(cshift0_##N##_char);                                          \
+                                                                             \
+  void                                                                       \
+  cshift0_##N##_char (gfc_array_char *ret,                                   \
+                     GFC_INTEGER_4 ret_length __attribute__((unused)),       \
+                     const gfc_array_char *array,                            \
+                     const GFC_INTEGER_##N *pshift,                          \
+                     const GFC_INTEGER_##N *pdim,                            \
+                     GFC_INTEGER_4 array_length)                             \
+  {                                                                          \
+    cshift0 (ret, array, *pshift, pdim ? *pdim : 1, array_length);           \
+  }
 
-void
-__cshift0_4 (const gfc_array_char * ret, const gfc_array_char * array,
-    const GFC_INTEGER_4 * pshift, const GFC_INTEGER_4 * pdim)
-{
-  __cshift0 (ret, array, *pshift, pdim ? *pdim : 1);
-}
-
-
-void
-__cshift0_8 (const gfc_array_char * ret, const gfc_array_char * array,
-    const GFC_INTEGER_8 * pshift, const GFC_INTEGER_8 * pdim)
-{
-  __cshift0 (ret, array, *pshift, pdim ? *pdim : 1);
-}
-
+DEFINE_CSHIFT (1);
+DEFINE_CSHIFT (2);
+DEFINE_CSHIFT (4);
+DEFINE_CSHIFT (8);