1627f7eb2Smrg /* Special implementation of the SPREAD intrinsic
2*4c3eb207Smrg Copyright (C) 2008-2020 Free Software Foundation, Inc.
3627f7eb2Smrg Contributed by Thomas Koenig <tkoenig@gcc.gnu.org>, based on
4627f7eb2Smrg spread_generic.c written by Paul Brook <paul@nowt.org>
5627f7eb2Smrg
6627f7eb2Smrg This file is part of the GNU Fortran runtime library (libgfortran).
7627f7eb2Smrg
8627f7eb2Smrg Libgfortran is free software; you can redistribute it and/or
9627f7eb2Smrg modify it under the terms of the GNU General Public
10627f7eb2Smrg License as published by the Free Software Foundation; either
11627f7eb2Smrg version 3 of the License, or (at your option) any later version.
12627f7eb2Smrg
13627f7eb2Smrg Ligbfortran is distributed in the hope that it will be useful,
14627f7eb2Smrg but WITHOUT ANY WARRANTY; without even the implied warranty of
15627f7eb2Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16627f7eb2Smrg GNU General Public License for more details.
17627f7eb2Smrg
18627f7eb2Smrg Under Section 7 of GPL version 3, you are granted additional
19627f7eb2Smrg permissions described in the GCC Runtime Library Exception, version
20627f7eb2Smrg 3.1, as published by the Free Software Foundation.
21627f7eb2Smrg
22627f7eb2Smrg You should have received a copy of the GNU General Public License and
23627f7eb2Smrg a copy of the GCC Runtime Library Exception along with this program;
24627f7eb2Smrg see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25627f7eb2Smrg <http://www.gnu.org/licenses/>. */
26627f7eb2Smrg
27627f7eb2Smrg #include "libgfortran.h"
28627f7eb2Smrg #include <string.h>
29627f7eb2Smrg
30627f7eb2Smrg
31627f7eb2Smrg #if defined (HAVE_GFC_COMPLEX_8)
32627f7eb2Smrg
33627f7eb2Smrg void
spread_c8(gfc_array_c8 * ret,const gfc_array_c8 * source,const index_type along,const index_type pncopies)34627f7eb2Smrg spread_c8 (gfc_array_c8 *ret, const gfc_array_c8 *source,
35627f7eb2Smrg const index_type along, const index_type pncopies)
36627f7eb2Smrg {
37627f7eb2Smrg /* r.* indicates the return array. */
38627f7eb2Smrg index_type rstride[GFC_MAX_DIMENSIONS];
39627f7eb2Smrg index_type rstride0;
40627f7eb2Smrg index_type rdelta = 0;
41627f7eb2Smrg index_type rrank;
42627f7eb2Smrg index_type rs;
43627f7eb2Smrg GFC_COMPLEX_8 *rptr;
44627f7eb2Smrg GFC_COMPLEX_8 * restrict dest;
45627f7eb2Smrg /* s.* indicates the source array. */
46627f7eb2Smrg index_type sstride[GFC_MAX_DIMENSIONS];
47627f7eb2Smrg index_type sstride0;
48627f7eb2Smrg index_type srank;
49627f7eb2Smrg const GFC_COMPLEX_8 *sptr;
50627f7eb2Smrg
51627f7eb2Smrg index_type count[GFC_MAX_DIMENSIONS];
52627f7eb2Smrg index_type extent[GFC_MAX_DIMENSIONS];
53627f7eb2Smrg index_type n;
54627f7eb2Smrg index_type dim;
55627f7eb2Smrg index_type ncopies;
56627f7eb2Smrg
57627f7eb2Smrg srank = GFC_DESCRIPTOR_RANK(source);
58627f7eb2Smrg
59627f7eb2Smrg rrank = srank + 1;
60627f7eb2Smrg if (rrank > GFC_MAX_DIMENSIONS)
61627f7eb2Smrg runtime_error ("return rank too large in spread()");
62627f7eb2Smrg
63627f7eb2Smrg if (along > rrank)
64627f7eb2Smrg runtime_error ("dim outside of rank in spread()");
65627f7eb2Smrg
66627f7eb2Smrg ncopies = pncopies;
67627f7eb2Smrg
68627f7eb2Smrg if (ret->base_addr == NULL)
69627f7eb2Smrg {
70627f7eb2Smrg
71627f7eb2Smrg size_t ub, stride;
72627f7eb2Smrg
73627f7eb2Smrg /* The front end has signalled that we need to populate the
74627f7eb2Smrg return array descriptor. */
75627f7eb2Smrg ret->dtype.rank = rrank;
76627f7eb2Smrg
77627f7eb2Smrg dim = 0;
78627f7eb2Smrg rs = 1;
79627f7eb2Smrg for (n = 0; n < rrank; n++)
80627f7eb2Smrg {
81627f7eb2Smrg stride = rs;
82627f7eb2Smrg if (n == along - 1)
83627f7eb2Smrg {
84627f7eb2Smrg ub = ncopies - 1;
85627f7eb2Smrg rdelta = rs;
86627f7eb2Smrg rs *= ncopies;
87627f7eb2Smrg }
88627f7eb2Smrg else
89627f7eb2Smrg {
90627f7eb2Smrg count[dim] = 0;
91627f7eb2Smrg extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
92627f7eb2Smrg sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
93627f7eb2Smrg rstride[dim] = rs;
94627f7eb2Smrg
95627f7eb2Smrg ub = extent[dim] - 1;
96627f7eb2Smrg rs *= extent[dim];
97627f7eb2Smrg dim++;
98627f7eb2Smrg }
99627f7eb2Smrg GFC_DIMENSION_SET(ret->dim[n], 0, ub, stride);
100627f7eb2Smrg }
101627f7eb2Smrg ret->offset = 0;
102627f7eb2Smrg
103627f7eb2Smrg /* xmallocarray allocates a single byte for zero size. */
104627f7eb2Smrg ret->base_addr = xmallocarray (rs, sizeof(GFC_COMPLEX_8));
105627f7eb2Smrg if (rs <= 0)
106627f7eb2Smrg return;
107627f7eb2Smrg }
108627f7eb2Smrg else
109627f7eb2Smrg {
110627f7eb2Smrg int zero_sized;
111627f7eb2Smrg
112627f7eb2Smrg zero_sized = 0;
113627f7eb2Smrg
114627f7eb2Smrg dim = 0;
115627f7eb2Smrg if (GFC_DESCRIPTOR_RANK(ret) != rrank)
116627f7eb2Smrg runtime_error ("rank mismatch in spread()");
117627f7eb2Smrg
118627f7eb2Smrg if (unlikely (compile_options.bounds_check))
119627f7eb2Smrg {
120627f7eb2Smrg for (n = 0; n < rrank; n++)
121627f7eb2Smrg {
122627f7eb2Smrg index_type ret_extent;
123627f7eb2Smrg
124627f7eb2Smrg ret_extent = GFC_DESCRIPTOR_EXTENT(ret,n);
125627f7eb2Smrg if (n == along - 1)
126627f7eb2Smrg {
127627f7eb2Smrg rdelta = GFC_DESCRIPTOR_STRIDE(ret,n);
128627f7eb2Smrg
129627f7eb2Smrg if (ret_extent != ncopies)
130627f7eb2Smrg runtime_error("Incorrect extent in return value of SPREAD"
131627f7eb2Smrg " intrinsic in dimension %ld: is %ld,"
132627f7eb2Smrg " should be %ld", (long int) n+1,
133627f7eb2Smrg (long int) ret_extent, (long int) ncopies);
134627f7eb2Smrg }
135627f7eb2Smrg else
136627f7eb2Smrg {
137627f7eb2Smrg count[dim] = 0;
138627f7eb2Smrg extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
139627f7eb2Smrg if (ret_extent != extent[dim])
140627f7eb2Smrg runtime_error("Incorrect extent in return value of SPREAD"
141627f7eb2Smrg " intrinsic in dimension %ld: is %ld,"
142627f7eb2Smrg " should be %ld", (long int) n+1,
143627f7eb2Smrg (long int) ret_extent,
144627f7eb2Smrg (long int) extent[dim]);
145627f7eb2Smrg
146627f7eb2Smrg if (extent[dim] <= 0)
147627f7eb2Smrg zero_sized = 1;
148627f7eb2Smrg sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
149627f7eb2Smrg rstride[dim] = GFC_DESCRIPTOR_STRIDE(ret,n);
150627f7eb2Smrg dim++;
151627f7eb2Smrg }
152627f7eb2Smrg }
153627f7eb2Smrg }
154627f7eb2Smrg else
155627f7eb2Smrg {
156627f7eb2Smrg for (n = 0; n < rrank; n++)
157627f7eb2Smrg {
158627f7eb2Smrg if (n == along - 1)
159627f7eb2Smrg {
160627f7eb2Smrg rdelta = GFC_DESCRIPTOR_STRIDE(ret,n);
161627f7eb2Smrg }
162627f7eb2Smrg else
163627f7eb2Smrg {
164627f7eb2Smrg count[dim] = 0;
165627f7eb2Smrg extent[dim] = GFC_DESCRIPTOR_EXTENT(source,dim);
166627f7eb2Smrg if (extent[dim] <= 0)
167627f7eb2Smrg zero_sized = 1;
168627f7eb2Smrg sstride[dim] = GFC_DESCRIPTOR_STRIDE(source,dim);
169627f7eb2Smrg rstride[dim] = GFC_DESCRIPTOR_STRIDE(ret,n);
170627f7eb2Smrg dim++;
171627f7eb2Smrg }
172627f7eb2Smrg }
173627f7eb2Smrg }
174627f7eb2Smrg
175627f7eb2Smrg if (zero_sized)
176627f7eb2Smrg return;
177627f7eb2Smrg
178627f7eb2Smrg if (sstride[0] == 0)
179627f7eb2Smrg sstride[0] = 1;
180627f7eb2Smrg }
181627f7eb2Smrg sstride0 = sstride[0];
182627f7eb2Smrg rstride0 = rstride[0];
183627f7eb2Smrg rptr = ret->base_addr;
184627f7eb2Smrg sptr = source->base_addr;
185627f7eb2Smrg
186627f7eb2Smrg while (sptr)
187627f7eb2Smrg {
188627f7eb2Smrg /* Spread this element. */
189627f7eb2Smrg dest = rptr;
190627f7eb2Smrg for (n = 0; n < ncopies; n++)
191627f7eb2Smrg {
192627f7eb2Smrg *dest = *sptr;
193627f7eb2Smrg dest += rdelta;
194627f7eb2Smrg }
195627f7eb2Smrg /* Advance to the next element. */
196627f7eb2Smrg sptr += sstride0;
197627f7eb2Smrg rptr += rstride0;
198627f7eb2Smrg count[0]++;
199627f7eb2Smrg n = 0;
200627f7eb2Smrg while (count[n] == extent[n])
201627f7eb2Smrg {
202627f7eb2Smrg /* When we get to the end of a dimension, reset it and increment
203627f7eb2Smrg the next dimension. */
204627f7eb2Smrg count[n] = 0;
205627f7eb2Smrg /* We could precalculate these products, but this is a less
206627f7eb2Smrg frequently used path so probably not worth it. */
207627f7eb2Smrg sptr -= sstride[n] * extent[n];
208627f7eb2Smrg rptr -= rstride[n] * extent[n];
209627f7eb2Smrg n++;
210627f7eb2Smrg if (n >= srank)
211627f7eb2Smrg {
212627f7eb2Smrg /* Break out of the loop. */
213627f7eb2Smrg sptr = NULL;
214627f7eb2Smrg break;
215627f7eb2Smrg }
216627f7eb2Smrg else
217627f7eb2Smrg {
218627f7eb2Smrg count[n]++;
219627f7eb2Smrg sptr += sstride[n];
220627f7eb2Smrg rptr += rstride[n];
221627f7eb2Smrg }
222627f7eb2Smrg }
223627f7eb2Smrg }
224627f7eb2Smrg }
225627f7eb2Smrg
226627f7eb2Smrg /* This version of spread_internal treats the special case of a scalar
227627f7eb2Smrg source. This is much simpler than the more general case above. */
228627f7eb2Smrg
229627f7eb2Smrg void
spread_scalar_c8(gfc_array_c8 * ret,const GFC_COMPLEX_8 * source,const index_type along,const index_type ncopies)230627f7eb2Smrg spread_scalar_c8 (gfc_array_c8 *ret, const GFC_COMPLEX_8 *source,
231627f7eb2Smrg const index_type along, const index_type ncopies)
232627f7eb2Smrg {
233627f7eb2Smrg GFC_COMPLEX_8 * restrict dest;
234627f7eb2Smrg index_type stride;
235627f7eb2Smrg
236627f7eb2Smrg if (GFC_DESCRIPTOR_RANK (ret) != 1)
237627f7eb2Smrg runtime_error ("incorrect destination rank in spread()");
238627f7eb2Smrg
239627f7eb2Smrg if (along > 1)
240627f7eb2Smrg runtime_error ("dim outside of rank in spread()");
241627f7eb2Smrg
242627f7eb2Smrg if (ret->base_addr == NULL)
243627f7eb2Smrg {
244627f7eb2Smrg ret->base_addr = xmallocarray (ncopies, sizeof (GFC_COMPLEX_8));
245627f7eb2Smrg ret->offset = 0;
246627f7eb2Smrg GFC_DIMENSION_SET(ret->dim[0], 0, ncopies - 1, 1);
247627f7eb2Smrg }
248627f7eb2Smrg else
249627f7eb2Smrg {
250627f7eb2Smrg if (ncopies - 1 > (GFC_DESCRIPTOR_EXTENT(ret,0) - 1)
251627f7eb2Smrg / GFC_DESCRIPTOR_STRIDE(ret,0))
252627f7eb2Smrg runtime_error ("dim too large in spread()");
253627f7eb2Smrg }
254627f7eb2Smrg
255627f7eb2Smrg dest = ret->base_addr;
256627f7eb2Smrg stride = GFC_DESCRIPTOR_STRIDE(ret,0);
257627f7eb2Smrg
258627f7eb2Smrg for (index_type n = 0; n < ncopies; n++)
259627f7eb2Smrg {
260627f7eb2Smrg *dest = *source;
261627f7eb2Smrg dest += stride;
262627f7eb2Smrg }
263627f7eb2Smrg }
264627f7eb2Smrg
265627f7eb2Smrg #endif
266627f7eb2Smrg
267