1 /* Implementation of the MATMUL intrinsic
2 Copyright 2002, 2005, 2006, 2007 Free Software Foundation, Inc.
3 Contributed by Paul Brook <paul@nowt.org>
5 This file is part of the GNU Fortran 95 runtime library (libgfortran).
7 Libgfortran is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 In addition to the permissions in the GNU General Public License, the
13 Free Software Foundation gives you unlimited permission to link the
14 compiled version of this file into combinations with other programs,
15 and to distribute those combinations without any restriction coming
16 from the use of this file. (The General Public License restrictions
17 do apply in other respects; for example, they cover modification of
18 the file, and distribution when not linked into a combine
21 Libgfortran is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public
27 License along with libgfortran; see the file COPYING. If not,
28 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29 Boston, MA 02110-1301, USA. */
31 #include "libgfortran.h"
37 #if defined (HAVE_GFC_REAL_4)
39 /* Prototype for the BLAS ?gemm subroutine, a pointer to which can be
40 passed to us by the front-end, in which case we'll call it for large
43 typedef void (*blas_call
)(const char *, const char *, const int *, const int *,
44 const int *, const GFC_REAL_4
*, const GFC_REAL_4
*,
45 const int *, const GFC_REAL_4
*, const int *,
46 const GFC_REAL_4
*, GFC_REAL_4
*, const int *,
49 /* The order of loops is different in the case of plain matrix
50 multiplication C=MATMUL(A,B), and in the frequent special case where
51 the argument A is the temporary result of a TRANSPOSE intrinsic:
52 C=MATMUL(TRANSPOSE(A),B). Transposed temporaries are detected by
53 looking at their strides.
55 The equivalent Fortran pseudo-code is:
57 DIMENSION A(M,COUNT), B(COUNT,N), C(M,N)
58 IF (.NOT.IS_TRANSPOSED(A)) THEN
63 C(I,J) = C(I,J)+A(I,K)*B(K,J)
74 /* If try_blas is set to a nonzero value, then the matmul function will
75 see if there is a way to perform the matrix multiplication by a call
76 to the BLAS gemm function. */
78 extern void matmul_r4 (gfc_array_r4
* const restrict retarray
,
79 gfc_array_r4
* const restrict a
, gfc_array_r4
* const restrict b
, int try_blas
,
80 int blas_limit
, blas_call gemm
);
81 export_proto(matmul_r4
);
84 matmul_r4 (gfc_array_r4
* const restrict retarray
,
85 gfc_array_r4
* const restrict a
, gfc_array_r4
* const restrict b
, int try_blas
,
86 int blas_limit
, blas_call gemm
)
88 const GFC_REAL_4
* restrict abase
;
89 const GFC_REAL_4
* restrict bbase
;
90 GFC_REAL_4
* restrict dest
;
92 index_type rxstride
, rystride
, axstride
, aystride
, bxstride
, bystride
;
93 index_type x
, y
, n
, count
, xcount
, ycount
;
95 assert (GFC_DESCRIPTOR_RANK (a
) == 2
96 || GFC_DESCRIPTOR_RANK (b
) == 2);
98 /* C[xcount,ycount] = A[xcount, count] * B[count,ycount]
100 Either A or B (but not both) can be rank 1:
102 o One-dimensional argument A is implicitly treated as a row matrix
103 dimensioned [1,count], so xcount=1.
105 o One-dimensional argument B is implicitly treated as a column matrix
106 dimensioned [count, 1], so ycount=1.
109 if (retarray
->data
== NULL
)
111 if (GFC_DESCRIPTOR_RANK (a
) == 1)
113 retarray
->dim
[0].lbound
= 0;
114 retarray
->dim
[0].ubound
= b
->dim
[1].ubound
- b
->dim
[1].lbound
;
115 retarray
->dim
[0].stride
= 1;
117 else if (GFC_DESCRIPTOR_RANK (b
) == 1)
119 retarray
->dim
[0].lbound
= 0;
120 retarray
->dim
[0].ubound
= a
->dim
[0].ubound
- a
->dim
[0].lbound
;
121 retarray
->dim
[0].stride
= 1;
125 retarray
->dim
[0].lbound
= 0;
126 retarray
->dim
[0].ubound
= a
->dim
[0].ubound
- a
->dim
[0].lbound
;
127 retarray
->dim
[0].stride
= 1;
129 retarray
->dim
[1].lbound
= 0;
130 retarray
->dim
[1].ubound
= b
->dim
[1].ubound
- b
->dim
[1].lbound
;
131 retarray
->dim
[1].stride
= retarray
->dim
[0].ubound
+1;
135 = internal_malloc_size (sizeof (GFC_REAL_4
) * size0 ((array_t
*) retarray
));
136 retarray
->offset
= 0;
140 if (GFC_DESCRIPTOR_RANK (retarray
) == 1)
142 /* One-dimensional result may be addressed in the code below
143 either as a row or a column matrix. We want both cases to
145 rxstride
= rystride
= retarray
->dim
[0].stride
;
149 rxstride
= retarray
->dim
[0].stride
;
150 rystride
= retarray
->dim
[1].stride
;
154 if (GFC_DESCRIPTOR_RANK (a
) == 1)
156 /* Treat it as a a row matrix A[1,count]. */
157 axstride
= a
->dim
[0].stride
;
161 count
= a
->dim
[0].ubound
+ 1 - a
->dim
[0].lbound
;
165 axstride
= a
->dim
[0].stride
;
166 aystride
= a
->dim
[1].stride
;
168 count
= a
->dim
[1].ubound
+ 1 - a
->dim
[1].lbound
;
169 xcount
= a
->dim
[0].ubound
+ 1 - a
->dim
[0].lbound
;
172 if (count
!= b
->dim
[0].ubound
+ 1 - b
->dim
[0].lbound
)
173 runtime_error ("dimension of array B incorrect in MATMUL intrinsic");
175 if (GFC_DESCRIPTOR_RANK (b
) == 1)
177 /* Treat it as a column matrix B[count,1] */
178 bxstride
= b
->dim
[0].stride
;
180 /* bystride should never be used for 1-dimensional b.
181 in case it is we want it to cause a segfault, rather than
182 an incorrect result. */
183 bystride
= 0xDEADBEEF;
188 bxstride
= b
->dim
[0].stride
;
189 bystride
= b
->dim
[1].stride
;
190 ycount
= b
->dim
[1].ubound
+ 1 - b
->dim
[1].lbound
;
195 dest
= retarray
->data
;
198 /* Now that everything is set up, we're performing the multiplication
201 #define POW3(x) (((float) (x)) * ((float) (x)) * ((float) (x)))
203 if (try_blas
&& rxstride
== 1 && (axstride
== 1 || aystride
== 1)
204 && (bxstride
== 1 || bystride
== 1)
205 && (((float) xcount
) * ((float) ycount
) * ((float) count
)
208 const int m
= xcount
, n
= ycount
, k
= count
, ldc
= rystride
;
209 const GFC_REAL_4 one
= 1, zero
= 0;
210 const int lda
= (axstride
== 1) ? aystride
: axstride
,
211 ldb
= (bxstride
== 1) ? bystride
: bxstride
;
213 if (lda
> 0 && ldb
> 0 && ldc
> 0 && m
> 1 && n
> 1 && k
> 1)
215 assert (gemm
!= NULL
);
216 gemm (axstride
== 1 ? "N" : "T", bxstride
== 1 ? "N" : "T", &m
, &n
, &k
,
217 &one
, abase
, &lda
, bbase
, &ldb
, &zero
, dest
, &ldc
, 1, 1);
222 if (rxstride
== 1 && axstride
== 1 && bxstride
== 1)
224 const GFC_REAL_4
* restrict bbase_y
;
225 GFC_REAL_4
* restrict dest_y
;
226 const GFC_REAL_4
* restrict abase_n
;
229 if (rystride
== xcount
)
230 memset (dest
, 0, (sizeof (GFC_REAL_4
) * xcount
* ycount
));
233 for (y
= 0; y
< ycount
; y
++)
234 for (x
= 0; x
< xcount
; x
++)
235 dest
[x
+ y
*rystride
] = (GFC_REAL_4
)0;
238 for (y
= 0; y
< ycount
; y
++)
240 bbase_y
= bbase
+ y
*bystride
;
241 dest_y
= dest
+ y
*rystride
;
242 for (n
= 0; n
< count
; n
++)
244 abase_n
= abase
+ n
*aystride
;
245 bbase_yn
= bbase_y
[n
];
246 for (x
= 0; x
< xcount
; x
++)
248 dest_y
[x
] += abase_n
[x
] * bbase_yn
;
253 else if (rxstride
== 1 && aystride
== 1 && bxstride
== 1)
255 if (GFC_DESCRIPTOR_RANK (a
) != 1)
257 const GFC_REAL_4
*restrict abase_x
;
258 const GFC_REAL_4
*restrict bbase_y
;
259 GFC_REAL_4
*restrict dest_y
;
262 for (y
= 0; y
< ycount
; y
++)
264 bbase_y
= &bbase
[y
*bystride
];
265 dest_y
= &dest
[y
*rystride
];
266 for (x
= 0; x
< xcount
; x
++)
268 abase_x
= &abase
[x
*axstride
];
270 for (n
= 0; n
< count
; n
++)
271 s
+= abase_x
[n
] * bbase_y
[n
];
278 const GFC_REAL_4
*restrict bbase_y
;
281 for (y
= 0; y
< ycount
; y
++)
283 bbase_y
= &bbase
[y
*bystride
];
285 for (n
= 0; n
< count
; n
++)
286 s
+= abase
[n
*axstride
] * bbase_y
[n
];
287 dest
[y
*rystride
] = s
;
291 else if (axstride
< aystride
)
293 for (y
= 0; y
< ycount
; y
++)
294 for (x
= 0; x
< xcount
; x
++)
295 dest
[x
*rxstride
+ y
*rystride
] = (GFC_REAL_4
)0;
297 for (y
= 0; y
< ycount
; y
++)
298 for (n
= 0; n
< count
; n
++)
299 for (x
= 0; x
< xcount
; x
++)
300 /* dest[x,y] += a[x,n] * b[n,y] */
301 dest
[x
*rxstride
+ y
*rystride
] += abase
[x
*axstride
+ n
*aystride
] * bbase
[n
*bxstride
+ y
*bystride
];
303 else if (GFC_DESCRIPTOR_RANK (a
) == 1)
305 const GFC_REAL_4
*restrict bbase_y
;
308 for (y
= 0; y
< ycount
; y
++)
310 bbase_y
= &bbase
[y
*bystride
];
312 for (n
= 0; n
< count
; n
++)
313 s
+= abase
[n
*axstride
] * bbase_y
[n
*bxstride
];
314 dest
[y
*rxstride
] = s
;
319 const GFC_REAL_4
*restrict abase_x
;
320 const GFC_REAL_4
*restrict bbase_y
;
321 GFC_REAL_4
*restrict dest_y
;
324 for (y
= 0; y
< ycount
; y
++)
326 bbase_y
= &bbase
[y
*bystride
];
327 dest_y
= &dest
[y
*rystride
];
328 for (x
= 0; x
< xcount
; x
++)
330 abase_x
= &abase
[x
*axstride
];
332 for (n
= 0; n
< count
; n
++)
333 s
+= abase_x
[n
*aystride
] * bbase_y
[n
*bxstride
];
334 dest_y
[x
*rxstride
] = s
;