xref: /netbsd-src/external/gpl3/gcc/dist/libgfortran/generated/spread_i2.c (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1181254a7Smrg /* Special implementation of the SPREAD intrinsic
2*b1e83836Smrg    Copyright (C) 2008-2022 Free Software Foundation, Inc.
3181254a7Smrg    Contributed by Thomas Koenig <tkoenig@gcc.gnu.org>, based on
4181254a7Smrg    spread_generic.c written by Paul Brook <paul@nowt.org>
5181254a7Smrg 
6181254a7Smrg This file is part of the GNU Fortran runtime library (libgfortran).
7181254a7Smrg 
8181254a7Smrg Libgfortran is free software; you can redistribute it and/or
9181254a7Smrg modify it under the terms of the GNU General Public
10181254a7Smrg License as published by the Free Software Foundation; either
11181254a7Smrg version 3 of the License, or (at your option) any later version.
12181254a7Smrg 
13181254a7Smrg Ligbfortran is distributed in the hope that it will be useful,
14181254a7Smrg but WITHOUT ANY WARRANTY; without even the implied warranty of
15181254a7Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16181254a7Smrg GNU General Public License for more details.
17181254a7Smrg 
18181254a7Smrg Under Section 7 of GPL version 3, you are granted additional
19181254a7Smrg permissions described in the GCC Runtime Library Exception, version
20181254a7Smrg 3.1, as published by the Free Software Foundation.
21181254a7Smrg 
22181254a7Smrg You should have received a copy of the GNU General Public License and
23181254a7Smrg a copy of the GCC Runtime Library Exception along with this program;
24181254a7Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
25181254a7Smrg <http://www.gnu.org/licenses/>.  */
26181254a7Smrg 
27181254a7Smrg #include "libgfortran.h"
28181254a7Smrg #include <string.h>
29181254a7Smrg 
30181254a7Smrg 
31181254a7Smrg #if defined (HAVE_GFC_INTEGER_2)
32181254a7Smrg 
33181254a7Smrg void
spread_i2(gfc_array_i2 * ret,const gfc_array_i2 * source,const index_type along,const index_type pncopies)34181254a7Smrg spread_i2 (gfc_array_i2 *ret, const gfc_array_i2 *source,
35181254a7Smrg 		 const index_type along, const index_type pncopies)
36181254a7Smrg {
37181254a7Smrg   /* r.* indicates the return array.  */
38181254a7Smrg   index_type rstride[GFC_MAX_DIMENSIONS];
39181254a7Smrg   index_type rstride0;
40181254a7Smrg   index_type rdelta = 0;
41181254a7Smrg   index_type rrank;
42181254a7Smrg   index_type rs;
43181254a7Smrg   GFC_INTEGER_2 *rptr;
44181254a7Smrg   GFC_INTEGER_2 * restrict dest;
45181254a7Smrg   /* s.* indicates the source array.  */
46181254a7Smrg   index_type sstride[GFC_MAX_DIMENSIONS];
47181254a7Smrg   index_type sstride0;
48181254a7Smrg   index_type srank;
49181254a7Smrg   const GFC_INTEGER_2 *sptr;
50181254a7Smrg 
51181254a7Smrg   index_type count[GFC_MAX_DIMENSIONS];
52181254a7Smrg   index_type extent[GFC_MAX_DIMENSIONS];
53181254a7Smrg   index_type n;
54181254a7Smrg   index_type dim;
55181254a7Smrg   index_type ncopies;
56181254a7Smrg 
57181254a7Smrg   srank = GFC_DESCRIPTOR_RANK(source);
58181254a7Smrg 
59181254a7Smrg   rrank = srank + 1;
60181254a7Smrg   if (rrank > GFC_MAX_DIMENSIONS)
61181254a7Smrg     runtime_error ("return rank too large in spread()");
62181254a7Smrg 
63181254a7Smrg   if (along > rrank)
64181254a7Smrg       runtime_error ("dim outside of rank in spread()");
65181254a7Smrg 
66181254a7Smrg   ncopies = pncopies;
67181254a7Smrg 
68181254a7Smrg   if (ret->base_addr == NULL)
69181254a7Smrg     {
70181254a7Smrg 
71181254a7Smrg       size_t ub, stride;
72181254a7Smrg 
73181254a7Smrg       /* The front end has signalled that we need to populate the
74181254a7Smrg 	 return array descriptor.  */
75181254a7Smrg       ret->dtype.rank = rrank;
76181254a7Smrg 
77181254a7Smrg       dim = 0;
78181254a7Smrg       rs = 1;
79181254a7Smrg       for (n = 0; n < rrank; n++)
80181254a7Smrg 	{
81181254a7Smrg 	  stride = rs;
82181254a7Smrg 	  if (n == along - 1)
83181254a7Smrg 	    {
84181254a7Smrg 	      ub = ncopies - 1;
85181254a7Smrg 	      rdelta = rs;
86181254a7Smrg 	      rs *= ncopies;
87181254a7Smrg 	    }
88181254a7Smrg 	  else
89181254a7Smrg 	    {
90181254a7Smrg 	      count[dim] = 0;
91181254a7Smrg 	      extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
92181254a7Smrg 	      sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
93181254a7Smrg 	      rstride[dim] = rs;
94181254a7Smrg 
95181254a7Smrg 	      ub = extent[dim] - 1;
96181254a7Smrg 	      rs *= extent[dim];
97181254a7Smrg 	      dim++;
98181254a7Smrg 	    }
99181254a7Smrg 	  GFC_DIMENSION_SET(ret->dim[n], 0, ub, stride);
100181254a7Smrg 	}
101181254a7Smrg       ret->offset = 0;
102181254a7Smrg 
103181254a7Smrg       /* xmallocarray allocates a single byte for zero size.  */
104181254a7Smrg       ret->base_addr = xmallocarray (rs, sizeof(GFC_INTEGER_2));
105181254a7Smrg       if (rs <= 0)
106181254a7Smrg         return;
107181254a7Smrg     }
108181254a7Smrg   else
109181254a7Smrg     {
110181254a7Smrg       int zero_sized;
111181254a7Smrg 
112181254a7Smrg       zero_sized = 0;
113181254a7Smrg 
114181254a7Smrg       dim = 0;
115181254a7Smrg       if (GFC_DESCRIPTOR_RANK(ret) != rrank)
116181254a7Smrg 	runtime_error ("rank mismatch in spread()");
117181254a7Smrg 
118181254a7Smrg       if (unlikely (compile_options.bounds_check))
119181254a7Smrg 	{
120181254a7Smrg 	  for (n = 0; n < rrank; n++)
121181254a7Smrg 	    {
122181254a7Smrg 	      index_type ret_extent;
123181254a7Smrg 
124181254a7Smrg 	      ret_extent = GFC_DESCRIPTOR_EXTENT(ret,n);
125181254a7Smrg 	      if (n == along - 1)
126181254a7Smrg 		{
127181254a7Smrg 		  rdelta = GFC_DESCRIPTOR_STRIDE(ret,n);
128181254a7Smrg 
129181254a7Smrg 		  if (ret_extent != ncopies)
130181254a7Smrg 		    runtime_error("Incorrect extent in return value of SPREAD"
131181254a7Smrg 				  " intrinsic in dimension %ld: is %ld,"
132181254a7Smrg 				  " should be %ld", (long int) n+1,
133181254a7Smrg 				  (long int) ret_extent, (long int) ncopies);
134181254a7Smrg 		}
135181254a7Smrg 	      else
136181254a7Smrg 		{
137181254a7Smrg 		  count[dim] = 0;
138181254a7Smrg 		  extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
139181254a7Smrg 		  if (ret_extent != extent[dim])
140181254a7Smrg 		    runtime_error("Incorrect extent in return value of SPREAD"
141181254a7Smrg 				  " intrinsic in dimension %ld: is %ld,"
142181254a7Smrg 				  " should be %ld", (long int) n+1,
143181254a7Smrg 				  (long int) ret_extent,
144181254a7Smrg 				  (long int) extent[dim]);
145181254a7Smrg 
146181254a7Smrg 		  if (extent[dim] <= 0)
147181254a7Smrg 		    zero_sized = 1;
148181254a7Smrg 		  sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
149181254a7Smrg 		  rstride[dim] = GFC_DESCRIPTOR_STRIDE(ret,n);
150181254a7Smrg 		  dim++;
151181254a7Smrg 		}
152181254a7Smrg 	    }
153181254a7Smrg 	}
154181254a7Smrg       else
155181254a7Smrg 	{
156181254a7Smrg 	  for (n = 0; n < rrank; n++)
157181254a7Smrg 	    {
158181254a7Smrg 	      if (n == along - 1)
159181254a7Smrg 		{
160181254a7Smrg 		  rdelta = GFC_DESCRIPTOR_STRIDE(ret,n);
161181254a7Smrg 		}
162181254a7Smrg 	      else
163181254a7Smrg 		{
164181254a7Smrg 		  count[dim] = 0;
165181254a7Smrg 		  extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
166181254a7Smrg 		  if (extent[dim] <= 0)
167181254a7Smrg 		    zero_sized = 1;
168181254a7Smrg 		  sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
169181254a7Smrg 		  rstride[dim] = GFC_DESCRIPTOR_STRIDE(ret,n);
170181254a7Smrg 		  dim++;
171181254a7Smrg 		}
172181254a7Smrg 	    }
173181254a7Smrg 	}
174181254a7Smrg 
175181254a7Smrg       if (zero_sized)
176181254a7Smrg 	return;
177181254a7Smrg 
178181254a7Smrg       if (sstride[0] == 0)
179181254a7Smrg 	sstride[0] = 1;
180181254a7Smrg     }
181181254a7Smrg   sstride0 = sstride[0];
182181254a7Smrg   rstride0 = rstride[0];
183181254a7Smrg   rptr = ret->base_addr;
184181254a7Smrg   sptr = source->base_addr;
185181254a7Smrg 
186181254a7Smrg   while (sptr)
187181254a7Smrg     {
188181254a7Smrg       /* Spread this element.  */
189181254a7Smrg       dest = rptr;
190181254a7Smrg       for (n = 0; n < ncopies; n++)
191181254a7Smrg         {
192181254a7Smrg 	  *dest = *sptr;
193181254a7Smrg           dest += rdelta;
194181254a7Smrg         }
195181254a7Smrg       /* Advance to the next element.  */
196181254a7Smrg       sptr += sstride0;
197181254a7Smrg       rptr += rstride0;
198181254a7Smrg       count[0]++;
199181254a7Smrg       n = 0;
200181254a7Smrg       while (count[n] == extent[n])
201181254a7Smrg         {
202181254a7Smrg           /* When we get to the end of a dimension, reset it and increment
203181254a7Smrg              the next dimension.  */
204181254a7Smrg           count[n] = 0;
205181254a7Smrg           /* We could precalculate these products, but this is a less
206181254a7Smrg              frequently used path so probably not worth it.  */
207181254a7Smrg           sptr -= sstride[n] * extent[n];
208181254a7Smrg           rptr -= rstride[n] * extent[n];
209181254a7Smrg           n++;
210181254a7Smrg           if (n >= srank)
211181254a7Smrg             {
212181254a7Smrg               /* Break out of the loop.  */
213181254a7Smrg               sptr = NULL;
214181254a7Smrg               break;
215181254a7Smrg             }
216181254a7Smrg           else
217181254a7Smrg             {
218181254a7Smrg               count[n]++;
219181254a7Smrg               sptr += sstride[n];
220181254a7Smrg               rptr += rstride[n];
221181254a7Smrg             }
222181254a7Smrg         }
223181254a7Smrg     }
224181254a7Smrg }
225181254a7Smrg 
226181254a7Smrg /* This version of spread_internal treats the special case of a scalar
227181254a7Smrg    source.  This is much simpler than the more general case above.  */
228181254a7Smrg 
229181254a7Smrg void
spread_scalar_i2(gfc_array_i2 * ret,const GFC_INTEGER_2 * source,const index_type along,const index_type ncopies)230181254a7Smrg spread_scalar_i2 (gfc_array_i2 *ret, const GFC_INTEGER_2 *source,
231181254a7Smrg 			const index_type along, const index_type ncopies)
232181254a7Smrg {
233181254a7Smrg   GFC_INTEGER_2 * restrict dest;
234181254a7Smrg   index_type stride;
235181254a7Smrg 
236181254a7Smrg   if (GFC_DESCRIPTOR_RANK (ret) != 1)
237181254a7Smrg     runtime_error ("incorrect destination rank in spread()");
238181254a7Smrg 
239181254a7Smrg   if (along > 1)
240181254a7Smrg     runtime_error ("dim outside of rank in spread()");
241181254a7Smrg 
242181254a7Smrg   if (ret->base_addr == NULL)
243181254a7Smrg     {
244181254a7Smrg       ret->base_addr = xmallocarray (ncopies, sizeof (GFC_INTEGER_2));
245181254a7Smrg       ret->offset = 0;
246181254a7Smrg       GFC_DIMENSION_SET(ret->dim[0], 0, ncopies - 1, 1);
247181254a7Smrg     }
248181254a7Smrg   else
249181254a7Smrg     {
250181254a7Smrg       if (ncopies - 1 > (GFC_DESCRIPTOR_EXTENT(ret,0) - 1)
251181254a7Smrg 			   / GFC_DESCRIPTOR_STRIDE(ret,0))
252181254a7Smrg 	runtime_error ("dim too large in spread()");
253181254a7Smrg     }
254181254a7Smrg 
255181254a7Smrg   dest = ret->base_addr;
256181254a7Smrg   stride = GFC_DESCRIPTOR_STRIDE(ret,0);
257181254a7Smrg 
258181254a7Smrg   for (index_type n = 0; n < ncopies; n++)
259181254a7Smrg     {
260181254a7Smrg       *dest = *source;
261181254a7Smrg       dest += stride;
262181254a7Smrg     }
263181254a7Smrg }
264181254a7Smrg 
265181254a7Smrg #endif
266181254a7Smrg 
267