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_REAL_4)
32181254a7Smrg
33181254a7Smrg void
spread_r4(gfc_array_r4 * ret,const gfc_array_r4 * source,const index_type along,const index_type pncopies)34181254a7Smrg spread_r4 (gfc_array_r4 *ret, const gfc_array_r4 *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_REAL_4 *rptr;
44181254a7Smrg GFC_REAL_4 * 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_REAL_4 *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_REAL_4));
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_r4(gfc_array_r4 * ret,const GFC_REAL_4 * source,const index_type along,const index_type ncopies)230181254a7Smrg spread_scalar_r4 (gfc_array_r4 *ret, const GFC_REAL_4 *source,
231181254a7Smrg const index_type along, const index_type ncopies)
232181254a7Smrg {
233181254a7Smrg GFC_REAL_4 * 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_REAL_4));
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