Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ax_helm_compr_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_AX_HELM_COMPR_KERNEL_H__
2#define __MATH_AX_HELM_COMPR_KERNEL_H__
3
4/*
5 Copyright (c) 2021-2024, The Neko Authors
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 * Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14
15 * Redistributions in binary form must reproduce the above
16 copyright notice, this list of conditions and the following
17 disclaimer in the documentation and/or other materials provided
18 with the distribution.
19
20 * Neither the name of the authors nor the names of its
21 contributors may be used to endorse or promote products derived
22 from this software without specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 POSSIBILITY OF SUCH DAMAGE.
36*/
37
38#include "elem_block.h"
39#include "mfma_kernel.h"
40
55template< typename T, const int LX, const int CHUNKS >
57 const T * __restrict__ u,
58 const T * __restrict__ dx,
59 const T * __restrict__ dy,
60 const T * __restrict__ dz,
61 const T * __restrict__ dxt,
62 const T * __restrict__ dyt,
63 const T * __restrict__ dzt,
64 const T * __restrict__ h1,
65 const T * __restrict__ g11,
66 const T * __restrict__ g22,
67 const T * __restrict__ g33,
68 const T * __restrict__ g12,
69 const T * __restrict__ g13,
70 const T * __restrict__ g23,
71 const int * __restrict__ ind) {
72
76
80
85
86 const int e = blockIdx.x;
87 /* This element's copy of the geometric factors, ind is 1-based */
88 const int ec = ind[e] - 1;
89 const int iii = threadIdx.x;
90 const int nchunks = (LX * LX * LX - 1)/CHUNKS + 1;
91
92 if (iii<LX*LX) {
93 shdx[iii] = dx[iii];
94 shdy[iii] = dy[iii];
95 shdz[iii] = dz[iii];
96 }
97
98 {
99 int i = iii;
100 while (i < LX * LX * LX){
101 shu[i] = u[i+e*LX*LX*LX];
102 i = i + CHUNKS;
103 }
104 }
105
107
108 if (iii<LX*LX){
109 shdxt[iii] = dxt[iii];
110 shdyt[iii] = dyt[iii];
111 shdzt[iii] = dzt[iii];
112 }
113
114 for (int n=0; n<nchunks; n++){
115 const int ijk = iii+n*CHUNKS;
116 const int jk = ijk/LX;
117 const int i = ijk-jk*LX;
118 const int k = jk/LX;
119 const int j = jk-k*LX;
120 if (i<LX && j<LX && k<LX && ijk < LX*LX*LX){
121 const T G00 = g11[ijk+ec*LX*LX*LX];
122 const T G11 = g22[ijk+ec*LX*LX*LX];
123 const T G22 = g33[ijk+ec*LX*LX*LX];
124 const T G01 = g12[ijk+ec*LX*LX*LX];
125 const T G02 = g13[ijk+ec*LX*LX*LX];
126 const T G12 = g23[ijk+ec*LX*LX*LX];
127 const T H1 = h1[ijk+e*LX*LX*LX];
128 T rtmp = 0.0;
129 T stmp = 0.0;
130 T ttmp = 0.0;
131#pragma unroll
132 for (int l = 0; l<LX; l++){
133 rtmp = rtmp + shdx[i+l*LX] * shu[l+j*LX+k*LX*LX];
134 stmp = stmp + shdy[j+l*LX] * shu[i+l*LX+k*LX*LX];
135 ttmp = ttmp + shdz[k+l*LX] * shu[i+j*LX+l*LX*LX];
136 }
137 shur[ijk] = H1 * (G00 * rtmp + G01 * stmp + G02 * ttmp);
138 shus[ijk] = H1 * (G01 * rtmp + G11 * stmp + G12 * ttmp);
139 shut[ijk] = H1 * (G02 * rtmp + G12 * stmp + G22 * ttmp);
140 }
141 }
142
144
145 for (int n=0; n<nchunks; n++){
146 const int ijk = iii+n*CHUNKS;
147 const int jk = ijk/LX;
148 const int k = jk/LX;
149 const int j = jk-k*LX;
150 const int i = ijk-jk*LX;
151 if (i<LX && j<LX && k<LX && ijk <LX*LX*LX){
152 T wijke = 0.0;
153#pragma unroll
154 for (int l = 0; l<LX; l++){
155 wijke = wijke
156 + shdxt[i+l*LX] * shur[l+j*LX+k*LX*LX]
157 + shdyt[j+l*LX] * shus[i+l*LX+k*LX*LX]
158 + shdzt[k+l*LX] * shut[i+j*LX+l*LX*LX];
159 }
160 w[ijk+e*LX*LX*LX] = wijke;
161 }
162 }
163}
164
165template< typename T, const int LX, const int EB >
168 const T * __restrict__ u,
179 const int * __restrict__ ind,
180 const int nelv) {
181
182 /* Element independent, one copy per block */
183 __shared__ T shdx[LX * LX];
186
187 /* One slice per element in the block */
191
192 static_assert(sizeof(shdx) +
193 sizeof(shdy) +
194 sizeof(shdz) +
195 sizeof(shu) +
196 sizeof(shur) +
197 sizeof(shus)
199 "kstep block exceeds the LDS budget");
200
204
205 /* Threads past the last element still have to reach the block wide
206 barriers below, so clamp their reads and drop their stores rather
207 than returning early. At EB == 1 the grid covers nelv exactly, so all
208 of this bookkeeping is constant folded and the kernel is exactly what
209 it was before blocking */
210 const int eb = (EB == 1) ? 0 : threadIdx.z;
211 const int e_blk = blockIdx.x * EB + eb;
212 const bool active = (EB == 1) ? true : (e_blk < nelv);
213 const int e = active ? e_blk : (nelv - 1);
214 const int j = threadIdx.y;
215 const int i = threadIdx.x;
216 const int ij = i + j*LX;
217 const int sh = eb*LX*LX;
218 const int ele = e*LX*LX*LX;
219 /* This element's copy of the geometric factors, ind is 1-based */
220 const int gele = (ind[e] - 1)*LX*LX*LX;
221
222 if (eb == 0) {
223 shdx[ij] = dx[ij];
224 shdy[ij] = dy[ij];
225 shdz[ij] = dz[ij];
226 }
227
228#pragma unroll
229 for(int k = 0; k < LX; ++k){
230 ru[k] = u[ij + k*LX*LX + ele];
231 rw[k] = 0.0;
232 }
233
234
236#pragma unroll
237 for (int k = 0; k < LX; ++k){
238 const int ijk = ij + k*LX*LX;
239 const T G00 = g11[ijk+gele];
240 const T G11 = g22[ijk+gele];
241 const T G22 = g33[ijk+gele];
242 const T G01 = g12[ijk+gele];
243 const T G02 = g13[ijk+gele];
244 const T G12 = g23[ijk+gele];
245 const T H1 = h1[ijk+ele];
246 T ttmp = 0.0;
247 shu[sh + ij] = ru[k];
248#pragma unroll
249 for (int l = 0; l < LX; l++){
250 ttmp += shdz[k+l*LX] * ru[l];
251 }
253
254 T rtmp = 0.0;
255 T stmp = 0.0;
256#pragma unroll
257 for (int l = 0; l < LX; l++){
258 rtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
259 stmp += shdy[j+l*LX] * shu[sh + i+l*LX];
260 }
261 shur[sh + ij] = H1
262 * (G00 * rtmp
263 + G01 * stmp
264 + G02 * ttmp);
265 shus[sh + ij] = H1
266 * (G01 * rtmp
267 + G11 * stmp
268 + G12 * ttmp);
269 rut = H1
270 * (G02 * rtmp
271 + G12 * stmp
272 + G22 * ttmp);
273
275
276 T wijke = 0.0;
277#pragma unroll
278 for (int l = 0; l < LX; l++){
279 wijke += shur[sh + l+j*LX] * shdx[l+i*LX];
280 rw[l] += rut * shdz[k+l*LX];
281 wijke += shus[sh + i+l*LX] * shdy[l + j*LX];
282 }
283 rw[k] += wijke;
284 }
285 if (active) {
286#pragma unroll
287 for (int k = 0; k < LX; ++k){
288 w[ij + k*LX*LX + ele] = rw[k];
289 }
290 }
291}
292
298template< typename T, const int LX, const int EB >
301 const T * __restrict__ u,
302 const T * __restrict__ dx,
303 const T * __restrict__ dy,
304 const T * __restrict__ dz,
305 const T * __restrict__ h1,
306 const T * __restrict__ g11,
307 const T * __restrict__ g22,
308 const T * __restrict__ g33,
309 const T * __restrict__ g12,
310 const T * __restrict__ g13,
311 const T * __restrict__ g23,
312 const int * __restrict__ ind,
313 const int nelv) {
314
315 /* Element independent, one copy per block */
316 __shared__ T shdx[LX * (LX+1)];
317 __shared__ T shdy[LX * (LX+1)];
318 __shared__ T shdz[LX * (LX+1)];
319
320 /* One slice per element in the block */
321 __shared__ T shu[EB * LX * (LX+1)];
322 __shared__ T shur[EB * LX * LX]; // only accessed using fastest dimension
323 __shared__ T shus[EB * LX * (LX+1)];
324
325 static_assert(sizeof(shdx) +
326 sizeof(shdy) +
327 sizeof(shdz) +
328 sizeof(shu) +
329 sizeof(shur) +
330 sizeof(shus)
332 "kstep block exceeds the LDS budget");
333
334 T ru[LX];
335 T rw[LX];
336 T rut;
337
338 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
339 constant folded away and the kernel is exactly what it was before */
340 const int eb = (EB == 1) ? 0 : threadIdx.z;
341 const int e_blk = blockIdx.x * EB + eb;
342 const bool active = (EB == 1) ? true : (e_blk < nelv);
343 const int e = active ? e_blk : (nelv - 1);
344 const int j = threadIdx.y;
345 const int i = threadIdx.x;
346 const int ij = i + j*LX;
347 const int ij_p = i + j*(LX+1);
348 const int sh = eb*LX*LX;
349 const int sh_p = eb*LX*(LX+1);
350 const int ele = e*LX*LX*LX;
351 /* This element's copy of the geometric factors, ind is 1-based */
352 const int gele = (ind[e] - 1)*LX*LX*LX;
353
354 if (eb == 0) {
355 shdx[ij_p] = dx[ij];
356 shdy[ij_p] = dy[ij];
357 shdz[ij_p] = dz[ij];
358 }
359
360#pragma unroll
361 for(int k = 0; k < LX; ++k){
362 ru[k] = u[ij + k*LX*LX + ele];
363 rw[k] = 0.0;
364 }
365
366
368#pragma unroll
369 for (int k = 0; k < LX; ++k){
370 const int ijk = ij + k*LX*LX;
371 const T G00 = g11[ijk+gele];
372 const T G11 = g22[ijk+gele];
373 const T G22 = g33[ijk+gele];
374 const T G01 = g12[ijk+gele];
375 const T G02 = g13[ijk+gele];
376 const T G12 = g23[ijk+gele];
377 const T H1 = h1[ijk+ele];
378 T ttmp = 0.0;
379 shu[sh_p + ij_p] = ru[k];
380#pragma unroll
381 for (int l = 0; l < LX; l++){
382 ttmp += shdz[k+l*(LX+1)] * ru[l];
383 }
385
386 T rtmp = 0.0;
387 T stmp = 0.0;
388#pragma unroll
389 for (int l = 0; l < LX; l++){
390 rtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
391 stmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
392 }
393 shur[sh + ij] = H1
394 * (G00 * rtmp
395 + G01 * stmp
396 + G02 * ttmp);
397 shus[sh_p + ij_p] = H1
398 * (G01 * rtmp
399 + G11 * stmp
400 + G12 * ttmp);
401 rut = H1
402 * (G02 * rtmp
403 + G12 * stmp
404 + G22 * ttmp);
405
407
408 T wijke = 0.0;
409#pragma unroll
410 for (int l = 0; l < LX; l++){
411 wijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
412 rw[l] += rut * shdz[k+l*(LX+1)];
413 wijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
414 }
415 rw[k] += wijke;
416 }
417 if (active) {
418#pragma unroll
419 for (int k = 0; k < LX; ++k){
420 w[ij + k*LX*LX + ele] = rw[k];
421 }
422 }
423}
424
425
465#if defined(__gfx90a__) || defined(__gfx942__)
466
467/* Matrix-core axhelm for one element of order LX-1 (T = float or double).
468 *
469 * NWF cooperating wavefronts (blockDim = (64, NWF, 1)) process one element,
470 * sharing a single staged cube in LDS. The matrix-core column tiles are
471 * striped across the wavefronts (wf = threadIdx.y) via mfma_contract_sel, while
472 * the staging, geometry and write-back passes parallelise over the WPE*64
473 * threads that serve one element (gtid); only the derivative matrices are
474 * staged by the whole block. The three accumulating divergence contractions
475 * stay separated
476 * by __syncthreads and each wavefront owns disjoint output columns, so the
477 * accumulation is race-free. NWF = 1 reproduces the single-wavefront kernel.
478 *
479 * mfma_contract_sel routes double precision through the batched 4x4x4 matrix
480 * core (full M-utilisation) and single precision through the 16x16x4 tile. */
481template< typename T, const int LX, const int NWF, const int TILE >
483 const T * __restrict__ u,
484 const T * __restrict__ dx,
485 const T * __restrict__ dy,
486 const T * __restrict__ dz,
487 const T * __restrict__ h1,
488 const T * __restrict__ g11,
489 const T * __restrict__ g22,
490 const T * __restrict__ g33,
491 const T * __restrict__ g12,
492 const T * __restrict__ g13,
493 const T * __restrict__ g23,
494 const int * __restrict__ ind,
495 const int nelv) {
496 const int LX2 = LX * LX;
497 const int LX3 = LX * LX * LX;
498
499 /* NWF wavefronts per block, WPE of them cooperating on one element and the
500 block covering EB elements, see the note in mfma_kernel.h. At LX = 4 the
501 contraction offers one column group, so WPE is 1 and every wavefront gets
502 an element of its own rather than idling. */
503 enum { PAD = NEKO_MFMA_PAD_N(LX, sizeof(T)),
504 CUBE = NEKO_MFMA_CUBE_N(LX, sizeof(T)),
505 DMAT = NEKO_MFMA_DMAT_N(LX, sizeof(T)),
506 EB = NEKO_MFMA_EB_N(NWF, LX, sizeof(T)),
507 WPE = NWF / EB,
508 GNTHR = WPE * 64, /* threads serving one element */
509 SPT = NEKO_MFMA_SPT_N(WPE, LX, sizeof(T)),
510 /* Elements the batched single precision tile contracts per
511 call, from the wavefronts of the first element of each
512 batch; one for every other tile, see NEKO_MFMA_NEL_N */
513 NEL = NEKO_MFMA_NEL_N(EB, LX, sizeof(T), TILE),
514 /* Geometry prefetched into registers, or read where it is used?
515 Same budget and the same macro the tuner logs with, see the note
516 on the prefetch below */
517 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
518 NG = GREG ? SPT : 1 };
519 static_assert(WPE * EB == NWF,
520 "wavefronts per block must split evenly over the elements");
521
525 __shared__ T shu[EB * CUBE]; // input u, later reused as output w
526 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
527 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
528 __shared__ T sht[EB * CUBE]; // d/dt -> St
529
530 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
531 sizeof(shu) + sizeof(shr) + sizeof(shs) + sizeof(sht)
533 "mfma block exceeds the shared memory budget");
534
535 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
536 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
537 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
538 const int nthr = NWF * 64;
539
540 const int eb = wf / WPE; // which element this wavefront serves
541 const int sub = wf % WPE; // its rank among that element's waves
542 const int gtid = sub * 64 + lane; // thread id within the element group
543
544 /* Threads past the last element still have to reach the block wide
545 barriers, so clamp their reads and drop their stores rather than
546 returning early. At EB == 1 the grid covers nelv exactly and this is
547 constant folded away */
548 const int e_blk = blockIdx.x * EB + eb;
549 const bool active = (EB == 1) ? true : (e_blk < nelv);
550 const int e = active ? e_blk : (nelv - 1);
551 const int ele = e * LX3;
552 /* This element's copy of the geometric factors, ind is 1-based */
553 const int gele = (ind[e] - 1) * LX3;
554 const int sh = eb * CUBE;
555
556 /* The element point each of this thread's LDS slots holds, or -1 for a pad
557 slot, decoded once and reused by every pass below. The three passes walk
558 slots at unit stride in LDS -- which is what keeps them off each other's
559 banks once the cube is padded -- and this array is the translation back
560 to the caller's LX^3 storage, see mfma_slot_point(). Where the cube is not
561 padded the decode folds away to the identity and this is the old
562 p = gtid + q*GNTHR loop. */
563 int goff[SPT];
564#pragma unroll
565 for (int q = 0; q < SPT; q++) {
566 const int sl = gtid + q * GNTHR;
568 }
569
570 /* Reference derivative matrices, one copy shared by every element. The
571 staged copy carries the padded column stride, so the linear index of the
572 caller's LX x LX copy is decoded here; the global reads stay contiguous
573 and it is the LDS side that gains the stride, which is the point. */
574 for (int p = tid; p < LX2; p += nthr) {
575 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
576 shdx[sd] = dx[p];
577 shdy[sd] = dy[p];
578 shdz[sd] = dz[p];
579 }
580 /* Element-local field, staged by the wavefronts that own it */
581#pragma unroll
582 for (int q = 0; q < SPT; q++) {
583 if (goff[q] >= 0)
584 shu[sh + gtid + q * GNTHR] = u[goff[q] + ele];
585 }
586
588
589 /*
590 * The seven geometric factors, issued here rather than where they are used.
591 *
592 * They are needed by the pointwise pass, which sits behind the gradient
593 * contractions and a barrier, so reading them there exposes the full global
594 * latency with nothing to cover it: this kernel runs at three wavefronts per
595 * SIMD in double precision at LX = 8, a third of what the 1D kernel that
596 * beats it reaches, so there is no other occupancy to hide behind. Issued
597 * here they are in flight across the three contractions instead.
598 *
599 * The cost is 7 * SPT * (sizeof(T)/4) VGPRs, and past a budget that stops
600 * paying -- 189 values at LX = 12 on a single wavefront, against the 256 a
601 * lane addresses -- so it is chosen per instantiation by the same
602 * NEKO_MFMA_VECTOR_GREG the vector kernel uses, and reported by the tuner
603 * for the same reason: it is derived from the wavefront count rather than
604 * swept, so two neighbouring candidates can differ in more than block shape.
605 */
606 T rG00[NG], rG11[NG], rG22[NG];
607 T rG01[NG], rG02[NG], rG12[NG];
608 T rH1[NG];
609
610 if (GREG) {
611#pragma unroll
612 for (int q = 0; q < NG; q++) {
613 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
614 const int gq = (goff[q] >= 0) ? (goff[q] + gele) : gele;
615 rG00[q] = g11[gq];
616 rG11[q] = g22[gq];
617 rG22[q] = g33[gq];
618 rG01[q] = g12[gq];
619 rG02[q] = g13[gq];
620 rG12[q] = g23[gq];
621 rH1[q] = h1[gp];
622 }
623 }
624
625 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout. */
626 if (eb % NEL == 0)
627 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD, NEL>::run(
628 shr + sh, shdx, shu + sh, lane, sub);
629 if (eb % NEL == 0)
630 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD, NEL>::run(
631 shs + sh, shdy, shu + sh, lane, sub);
632 if (eb % NEL == 0)
633 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD, NEL>::run(
634 sht + sh, shdz, shu + sh, lane, sub);
635
637
638 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht. */
639#pragma unroll
640 for (int q = 0; q < SPT; q++) {
641 if (goff[q] >= 0) {
642 const int sl = sh + gtid + q * GNTHR;
643 T G00, G11, G22, G01, G02, G12, H1;
644 if (GREG) {
645 /* GREG is a compile time constant, so only one of these two bodies is
646 emitted; the clamp keeps the index inside the NG == 1 array that the
647 other one never reads from */
648 const int r = GREG ? q : 0;
649 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
650 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
651 H1 = rH1[r];
652 } else {
653 const int gp = goff[q] + ele;
654 const int gq = goff[q] + gele;
655 G00 = g11[gq]; G11 = g22[gq]; G22 = g33[gq];
656 G01 = g12[gq]; G02 = g13[gq]; G12 = g23[gq];
657 H1 = h1[gp];
658 }
659 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
660 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
661 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
662 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
663 }
664 }
665
667
668 /* Divergence: w = Dr^T Sr + Ds^T Ss + Dt^T St, accumulated in shu (= w).
669 The first contraction overwrites rather than accumulates, which is what
670 lets the separate zeroing pass over the cube -- and the barrier after it
671 -- go away entirely; the summation order of the three is unchanged, so
672 this is not a reassociation. All four (TRANSPOSE, ACCUM) pairs are covered
673 by the gfx90a read-out, so the non-accumulating one is no less verified
674 than the accumulating one. */
675 if (eb % NEL == 0)
676 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD, NEL>::run(
677 shu + sh, shdx, shr + sh, lane, sub);
679 if (eb % NEL == 0)
680 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD, NEL>::run(
681 shu + sh, shdy, shs + sh, lane, sub);
683 if (eb % NEL == 0)
684 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD, NEL>::run(
685 shu + sh, shdz, sht + sh, lane, sub);
687
688 if (active) {
689#pragma unroll
690 for (int q = 0; q < SPT; q++) {
691 if (goff[q] >= 0)
692 w[goff[q] + ele] = shu[sh + gtid + q * GNTHR];
693 }
694 }
695}
696#endif // __gfx90a__ || __gfx942__
697
698/*
699 * Compile-time dispatch onto the MFMA element kernel. The launch macros in
700 * ax_helm_compr.hip are written for every LX the operator dispatches and for
701 * whatever `real` is, so every combination has to compile; the ones the
702 * strategy does not cover -- LX outside the supported range, a build without
703 * a matrix-core arch -- resolve to this no-op. The autotuner never selects
704 * the strategy for them, so the no-op is unreachable at runtime, see
705 * mfma_lx_supported() and hip_have_mfma() in mfma_kernel.h.
706 */
707template< typename T, const int LX, const int NWF, const int TILE >
709 __device__ static void run(T *, const T *, const T *, const T *, const T *,
710 const T *, const T *, const T *, const T *,
711 const T *, const T *, const T *,
712 const int *, const int) {}
713};
714
715#if defined(__gfx90a__) || defined(__gfx942__)
716
717/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
718#define NEKO_AX_HELM_COMPR_MFMA_DISPATCH(TYPE, LXV) \
719 template< const int NWF, const int TILE > \
720 struct ax_helm_compr_mfma_dispatch< TYPE, LXV, NWF, TILE > { \
721 __device__ static void run(TYPE *w, const TYPE *u, \
722 const TYPE *dx, const TYPE *dy, \
723 const TYPE *dz, const TYPE *h1, \
724 const TYPE *g11, const TYPE *g22, \
725 const TYPE *g33, const TYPE *g12, \
726 const TYPE *g13, const TYPE *g23, \
727 const int *ind, const int nelv) { \
728 ax_helm_compr_mfma_elem< TYPE, LXV, NWF, TILE >(w, u, dx, dy, dz, h1, \
729 g11, g22, g33, g12, g13, g23, \
730 ind, nelv); \
731 } \
732 }
733
754
755#endif // __gfx90a__ || __gfx942__
756
757/*
758 * Note the bare __launch_bounds__ rather than NEKO_EB_BOUNDS: the kstep
759 * kernels ask for three waves per SIMD, and this kernel was validated on
760 * gfx90a/gfx942 without that constraint. Keep it byte-identical to the
761 * configuration that was confirmed on hardware.
762 */
763template< typename T, const int LX, const int NWF, const int TILE >
766 const T * __restrict__ u,
767 const T * __restrict__ dx,
768 const T * __restrict__ dy,
769 const T * __restrict__ dz,
770 const T * __restrict__ h1,
771 const T * __restrict__ g11,
772 const T * __restrict__ g22,
773 const T * __restrict__ g33,
774 const T * __restrict__ g12,
775 const T * __restrict__ g13,
776 const T * __restrict__ g23,
777 const int * __restrict__ ind,
778 const int nelv) {
779
781 g11, g22, g33,
782 g12, g13, g23,
783 ind, nelv);
784}
785
786/*
787 * Vector versions
788 */
789
790template< typename T, const int LX, const int EB >
795 const T * __restrict__ u,
796 const T * __restrict__ v,
797 const T * __restrict__ w,
798 const T * __restrict__ dx,
799 const T * __restrict__ dy,
800 const T * __restrict__ dz,
801 const T * __restrict__ h1,
802 const T * __restrict__ g11,
803 const T * __restrict__ g22,
804 const T * __restrict__ g33,
805 const T * __restrict__ g12,
806 const T * __restrict__ g13,
807 const T * __restrict__ g23,
808 const int * __restrict__ ind,
809 const int nelv) {
810
811 /* Element independent, one copy per block */
812 __shared__ T shdx[LX * LX];
813 __shared__ T shdy[LX * LX];
814 __shared__ T shdz[LX * LX];
815
816 /* One slice per element in the block */
817 __shared__ T shu[EB * LX * LX];
818 __shared__ T shur[EB * LX * LX];
819 __shared__ T shus[EB * LX * LX];
820
824
828
829 static_assert(sizeof(shdx) +
830 sizeof(shdy) +
831 sizeof(shdz) +
832 sizeof(shu) +
833 sizeof(shur) +
834 sizeof(shus) +
835 sizeof(shv) +
836 sizeof(shvr) +
837 sizeof(shvs) +
838 sizeof(shw) +
839 sizeof(shwr) +
840 sizeof(shws)
842 "kstep block exceeds the LDS budget");
843
844 T ru[LX];
846 T rw[LX];
847
851
852 T rut;
855
856 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
857 constant folded away and the kernel is exactly what it was before */
858 const int eb = (EB == 1) ? 0 : threadIdx.z;
859 const int e_blk = blockIdx.x * EB + eb;
860 const bool active = (EB == 1) ? true : (e_blk < nelv);
861 const int e = active ? e_blk : (nelv - 1);
862 const int j = threadIdx.y;
863 const int i = threadIdx.x;
864 const int ij = i + j*LX;
865 const int sh = eb*LX*LX;
866 const int ele = e*LX*LX*LX;
867 /* This element's copy of the geometric factors, ind is 1-based */
868 const int gele = (ind[e] - 1)*LX*LX*LX;
869
870 if (eb == 0) {
871 shdx[ij] = dx[ij];
872 shdy[ij] = dy[ij];
873 shdz[ij] = dz[ij];
874 }
875
876#pragma unroll
877 for(int k = 0; k < LX; ++k){
878 ru[k] = u[ij + k*LX*LX + ele];
879 ruw[k] = 0.0;
880
881 rv[k] = v[ij + k*LX*LX + ele];
882 rvw[k] = 0.0;
883
884 rw[k] = w[ij + k*LX*LX + ele];
885 rww[k] = 0.0;
886 }
887
888
890#pragma unroll
891 for (int k = 0; k < LX; ++k){
892 const int ijk = ij + k*LX*LX;
893 const T G00 = g11[ijk+gele];
894 const T G11 = g22[ijk+gele];
895 const T G22 = g33[ijk+gele];
896 const T G01 = g12[ijk+gele];
897 const T G02 = g13[ijk+gele];
898 const T G12 = g23[ijk+gele];
899 const T H1 = h1[ijk+ele];
900 T uttmp = 0.0;
901 T vttmp = 0.0;
902 T wttmp = 0.0;
903 shu[sh + ij] = ru[k];
904 shv[sh + ij] = rv[k];
905 shw[sh + ij] = rw[k];
906#pragma unroll
907 for (int l = 0; l < LX; l++){
908 uttmp += shdz[k+l*LX] * ru[l];
909 vttmp += shdz[k+l*LX] * rv[l];
910 wttmp += shdz[k+l*LX] * rw[l];
911 }
913
914 T urtmp = 0.0;
915 T ustmp = 0.0;
916
917 T vrtmp = 0.0;
918 T vstmp = 0.0;
919
920 T wrtmp = 0.0;
921 T wstmp = 0.0;
922#pragma unroll
923 for (int l = 0; l < LX; l++){
924 urtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
925 ustmp += shdy[j+l*LX] * shu[sh + i+l*LX];
926
927 vrtmp += shdx[i+l*LX] * shv[sh + l+j*LX];
928 vstmp += shdy[j+l*LX] * shv[sh + i+l*LX];
929
930 wrtmp += shdx[i+l*LX] * shw[sh + l+j*LX];
931 wstmp += shdy[j+l*LX] * shw[sh + i+l*LX];
932 }
933
934 shur[sh + ij] = H1
935 * (G00 * urtmp
936 + G01 * ustmp
937 + G02 * uttmp);
938 shus[sh + ij] = H1
939 * (G01 * urtmp
940 + G11 * ustmp
941 + G12 * uttmp);
942 rut = H1
943 * (G02 * urtmp
944 + G12 * ustmp
945 + G22 * uttmp);
946
947 shvr[sh + ij] = H1
948 * (G00 * vrtmp
949 + G01 * vstmp
950 + G02 * vttmp);
951 shvs[sh + ij] = H1
952 * (G01 * vrtmp
953 + G11 * vstmp
954 + G12 * vttmp);
955 rvt = H1
956 * (G02 * vrtmp
957 + G12 * vstmp
958 + G22 * vttmp);
959
960 shwr[sh + ij] = H1
961 * (G00 * wrtmp
962 + G01 * wstmp
963 + G02 * wttmp);
964 shws[sh + ij] = H1
965 * (G01 * wrtmp
966 + G11 * wstmp
967 + G12 * wttmp);
968 rwt = H1
969 * (G02 * wrtmp
970 + G12 * wstmp
971 + G22 * wttmp);
972
974
975 T uwijke = 0.0;
976 T vwijke = 0.0;
977 T wwijke = 0.0;
978#pragma unroll
979 for (int l = 0; l < LX; l++){
980 uwijke += shur[sh + l+j*LX] * shdx[l+i*LX];
981 ruw[l] += rut * shdz[k+l*LX];
982 uwijke += shus[sh + i+l*LX] * shdy[l + j*LX];
983
984 vwijke += shvr[sh + l+j*LX] * shdx[l+i*LX];
985 rvw[l] += rvt * shdz[k+l*LX];
986 vwijke += shvs[sh + i+l*LX] * shdy[l + j*LX];
987
988 wwijke += shwr[sh + l+j*LX] * shdx[l+i*LX];
989 rww[l] += rwt * shdz[k+l*LX];
990 wwijke += shws[sh + i+l*LX] * shdy[l + j*LX];
991 }
992 ruw[k] += uwijke;
993 rvw[k] += vwijke;
994 rww[k] += wwijke;
995 }
996 if (active) {
997#pragma unroll
998 for (int k = 0; k < LX; ++k){
999 au[ij + k*LX*LX + ele] = ruw[k];
1000 av[ij + k*LX*LX + ele] = rvw[k];
1001 aw[ij + k*LX*LX + ele] = rww[k];
1002 }
1003 }
1004}
1005
1006template< typename T, const int LX, const int EB >
1009 T * __restrict__ av,
1010 T * __restrict__ aw,
1011 const T * __restrict__ u,
1012 const T * __restrict__ v,
1013 const T * __restrict__ w,
1014 const T * __restrict__ dx,
1015 const T * __restrict__ dy,
1016 const T * __restrict__ dz,
1017 const T * __restrict__ h1,
1018 const T * __restrict__ g11,
1019 const T * __restrict__ g22,
1020 const T * __restrict__ g33,
1021 const T * __restrict__ g12,
1022 const T * __restrict__ g13,
1023 const T * __restrict__ g23,
1024 const int * __restrict__ ind,
1025 const int nelv) {
1026
1027 /* Element independent, one copy per block */
1028 __shared__ T shdx[LX * (LX+1)];
1029 __shared__ T shdy[LX * (LX+1)];
1030 __shared__ T shdz[LX * (LX+1)];
1031
1032 /* One slice per element in the block */
1033 __shared__ T shu[EB * LX * (LX+1)];
1034 __shared__ T shur[EB * LX * LX];
1035 __shared__ T shus[EB * LX * (LX+1)];
1036
1037 __shared__ T shv[EB * LX * (LX+1)];
1038 __shared__ T shvr[EB * LX * LX];
1039 __shared__ T shvs[EB * LX * (LX+1)];
1040
1041 __shared__ T shw[EB * LX * (LX+1)];
1042 __shared__ T shwr[EB * LX * LX];
1043 __shared__ T shws[EB * LX * (LX+1)];
1044
1045 static_assert(sizeof(shdx) +
1046 sizeof(shdy) +
1047 sizeof(shdz) +
1048 sizeof(shu) +
1049 sizeof(shur) +
1050 sizeof(shus) +
1051 sizeof(shv) +
1052 sizeof(shvr) +
1053 sizeof(shvs) +
1054 sizeof(shw) +
1055 sizeof(shwr) +
1056 sizeof(shws)
1057 <= NEKO_EB_MAX_LDS,
1058 "kstep block exceeds the LDS budget");
1059
1060 T ru[LX];
1061 T rv[LX];
1062 T rw[LX];
1063
1064 T ruw[LX];
1065 T rvw[LX];
1066 T rww[LX];
1067
1068 T rut;
1069 T rvt;
1070 T rwt;
1071
1072 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
1073 constant folded away and the kernel is exactly what it was before */
1074 const int eb = (EB == 1) ? 0 : threadIdx.z;
1075 const int e_blk = blockIdx.x * EB + eb;
1076 const bool active = (EB == 1) ? true : (e_blk < nelv);
1077 const int e = active ? e_blk : (nelv - 1);
1078 const int j = threadIdx.y;
1079 const int i = threadIdx.x;
1080 const int ij = i + j*LX;
1081 const int ij_p = i + j*(LX+1);
1082 const int sh = eb*LX*LX;
1083 const int sh_p = eb*LX*(LX+1);
1084 const int ele = e*LX*LX*LX;
1085 /* This element's copy of the geometric factors, ind is 1-based */
1086 const int gele = (ind[e] - 1)*LX*LX*LX;
1087
1088 if (eb == 0) {
1089 shdx[ij_p] = dx[ij];
1090 shdy[ij_p] = dy[ij];
1091 shdz[ij_p] = dz[ij];
1092 }
1093
1094#pragma unroll
1095 for(int k = 0; k < LX; ++k){
1096 ru[k] = u[ij + k*LX*LX + ele];
1097 ruw[k] = 0.0;
1098
1099 rv[k] = v[ij + k*LX*LX + ele];
1100 rvw[k] = 0.0;
1101
1102 rw[k] = w[ij + k*LX*LX + ele];
1103 rww[k] = 0.0;
1104 }
1105
1106
1107 __syncthreads();
1108#pragma unroll
1109 for (int k = 0; k < LX; ++k){
1110 const int ijk = ij + k*LX*LX;
1111 const T G00 = g11[ijk+gele];
1112 const T G11 = g22[ijk+gele];
1113 const T G22 = g33[ijk+gele];
1114 const T G01 = g12[ijk+gele];
1115 const T G02 = g13[ijk+gele];
1116 const T G12 = g23[ijk+gele];
1117 const T H1 = h1[ijk+ele];
1118 T uttmp = 0.0;
1119 T vttmp = 0.0;
1120 T wttmp = 0.0;
1121 shu[sh_p + ij_p] = ru[k];
1122 shv[sh_p + ij_p] = rv[k];
1123 shw[sh_p + ij_p] = rw[k];
1124#pragma unroll
1125 for (int l = 0; l < LX; l++){
1126 uttmp += shdz[k+l*(LX+1)] * ru[l];
1127 vttmp += shdz[k+l*(LX+1)] * rv[l];
1128 wttmp += shdz[k+l*(LX+1)] * rw[l];
1129 }
1131
1132 T urtmp = 0.0;
1133 T ustmp = 0.0;
1134
1135 T vrtmp = 0.0;
1136 T vstmp = 0.0;
1137
1138 T wrtmp = 0.0;
1139 T wstmp = 0.0;
1140#pragma unroll
1141 for (int l = 0; l < LX; l++){
1142 urtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
1143 ustmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
1144
1145 vrtmp += shdx[i+l*(LX+1)] * shv[sh_p + l+j*(LX+1)];
1146 vstmp += shdy[j+l*(LX+1)] * shv[sh_p + i+l*(LX+1)];
1147
1148 wrtmp += shdx[i+l*(LX+1)] * shw[sh_p + l+j*(LX+1)];
1149 wstmp += shdy[j+l*(LX+1)] * shw[sh_p + i+l*(LX+1)];
1150 }
1151
1152 shur[sh + ij] = H1
1153 * (G00 * urtmp
1154 + G01 * ustmp
1155 + G02 * uttmp);
1156 shus[sh_p + ij_p] = H1
1157 * (G01 * urtmp
1158 + G11 * ustmp
1159 + G12 * uttmp);
1160 rut = H1
1161 * (G02 * urtmp
1162 + G12 * ustmp
1163 + G22 * uttmp);
1164
1165 shvr[sh + ij] = H1
1166 * (G00 * vrtmp
1167 + G01 * vstmp
1168 + G02 * vttmp);
1169 shvs[sh_p + ij_p] = H1
1170 * (G01 * vrtmp
1171 + G11 * vstmp
1172 + G12 * vttmp);
1173 rvt = H1
1174 * (G02 * vrtmp
1175 + G12 * vstmp
1176 + G22 * vttmp);
1177
1178 shwr[sh + ij] = H1
1179 * (G00 * wrtmp
1180 + G01 * wstmp
1181 + G02 * wttmp);
1182 shws[sh_p + ij_p] = H1
1183 * (G01 * wrtmp
1184 + G11 * wstmp
1185 + G12 * wttmp);
1186 rwt = H1
1187 * (G02 * wrtmp
1188 + G12 * wstmp
1189 + G22 * wttmp);
1190
1192
1193 T uwijke = 0.0;
1194 T vwijke = 0.0;
1195 T wwijke = 0.0;
1196#pragma unroll
1197 for (int l = 0; l < LX; l++){
1198 uwijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
1199 ruw[l] += rut * shdz[k+l*(LX+1)];
1200 uwijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1201
1202 vwijke += shvr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1203 rvw[l] += rvt * shdz[k+l*(LX+1)];
1204 vwijke += shvs[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1205
1206 wwijke += shwr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1207 rww[l] += rwt * shdz[k+l*(LX+1)];
1208 wwijke += shws[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1209 }
1210 ruw[k] += uwijke;
1211 rvw[k] += vwijke;
1212 rww[k] += wwijke;
1213 }
1214 if (active) {
1215#pragma unroll
1216 for (int k = 0; k < LX; ++k){
1217 au[ij + k*LX*LX + ele] = ruw[k];
1218 av[ij + k*LX*LX + ele] = rvw[k];
1219 aw[ij + k*LX*LX + ele] = rww[k];
1220 }
1221 }
1222}
1223
1256#if defined(__gfx90a__) || defined(__gfx942__)
1257
1258/* Matrix-core vector axhelm for one element of order LX-1 (T = float or
1259 * double), see ax_helm_compr_mfma_elem for the block geometry this shares. */
1260template< typename T, const int LX, const int NWF, const int TILE >
1262 T * __restrict__ av,
1263 T * __restrict__ aw,
1264 const T * __restrict__ u,
1265 const T * __restrict__ v,
1266 const T * __restrict__ w,
1267 const T * __restrict__ dx,
1268 const T * __restrict__ dy,
1269 const T * __restrict__ dz,
1270 const T * __restrict__ h1,
1271 const T * __restrict__ g11,
1272 const T * __restrict__ g22,
1273 const T * __restrict__ g33,
1274 const T * __restrict__ g12,
1275 const T * __restrict__ g13,
1276 const T * __restrict__ g23,
1277 const int * __restrict__ ind,
1278 const int nelv) {
1279 const int LX2 = LX * LX;
1280 const int LX3 = LX * LX * LX;
1281
1282 /* NWF wavefronts per block, WPE of them cooperating on one element and the
1283 block covering EB elements, exactly as in ax_helm_compr_mfma_elem */
1284 enum { PAD = NEKO_MFMA_PAD_N(LX, sizeof(T)),
1285 CUBE = NEKO_MFMA_CUBE_N(LX, sizeof(T)),
1286 DMAT = NEKO_MFMA_DMAT_N(LX, sizeof(T)),
1287 EB = NEKO_MFMA_EB_N(NWF, LX, sizeof(T)),
1288 WPE = NWF / EB,
1289 GNTHR = WPE * 64, /* threads serving one element */
1290 SPT = NEKO_MFMA_SPT_N(WPE, LX, sizeof(T)),
1291 /* Elements the batched single precision tile contracts per
1292 call, from the wavefronts of the first element of each
1293 batch; one for every other tile, see NEKO_MFMA_NEL_N */
1294 NEL = NEKO_MFMA_NEL_N(EB, LX, sizeof(T), TILE),
1295 /* Geometry in registers across the components, or re-read per
1296 component? The same macro the tuner logs with, so the reported
1297 mode cannot drift from the compiled one */
1298 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
1299 /* One slot when the factors are re-read, so the arrays below cost
1300 nothing in that case */
1301 NG = GREG ? SPT : 1 };
1302 static_assert(WPE * EB == NWF,
1303 "wavefronts per block must split evenly over the elements");
1304
1308 __shared__ T shc[EB * CUBE]; // component in, later its result out
1309 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
1310 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
1311 __shared__ T sht[EB * CUBE]; // d/dt -> St
1312
1313 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
1314 sizeof(shc) + sizeof(shr) + sizeof(shs) + sizeof(sht)
1315 <= NEKO_EB_MAX_LDS,
1316 "mfma vector block exceeds the shared memory budget");
1317
1318 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
1319 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
1320 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
1321 const int nthr = NWF * 64;
1322
1323 const int eb = wf / WPE; // which element this wavefront serves
1324 const int sub = wf % WPE; // its rank among that element's waves
1325 const int gtid = sub * 64 + lane; // thread id within the element group
1326
1327 /* Threads past the last element still have to reach the block wide
1328 barriers, so clamp their reads and drop their stores rather than
1329 returning early. At EB == 1 the grid covers nelv exactly and this is
1330 constant folded away */
1331 const int e_blk = blockIdx.x * EB + eb;
1332 const bool active = (EB == 1) ? true : (e_blk < nelv);
1333 const int e = active ? e_blk : (nelv - 1);
1334 const int ele = e * LX3;
1335 /* This element's copy of the geometric factors, ind is 1-based */
1336 const int gele = (ind[e] - 1) * LX3;
1337 const int sh = eb * CUBE;
1338
1339 /* The element point behind each of this thread's LDS slots, decoded once,
1340 see ax_helm_compr_mfma_elem */
1341 int goff[SPT];
1342#pragma unroll
1343 for (int q = 0; q < SPT; q++) {
1344 const int sl = gtid + q * GNTHR;
1346 }
1347
1348 /* Reference derivative matrices, one copy shared by every element. The
1349 staged copy carries the padded column stride, so the linear index of the
1350 caller's LX x LX copy is decoded here; the global reads stay contiguous
1351 and it is the LDS side that gains the stride, which is the point. */
1352 for (int p = tid; p < LX2; p += nthr) {
1353 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
1354 shdx[sd] = dx[p];
1355 shdy[sd] = dy[p];
1356 shdz[sd] = dz[p];
1357 }
1358
1359 /* The geometric factors, read once and reused by all three components. The
1360 pointwise pass below strides the slots the same way, so the value for slot
1361 gtid + q * GNTHR stays in slot q and no index array is needed */
1362 T rG00[NG], rG11[NG], rG22[NG];
1363 T rG01[NG], rG02[NG], rG12[NG];
1364 T rH1[NG];
1365
1366 if (GREG) {
1367#pragma unroll
1368 for (int q = 0; q < NG; q++) {
1369 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
1370 const int gq = (goff[q] >= 0) ? (goff[q] + gele) : gele;
1371 rG00[q] = g11[gq];
1372 rG11[q] = g22[gq];
1373 rG22[q] = g33[gq];
1374 rG01[q] = g12[gq];
1375 rG02[q] = g13[gq];
1376 rG12[q] = g23[gq];
1377 rH1[q] = h1[gp];
1378 }
1379 }
1380
1381#pragma unroll
1382 for (int c = 0; c < 3; c++) {
1383 /* Selected rather than indexed out of a pointer array: an array of
1384 pointers indexed by a loop counter is only free if the loop is fully
1385 unrolled, and this one carries barriers */
1386 const T * const cin = (c == 0) ? u : (c == 1) ? v : w;
1387 T * const cout = (c == 0) ? au : (c == 1) ? av : aw;
1388
1389 /* Element-local component, staged by the wavefronts that own it, over the
1390 same slot set the write-back at the end of the loop uses -- which is
1391 what makes the barrier between the two unnecessary, see there */
1392#pragma unroll
1393 for (int q = 0; q < SPT; q++) {
1394 if (goff[q] >= 0)
1395 shc[sh + gtid + q * GNTHR] = cin[goff[q] + ele];
1396 }
1397
1398 __syncthreads();
1399
1400 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout */
1401 if (eb % NEL == 0)
1402 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD, NEL>::run(
1403 shr + sh, shdx, shc + sh, lane, sub);
1404 if (eb % NEL == 0)
1405 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD, NEL>::run(
1406 shs + sh, shdy, shc + sh, lane, sub);
1407 if (eb % NEL == 0)
1408 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD, NEL>::run(
1409 sht + sh, shdz, shc + sh, lane, sub);
1410
1411 __syncthreads();
1412
1413 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht.
1414 The staged component is dead once the gradient is out and does not need
1415 clearing either: the first divergence contraction below overwrites it */
1416#pragma unroll
1417 for (int q = 0; q < SPT; q++) {
1418 if (goff[q] >= 0) {
1419 const int sl = sh + gtid + q * GNTHR;
1420 T G00, G11, G22, G01, G02, G12, H1;
1421 if (GREG) {
1422 /* GREG is a compile time constant, so only one of these two bodies
1423 is emitted; the clamp keeps the index inside the NG == 1 array
1424 that the other one never reads from */
1425 const int r = GREG ? q : 0;
1426 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
1427 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
1428 H1 = rH1[r];
1429 } else {
1430 const int gp = goff[q] + ele;
1431 const int gq = goff[q] + gele;
1432 G00 = g11[gq]; G11 = g22[gq]; G22 = g33[gq];
1433 G01 = g12[gq]; G02 = g13[gq]; G12 = g23[gq];
1434 H1 = h1[gp];
1435 }
1436 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
1437 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
1438 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
1439 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
1440 }
1441 }
1442
1443 __syncthreads();
1444
1445 /* Divergence: Dr^T Sr + Ds^T Ss + Dt^T St, the first one overwriting shc
1446 so that nothing has to clear it, see ax_helm_compr_mfma_elem */
1447 if (eb % NEL == 0)
1448 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD, NEL>::run(
1449 shc + sh, shdx, shr + sh, lane, sub);
1450 __syncthreads();
1451 if (eb % NEL == 0)
1452 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD, NEL>::run(
1453 shc + sh, shdy, shs + sh, lane, sub);
1454 __syncthreads();
1455 if (eb % NEL == 0)
1456 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD, NEL>::run(
1457 shc + sh, shdz, sht + sh, lane, sub);
1458 __syncthreads();
1459
1460 if (active) {
1461#pragma unroll
1462 for (int q = 0; q < SPT; q++) {
1463 if (goff[q] >= 0)
1464 cout[goff[q] + ele] = shc[sh + gtid + q * GNTHR];
1465 }
1466 }
1467
1468 /* Not required as the loops stand: the write-back above and the restage
1469 at the top of the next pass walk the same slot set per thread, so a
1470 thread only restages what it just read out itself, and no wavefront
1471 can run ahead into another's slots. Kept so that re-striding either
1472 loop -- coalescing the write-back over tid rather than gtid, say --
1473 cannot introduce a race silently */
1474 if (c < 2) {
1475 __syncthreads();
1476 }
1477 }
1478}
1479#endif // __gfx90a__ || __gfx942__
1480
1481/*
1482 * Compile-time dispatch onto the vector MFMA element kernel, see the note on
1483 * ax_helm_compr_mfma_dispatch above. The vector operator dispatches every
1484 * 2 <= LX <= 16 and the strategy covers 4 <= LX <= 12, so this no-op is
1485 * reached at compile time for rather more instantiations than the scalar
1486 * one's -- and must stay unreachable at runtime: a no-op times as free and
1487 * would win the comparison, leaving stale values in au, av and aw. The
1488 * autotuner only offers the strategy where mfma_lx_supported() and
1489 * hip_have_mfma() both say yes, which is exactly the set instantiated below.
1490 */
1491template< typename T, const int LX, const int NWF, const int TILE >
1493 __device__ static void run(T *, T *, T *,
1494 const T *, const T *, const T *,
1495 const T *, const T *, const T *, const T *,
1496 const T *, const T *, const T *,
1497 const T *, const T *, const T *,
1498 const int *, const int) {}
1499};
1500
1501#if defined(__gfx90a__) || defined(__gfx942__)
1502
1503/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
1504#define NEKO_AX_HELM_COMPR_MFMA_VECTOR_DISPATCH(TYPE, LXV) \
1505 template< const int NWF, const int TILE > \
1506 struct ax_helm_compr_mfma_vector_dispatch< TYPE, LXV, NWF, TILE > { \
1507 __device__ static void run(TYPE *au, TYPE *av, TYPE *aw, \
1508 const TYPE *u, const TYPE *v, const TYPE *w, \
1509 const TYPE *dx, const TYPE *dy, \
1510 const TYPE *dz, const TYPE *h1, \
1511 const TYPE *g11, const TYPE *g22, \
1512 const TYPE *g33, const TYPE *g12, \
1513 const TYPE *g13, const TYPE *g23, \
1514 const int *ind, const int nelv) { \
1515 ax_helm_compr_mfma_vector_elem< TYPE, LXV, NWF, TILE >(au, av, aw, \
1516 u, v, w, dx, dy, dz, h1, \
1517 g11, g22, g33, \
1518 g12, g13, g23, ind, nelv); \
1519 } \
1520 }
1521
1542
1543#endif // __gfx90a__ || __gfx942__
1544
1545/* Named after its CUDA counterpart ax_helm_kernel_dmma_vector rather than
1546 after the adjacent ax_helm_compr_kernel_vector_kstep, so that the matrix unit
1547 kernels read alike across the two backends.
1548
1549 Bare __launch_bounds__ rather than NEKO_EB_BOUNDS, for the reason given on
1550 ax_helm_compr_kernel_mfma: the three waves per SIMD the kstep kernels ask for
1551 would tighten the register budget of a kernel whose occupancy is already
1552 set by its LDS footprint */
1553template< typename T, const int LX, const int NWF, const int TILE >
1556 T * __restrict__ av,
1557 T * __restrict__ aw,
1558 const T * __restrict__ u,
1559 const T * __restrict__ v,
1560 const T * __restrict__ w,
1561 const T * __restrict__ dx,
1562 const T * __restrict__ dy,
1563 const T * __restrict__ dz,
1564 const T * __restrict__ h1,
1565 const T * __restrict__ g11,
1566 const T * __restrict__ g22,
1567 const T * __restrict__ g33,
1568 const T * __restrict__ g12,
1569 const T * __restrict__ g13,
1570 const T * __restrict__ g23,
1571 const int * __restrict__ ind,
1572 const int nelv) {
1573
1575 u, v, w,
1576 dx, dy, dz, h1,
1577 g11, g22, g33,
1578 g12, g13, g23,
1579 ind, nelv);
1580}
1581
1582/* The mass term of the vector operator, ax_helm_kernel_vector_part2, reads
1583 no geometric factors, so it is not duplicated here: the compressed vector
1584 operator is completed by the standard hip_ax_helm_vector_part2 */
1585#endif // __MATH_AX_HELM_COMPR_KERNEL_H__
__global__ void ale_add_kinematics_kernel(const int n, T *__restrict__ wx, T *__restrict__ wy, T *__restrict__ wz, const T *__restrict__ x_ref, const T *__restrict__ y_ref, const T *__restrict__ z_ref, const T *__restrict__ phi, const T *__restrict__ x, const T *__restrict__ y, const T *__restrict__ z, const kinematics_params_t kin_params)
const bool active
__shared__ T shv[EB *LX *LX]
const int ij_p
__shared__ T shdy[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g23
T rvw[LX]
__global__ void T *__restrict__ T *__restrict__ aw
__shared__ T shu[EB *LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g22
const int sh
const int eb
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const int *__restrict__ ind
__shared__ T shdz[LX *LX]
T rww[LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g13
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ w
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const int *__restrict__ const int nelv
const int sh_p
const int i
const int ij
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g12
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dz
__global__ void const T *__restrict__ const T *__restrict__ dx
__global__ void const T *__restrict__ u
__shared__ T shus[EB *LX *LX]
const int e
__global__ void T *__restrict__ av
__shared__ T shur[EB *LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ dy
T ruw[LX]
__shared__ T shvs[EB *LX *LX]
const int e_blk
const int gele
T ru[LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g33
__shared__ T shw[EB *LX *LX]
const int ele
__shared__ T shwr[EB *LX *LX]
__global__ void ax_helm_compr_kernel_1d(T *__restrict__ w, const T *__restrict__ u, const T *__restrict__ dx, const T *__restrict__ dy, const T *__restrict__ dz, const T *__restrict__ dxt, const T *__restrict__ dyt, const T *__restrict__ dzt, const T *__restrict__ h1, const T *__restrict__ g11, const T *__restrict__ g22, const T *__restrict__ g33, const T *__restrict__ g12, const T *__restrict__ g13, const T *__restrict__ g23, const int *__restrict__ ind)
T rw[LX]
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ v
const int j
__shared__ T shvr[EB *LX *LX]
__syncthreads()
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g11
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ h1
__shared__ T shws[EB *LX *LX]
shdx[ij]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dyt
__shared__ T shdzt[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dzt
__shared__ T shdyt[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dxt
#define NEKO_EB_BOUNDS(NT)
Definition elem_block.h:95
__global__ void __launch_bounds__((LX *LX *EB)) ax_helm_compr_kernel_kstep(T *__restrict__ w
#define NEKO_EB_MAX_LDS
Definition elem_block.h:79
#define NEKO_MFMA_CUBE_N(LX, SZ)
#define NEKO_MFMA_NEL_N(EB, LX, SZ, TILE)
#define NEKO_MFMA_EB_N(NWF, LX, SZ)
#define NEKO_MFMA_VECTOR_GREG_N(SPT, SZ)
#define NEKO_MFMA_SPT_N(WPE, LX, SZ)
#define NEKO_MFMA_PAD_N(LX, SZ)
#define NEKO_MFMA_DMAT_N(LX, SZ)
static __device__ void run(T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const int *, const int)
static __device__ void run(T *, T *, T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const T *, const int *, const int)