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 /* Geometry prefetched into registers, or read where it is used?
511 Same budget and the same macro the tuner logs with, see the note
512 on the prefetch below */
513 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
514 NG = GREG ? SPT : 1 };
515 static_assert(WPE * EB == NWF,
516 "wavefronts per block must split evenly over the elements");
517
521 __shared__ T shu[EB * CUBE]; // input u, later reused as output w
522 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
523 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
524 __shared__ T sht[EB * CUBE]; // d/dt -> St
525
526 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
527 sizeof(shu) + sizeof(shr) + sizeof(shs) + sizeof(sht)
529 "mfma block exceeds the shared memory budget");
530
531 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
532 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
533 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
534 const int nthr = NWF * 64;
535
536 const int eb = wf / WPE; // which element this wavefront serves
537 const int sub = wf % WPE; // its rank among that element's waves
538 const int gtid = sub * 64 + lane; // thread id within the element group
539
540 /* Threads past the last element still have to reach the block wide
541 barriers, so clamp their reads and drop their stores rather than
542 returning early. At EB == 1 the grid covers nelv exactly and this is
543 constant folded away */
544 const int e_blk = blockIdx.x * EB + eb;
545 const bool active = (EB == 1) ? true : (e_blk < nelv);
546 const int e = active ? e_blk : (nelv - 1);
547 const int ele = e * LX3;
548 /* This element's copy of the geometric factors, ind is 1-based */
549 const int gele = (ind[e] - 1) * LX3;
550 const int sh = eb * CUBE;
551
552 /* The element point each of this thread's LDS slots holds, or -1 for a pad
553 slot, decoded once and reused by every pass below. The three passes walk
554 slots at unit stride in LDS -- which is what keeps them off each other's
555 banks once the cube is padded -- and this array is the translation back
556 to the caller's LX^3 storage, see mfma_slot_point(). Where the cube is not
557 padded the decode folds away to the identity and this is the old
558 p = gtid + q*GNTHR loop. */
559 int goff[SPT];
560#pragma unroll
561 for (int q = 0; q < SPT; q++) {
562 const int sl = gtid + q * GNTHR;
564 }
565
566 /* Reference derivative matrices, one copy shared by every element. The
567 staged copy carries the padded column stride, so the linear index of the
568 caller's LX x LX copy is decoded here; the global reads stay contiguous
569 and it is the LDS side that gains the stride, which is the point. */
570 for (int p = tid; p < LX2; p += nthr) {
571 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
572 shdx[sd] = dx[p];
573 shdy[sd] = dy[p];
574 shdz[sd] = dz[p];
575 }
576 /* Element-local field, staged by the wavefronts that own it */
577#pragma unroll
578 for (int q = 0; q < SPT; q++) {
579 if (goff[q] >= 0)
580 shu[sh + gtid + q * GNTHR] = u[goff[q] + ele];
581 }
582
584
585 /*
586 * The seven geometric factors, issued here rather than where they are used.
587 *
588 * They are needed by the pointwise pass, which sits behind the gradient
589 * contractions and a barrier, so reading them there exposes the full global
590 * latency with nothing to cover it: this kernel runs at three wavefronts per
591 * SIMD in double precision at LX = 8, a third of what the 1D kernel that
592 * beats it reaches, so there is no other occupancy to hide behind. Issued
593 * here they are in flight across the three contractions instead.
594 *
595 * The cost is 7 * SPT * (sizeof(T)/4) VGPRs, and past a budget that stops
596 * paying -- 189 values at LX = 12 on a single wavefront, against the 256 a
597 * lane addresses -- so it is chosen per instantiation by the same
598 * NEKO_MFMA_VECTOR_GREG the vector kernel uses, and reported by the tuner
599 * for the same reason: it is derived from the wavefront count rather than
600 * swept, so two neighbouring candidates can differ in more than block shape.
601 */
602 T rG00[NG], rG11[NG], rG22[NG];
603 T rG01[NG], rG02[NG], rG12[NG];
604 T rH1[NG];
605
606 if (GREG) {
607#pragma unroll
608 for (int q = 0; q < NG; q++) {
609 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
610 const int gq = (goff[q] >= 0) ? (goff[q] + gele) : gele;
611 rG00[q] = g11[gq];
612 rG11[q] = g22[gq];
613 rG22[q] = g33[gq];
614 rG01[q] = g12[gq];
615 rG02[q] = g13[gq];
616 rG12[q] = g23[gq];
617 rH1[q] = h1[gp];
618 }
619 }
620
621 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout. */
622 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD>::run(shr + sh,
623 shdx, shu + sh, lane, sub);
624 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD>::run(shs + sh,
625 shdy, shu + sh, lane, sub);
626 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD>::run(sht + sh,
627 shdz, shu + sh, lane, sub);
628
630
631 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht. */
632#pragma unroll
633 for (int q = 0; q < SPT; q++) {
634 if (goff[q] >= 0) {
635 const int sl = sh + gtid + q * GNTHR;
636 T G00, G11, G22, G01, G02, G12, H1;
637 if (GREG) {
638 /* GREG is a compile time constant, so only one of these two bodies is
639 emitted; the clamp keeps the index inside the NG == 1 array that the
640 other one never reads from */
641 const int r = GREG ? q : 0;
642 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
643 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
644 H1 = rH1[r];
645 } else {
646 const int gp = goff[q] + ele;
647 const int gq = goff[q] + gele;
648 G00 = g11[gq]; G11 = g22[gq]; G22 = g33[gq];
649 G01 = g12[gq]; G02 = g13[gq]; G12 = g23[gq];
650 H1 = h1[gp];
651 }
652 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
653 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
654 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
655 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
656 }
657 }
658
660
661 /* Divergence: w = Dr^T Sr + Ds^T Ss + Dt^T St, accumulated in shu (= w).
662 The first contraction overwrites rather than accumulates, which is what
663 lets the separate zeroing pass over the cube -- and the barrier after it
664 -- go away entirely; the summation order of the three is unchanged, so
665 this is not a reassociation. All four (TRANSPOSE, ACCUM) pairs are covered
666 by the gfx90a read-out, so the non-accumulating one is no less verified
667 than the accumulating one. */
668 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD>::run(shu + sh,
669 shdx, shr + sh, lane, sub);
671 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD>::run(shu + sh,
672 shdy, shs + sh, lane, sub);
674 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD>::run(shu + sh,
675 shdz, sht + sh, lane, sub);
677
678 if (active) {
679#pragma unroll
680 for (int q = 0; q < SPT; q++) {
681 if (goff[q] >= 0)
682 w[goff[q] + ele] = shu[sh + gtid + q * GNTHR];
683 }
684 }
685}
686#endif // __gfx90a__ || __gfx942__
687
688/*
689 * Compile-time dispatch onto the MFMA element kernel. The launch macros in
690 * ax_helm_compr.hip are written for every LX the operator dispatches and for
691 * whatever `real` is, so every combination has to compile; the ones the
692 * strategy does not cover -- LX outside the supported range, a build without
693 * a matrix-core arch -- resolve to this no-op. The autotuner never selects
694 * the strategy for them, so the no-op is unreachable at runtime, see
695 * mfma_lx_supported() and hip_have_mfma() in mfma_kernel.h.
696 */
697template< typename T, const int LX, const int NWF, const int TILE >
699 __device__ static void run(T *, const T *, const T *, const T *, const T *,
700 const T *, const T *, const T *, const T *,
701 const T *, const T *, const T *,
702 const int *, const int) {}
703};
704
705#if defined(__gfx90a__) || defined(__gfx942__)
706
707/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
708#define NEKO_AX_HELM_COMPR_MFMA_DISPATCH(TYPE, LXV) \
709 template< const int NWF, const int TILE > \
710 struct ax_helm_compr_mfma_dispatch< TYPE, LXV, NWF, TILE > { \
711 __device__ static void run(TYPE *w, const TYPE *u, \
712 const TYPE *dx, const TYPE *dy, \
713 const TYPE *dz, const TYPE *h1, \
714 const TYPE *g11, const TYPE *g22, \
715 const TYPE *g33, const TYPE *g12, \
716 const TYPE *g13, const TYPE *g23, \
717 const int *ind, const int nelv) { \
718 ax_helm_compr_mfma_elem< TYPE, LXV, NWF, TILE >(w, u, dx, dy, dz, h1, \
719 g11, g22, g33, g12, g13, g23, \
720 ind, nelv); \
721 } \
722 }
723
742
743#endif // __gfx90a__ || __gfx942__
744
745/*
746 * Note the bare __launch_bounds__ rather than NEKO_EB_BOUNDS: the kstep
747 * kernels ask for three waves per SIMD, and this kernel was validated on
748 * gfx90a/gfx942 without that constraint. Keep it byte-identical to the
749 * configuration that was confirmed on hardware.
750 */
751template< typename T, const int LX, const int NWF, const int TILE >
754 const T * __restrict__ u,
755 const T * __restrict__ dx,
756 const T * __restrict__ dy,
757 const T * __restrict__ dz,
758 const T * __restrict__ h1,
759 const T * __restrict__ g11,
760 const T * __restrict__ g22,
761 const T * __restrict__ g33,
762 const T * __restrict__ g12,
763 const T * __restrict__ g13,
764 const T * __restrict__ g23,
765 const int * __restrict__ ind,
766 const int nelv) {
767
769 g11, g22, g33,
770 g12, g13, g23,
771 ind, nelv);
772}
773
774/*
775 * Vector versions
776 */
777
778template< typename T, const int LX, const int EB >
783 const T * __restrict__ u,
784 const T * __restrict__ v,
785 const T * __restrict__ w,
786 const T * __restrict__ dx,
787 const T * __restrict__ dy,
788 const T * __restrict__ dz,
789 const T * __restrict__ h1,
790 const T * __restrict__ g11,
791 const T * __restrict__ g22,
792 const T * __restrict__ g33,
793 const T * __restrict__ g12,
794 const T * __restrict__ g13,
795 const T * __restrict__ g23,
796 const int * __restrict__ ind,
797 const int nelv) {
798
799 /* Element independent, one copy per block */
800 __shared__ T shdx[LX * LX];
801 __shared__ T shdy[LX * LX];
802 __shared__ T shdz[LX * LX];
803
804 /* One slice per element in the block */
805 __shared__ T shu[EB * LX * LX];
806 __shared__ T shur[EB * LX * LX];
807 __shared__ T shus[EB * LX * LX];
808
812
816
817 static_assert(sizeof(shdx) +
818 sizeof(shdy) +
819 sizeof(shdz) +
820 sizeof(shu) +
821 sizeof(shur) +
822 sizeof(shus) +
823 sizeof(shv) +
824 sizeof(shvr) +
825 sizeof(shvs) +
826 sizeof(shw) +
827 sizeof(shwr) +
828 sizeof(shws)
830 "kstep block exceeds the LDS budget");
831
832 T ru[LX];
834 T rw[LX];
835
839
840 T rut;
843
844 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
845 constant folded away and the kernel is exactly what it was before */
846 const int eb = (EB == 1) ? 0 : threadIdx.z;
847 const int e_blk = blockIdx.x * EB + eb;
848 const bool active = (EB == 1) ? true : (e_blk < nelv);
849 const int e = active ? e_blk : (nelv - 1);
850 const int j = threadIdx.y;
851 const int i = threadIdx.x;
852 const int ij = i + j*LX;
853 const int sh = eb*LX*LX;
854 const int ele = e*LX*LX*LX;
855 /* This element's copy of the geometric factors, ind is 1-based */
856 const int gele = (ind[e] - 1)*LX*LX*LX;
857
858 if (eb == 0) {
859 shdx[ij] = dx[ij];
860 shdy[ij] = dy[ij];
861 shdz[ij] = dz[ij];
862 }
863
864#pragma unroll
865 for(int k = 0; k < LX; ++k){
866 ru[k] = u[ij + k*LX*LX + ele];
867 ruw[k] = 0.0;
868
869 rv[k] = v[ij + k*LX*LX + ele];
870 rvw[k] = 0.0;
871
872 rw[k] = w[ij + k*LX*LX + ele];
873 rww[k] = 0.0;
874 }
875
876
878#pragma unroll
879 for (int k = 0; k < LX; ++k){
880 const int ijk = ij + k*LX*LX;
881 const T G00 = g11[ijk+gele];
882 const T G11 = g22[ijk+gele];
883 const T G22 = g33[ijk+gele];
884 const T G01 = g12[ijk+gele];
885 const T G02 = g13[ijk+gele];
886 const T G12 = g23[ijk+gele];
887 const T H1 = h1[ijk+ele];
888 T uttmp = 0.0;
889 T vttmp = 0.0;
890 T wttmp = 0.0;
891 shu[sh + ij] = ru[k];
892 shv[sh + ij] = rv[k];
893 shw[sh + ij] = rw[k];
894#pragma unroll
895 for (int l = 0; l < LX; l++){
896 uttmp += shdz[k+l*LX] * ru[l];
897 vttmp += shdz[k+l*LX] * rv[l];
898 wttmp += shdz[k+l*LX] * rw[l];
899 }
901
902 T urtmp = 0.0;
903 T ustmp = 0.0;
904
905 T vrtmp = 0.0;
906 T vstmp = 0.0;
907
908 T wrtmp = 0.0;
909 T wstmp = 0.0;
910#pragma unroll
911 for (int l = 0; l < LX; l++){
912 urtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
913 ustmp += shdy[j+l*LX] * shu[sh + i+l*LX];
914
915 vrtmp += shdx[i+l*LX] * shv[sh + l+j*LX];
916 vstmp += shdy[j+l*LX] * shv[sh + i+l*LX];
917
918 wrtmp += shdx[i+l*LX] * shw[sh + l+j*LX];
919 wstmp += shdy[j+l*LX] * shw[sh + i+l*LX];
920 }
921
922 shur[sh + ij] = H1
923 * (G00 * urtmp
924 + G01 * ustmp
925 + G02 * uttmp);
926 shus[sh + ij] = H1
927 * (G01 * urtmp
928 + G11 * ustmp
929 + G12 * uttmp);
930 rut = H1
931 * (G02 * urtmp
932 + G12 * ustmp
933 + G22 * uttmp);
934
935 shvr[sh + ij] = H1
936 * (G00 * vrtmp
937 + G01 * vstmp
938 + G02 * vttmp);
939 shvs[sh + ij] = H1
940 * (G01 * vrtmp
941 + G11 * vstmp
942 + G12 * vttmp);
943 rvt = H1
944 * (G02 * vrtmp
945 + G12 * vstmp
946 + G22 * vttmp);
947
948 shwr[sh + ij] = H1
949 * (G00 * wrtmp
950 + G01 * wstmp
951 + G02 * wttmp);
952 shws[sh + ij] = H1
953 * (G01 * wrtmp
954 + G11 * wstmp
955 + G12 * wttmp);
956 rwt = H1
957 * (G02 * wrtmp
958 + G12 * wstmp
959 + G22 * wttmp);
960
962
963 T uwijke = 0.0;
964 T vwijke = 0.0;
965 T wwijke = 0.0;
966#pragma unroll
967 for (int l = 0; l < LX; l++){
968 uwijke += shur[sh + l+j*LX] * shdx[l+i*LX];
969 ruw[l] += rut * shdz[k+l*LX];
970 uwijke += shus[sh + i+l*LX] * shdy[l + j*LX];
971
972 vwijke += shvr[sh + l+j*LX] * shdx[l+i*LX];
973 rvw[l] += rvt * shdz[k+l*LX];
974 vwijke += shvs[sh + i+l*LX] * shdy[l + j*LX];
975
976 wwijke += shwr[sh + l+j*LX] * shdx[l+i*LX];
977 rww[l] += rwt * shdz[k+l*LX];
978 wwijke += shws[sh + i+l*LX] * shdy[l + j*LX];
979 }
980 ruw[k] += uwijke;
981 rvw[k] += vwijke;
982 rww[k] += wwijke;
983 }
984 if (active) {
985#pragma unroll
986 for (int k = 0; k < LX; ++k){
987 au[ij + k*LX*LX + ele] = ruw[k];
988 av[ij + k*LX*LX + ele] = rvw[k];
989 aw[ij + k*LX*LX + ele] = rww[k];
990 }
991 }
992}
993
994template< typename T, const int LX, const int EB >
997 T * __restrict__ av,
998 T * __restrict__ aw,
999 const T * __restrict__ u,
1000 const T * __restrict__ v,
1001 const T * __restrict__ w,
1002 const T * __restrict__ dx,
1003 const T * __restrict__ dy,
1004 const T * __restrict__ dz,
1005 const T * __restrict__ h1,
1006 const T * __restrict__ g11,
1007 const T * __restrict__ g22,
1008 const T * __restrict__ g33,
1009 const T * __restrict__ g12,
1010 const T * __restrict__ g13,
1011 const T * __restrict__ g23,
1012 const int * __restrict__ ind,
1013 const int nelv) {
1014
1015 /* Element independent, one copy per block */
1016 __shared__ T shdx[LX * (LX+1)];
1017 __shared__ T shdy[LX * (LX+1)];
1018 __shared__ T shdz[LX * (LX+1)];
1019
1020 /* One slice per element in the block */
1021 __shared__ T shu[EB * LX * (LX+1)];
1022 __shared__ T shur[EB * LX * LX];
1023 __shared__ T shus[EB * LX * (LX+1)];
1024
1025 __shared__ T shv[EB * LX * (LX+1)];
1026 __shared__ T shvr[EB * LX * LX];
1027 __shared__ T shvs[EB * LX * (LX+1)];
1028
1029 __shared__ T shw[EB * LX * (LX+1)];
1030 __shared__ T shwr[EB * LX * LX];
1031 __shared__ T shws[EB * LX * (LX+1)];
1032
1033 static_assert(sizeof(shdx) +
1034 sizeof(shdy) +
1035 sizeof(shdz) +
1036 sizeof(shu) +
1037 sizeof(shur) +
1038 sizeof(shus) +
1039 sizeof(shv) +
1040 sizeof(shvr) +
1041 sizeof(shvs) +
1042 sizeof(shw) +
1043 sizeof(shwr) +
1044 sizeof(shws)
1045 <= NEKO_EB_MAX_LDS,
1046 "kstep block exceeds the LDS budget");
1047
1048 T ru[LX];
1049 T rv[LX];
1050 T rw[LX];
1051
1052 T ruw[LX];
1053 T rvw[LX];
1054 T rww[LX];
1055
1056 T rut;
1057 T rvt;
1058 T rwt;
1059
1060 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
1061 constant folded away and the kernel is exactly what it was before */
1062 const int eb = (EB == 1) ? 0 : threadIdx.z;
1063 const int e_blk = blockIdx.x * EB + eb;
1064 const bool active = (EB == 1) ? true : (e_blk < nelv);
1065 const int e = active ? e_blk : (nelv - 1);
1066 const int j = threadIdx.y;
1067 const int i = threadIdx.x;
1068 const int ij = i + j*LX;
1069 const int ij_p = i + j*(LX+1);
1070 const int sh = eb*LX*LX;
1071 const int sh_p = eb*LX*(LX+1);
1072 const int ele = e*LX*LX*LX;
1073 /* This element's copy of the geometric factors, ind is 1-based */
1074 const int gele = (ind[e] - 1)*LX*LX*LX;
1075
1076 if (eb == 0) {
1077 shdx[ij_p] = dx[ij];
1078 shdy[ij_p] = dy[ij];
1079 shdz[ij_p] = dz[ij];
1080 }
1081
1082#pragma unroll
1083 for(int k = 0; k < LX; ++k){
1084 ru[k] = u[ij + k*LX*LX + ele];
1085 ruw[k] = 0.0;
1086
1087 rv[k] = v[ij + k*LX*LX + ele];
1088 rvw[k] = 0.0;
1089
1090 rw[k] = w[ij + k*LX*LX + ele];
1091 rww[k] = 0.0;
1092 }
1093
1094
1095 __syncthreads();
1096#pragma unroll
1097 for (int k = 0; k < LX; ++k){
1098 const int ijk = ij + k*LX*LX;
1099 const T G00 = g11[ijk+gele];
1100 const T G11 = g22[ijk+gele];
1101 const T G22 = g33[ijk+gele];
1102 const T G01 = g12[ijk+gele];
1103 const T G02 = g13[ijk+gele];
1104 const T G12 = g23[ijk+gele];
1105 const T H1 = h1[ijk+ele];
1106 T uttmp = 0.0;
1107 T vttmp = 0.0;
1108 T wttmp = 0.0;
1109 shu[sh_p + ij_p] = ru[k];
1110 shv[sh_p + ij_p] = rv[k];
1111 shw[sh_p + ij_p] = rw[k];
1112#pragma unroll
1113 for (int l = 0; l < LX; l++){
1114 uttmp += shdz[k+l*(LX+1)] * ru[l];
1115 vttmp += shdz[k+l*(LX+1)] * rv[l];
1116 wttmp += shdz[k+l*(LX+1)] * rw[l];
1117 }
1119
1120 T urtmp = 0.0;
1121 T ustmp = 0.0;
1122
1123 T vrtmp = 0.0;
1124 T vstmp = 0.0;
1125
1126 T wrtmp = 0.0;
1127 T wstmp = 0.0;
1128#pragma unroll
1129 for (int l = 0; l < LX; l++){
1130 urtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
1131 ustmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
1132
1133 vrtmp += shdx[i+l*(LX+1)] * shv[sh_p + l+j*(LX+1)];
1134 vstmp += shdy[j+l*(LX+1)] * shv[sh_p + i+l*(LX+1)];
1135
1136 wrtmp += shdx[i+l*(LX+1)] * shw[sh_p + l+j*(LX+1)];
1137 wstmp += shdy[j+l*(LX+1)] * shw[sh_p + i+l*(LX+1)];
1138 }
1139
1140 shur[sh + ij] = H1
1141 * (G00 * urtmp
1142 + G01 * ustmp
1143 + G02 * uttmp);
1144 shus[sh_p + ij_p] = H1
1145 * (G01 * urtmp
1146 + G11 * ustmp
1147 + G12 * uttmp);
1148 rut = H1
1149 * (G02 * urtmp
1150 + G12 * ustmp
1151 + G22 * uttmp);
1152
1153 shvr[sh + ij] = H1
1154 * (G00 * vrtmp
1155 + G01 * vstmp
1156 + G02 * vttmp);
1157 shvs[sh_p + ij_p] = H1
1158 * (G01 * vrtmp
1159 + G11 * vstmp
1160 + G12 * vttmp);
1161 rvt = H1
1162 * (G02 * vrtmp
1163 + G12 * vstmp
1164 + G22 * vttmp);
1165
1166 shwr[sh + ij] = H1
1167 * (G00 * wrtmp
1168 + G01 * wstmp
1169 + G02 * wttmp);
1170 shws[sh_p + ij_p] = H1
1171 * (G01 * wrtmp
1172 + G11 * wstmp
1173 + G12 * wttmp);
1174 rwt = H1
1175 * (G02 * wrtmp
1176 + G12 * wstmp
1177 + G22 * wttmp);
1178
1180
1181 T uwijke = 0.0;
1182 T vwijke = 0.0;
1183 T wwijke = 0.0;
1184#pragma unroll
1185 for (int l = 0; l < LX; l++){
1186 uwijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
1187 ruw[l] += rut * shdz[k+l*(LX+1)];
1188 uwijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1189
1190 vwijke += shvr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1191 rvw[l] += rvt * shdz[k+l*(LX+1)];
1192 vwijke += shvs[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1193
1194 wwijke += shwr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1195 rww[l] += rwt * shdz[k+l*(LX+1)];
1196 wwijke += shws[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1197 }
1198 ruw[k] += uwijke;
1199 rvw[k] += vwijke;
1200 rww[k] += wwijke;
1201 }
1202 if (active) {
1203#pragma unroll
1204 for (int k = 0; k < LX; ++k){
1205 au[ij + k*LX*LX + ele] = ruw[k];
1206 av[ij + k*LX*LX + ele] = rvw[k];
1207 aw[ij + k*LX*LX + ele] = rww[k];
1208 }
1209 }
1210}
1211
1244#if defined(__gfx90a__) || defined(__gfx942__)
1245
1246/* Matrix-core vector axhelm for one element of order LX-1 (T = float or
1247 * double), see ax_helm_compr_mfma_elem for the block geometry this shares. */
1248template< typename T, const int LX, const int NWF, const int TILE >
1250 T * __restrict__ av,
1251 T * __restrict__ aw,
1252 const T * __restrict__ u,
1253 const T * __restrict__ v,
1254 const T * __restrict__ w,
1255 const T * __restrict__ dx,
1256 const T * __restrict__ dy,
1257 const T * __restrict__ dz,
1258 const T * __restrict__ h1,
1259 const T * __restrict__ g11,
1260 const T * __restrict__ g22,
1261 const T * __restrict__ g33,
1262 const T * __restrict__ g12,
1263 const T * __restrict__ g13,
1264 const T * __restrict__ g23,
1265 const int * __restrict__ ind,
1266 const int nelv) {
1267 const int LX2 = LX * LX;
1268 const int LX3 = LX * LX * LX;
1269
1270 /* NWF wavefronts per block, WPE of them cooperating on one element and the
1271 block covering EB elements, exactly as in ax_helm_compr_mfma_elem */
1272 enum { PAD = NEKO_MFMA_PAD_N(LX, sizeof(T)),
1273 CUBE = NEKO_MFMA_CUBE_N(LX, sizeof(T)),
1274 DMAT = NEKO_MFMA_DMAT_N(LX, sizeof(T)),
1275 EB = NEKO_MFMA_EB_N(NWF, LX, sizeof(T)),
1276 WPE = NWF / EB,
1277 GNTHR = WPE * 64, /* threads serving one element */
1278 SPT = NEKO_MFMA_SPT_N(WPE, LX, sizeof(T)),
1279 /* Geometry in registers across the components, or re-read per
1280 component? The same macro the tuner logs with, so the reported
1281 mode cannot drift from the compiled one */
1282 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
1283 /* One slot when the factors are re-read, so the arrays below cost
1284 nothing in that case */
1285 NG = GREG ? SPT : 1 };
1286 static_assert(WPE * EB == NWF,
1287 "wavefronts per block must split evenly over the elements");
1288
1292 __shared__ T shc[EB * CUBE]; // component in, later its result out
1293 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
1294 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
1295 __shared__ T sht[EB * CUBE]; // d/dt -> St
1296
1297 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
1298 sizeof(shc) + sizeof(shr) + sizeof(shs) + sizeof(sht)
1299 <= NEKO_EB_MAX_LDS,
1300 "mfma vector block exceeds the shared memory budget");
1301
1302 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
1303 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
1304 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
1305 const int nthr = NWF * 64;
1306
1307 const int eb = wf / WPE; // which element this wavefront serves
1308 const int sub = wf % WPE; // its rank among that element's waves
1309 const int gtid = sub * 64 + lane; // thread id within the element group
1310
1311 /* Threads past the last element still have to reach the block wide
1312 barriers, so clamp their reads and drop their stores rather than
1313 returning early. At EB == 1 the grid covers nelv exactly and this is
1314 constant folded away */
1315 const int e_blk = blockIdx.x * EB + eb;
1316 const bool active = (EB == 1) ? true : (e_blk < nelv);
1317 const int e = active ? e_blk : (nelv - 1);
1318 const int ele = e * LX3;
1319 /* This element's copy of the geometric factors, ind is 1-based */
1320 const int gele = (ind[e] - 1) * LX3;
1321 const int sh = eb * CUBE;
1322
1323 /* The element point behind each of this thread's LDS slots, decoded once,
1324 see ax_helm_compr_mfma_elem */
1325 int goff[SPT];
1326#pragma unroll
1327 for (int q = 0; q < SPT; q++) {
1328 const int sl = gtid + q * GNTHR;
1330 }
1331
1332 /* Reference derivative matrices, one copy shared by every element. The
1333 staged copy carries the padded column stride, so the linear index of the
1334 caller's LX x LX copy is decoded here; the global reads stay contiguous
1335 and it is the LDS side that gains the stride, which is the point. */
1336 for (int p = tid; p < LX2; p += nthr) {
1337 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
1338 shdx[sd] = dx[p];
1339 shdy[sd] = dy[p];
1340 shdz[sd] = dz[p];
1341 }
1342
1343 /* The geometric factors, read once and reused by all three components. The
1344 pointwise pass below strides the slots the same way, so the value for slot
1345 gtid + q * GNTHR stays in slot q and no index array is needed */
1346 T rG00[NG], rG11[NG], rG22[NG];
1347 T rG01[NG], rG02[NG], rG12[NG];
1348 T rH1[NG];
1349
1350 if (GREG) {
1351#pragma unroll
1352 for (int q = 0; q < NG; q++) {
1353 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
1354 const int gq = (goff[q] >= 0) ? (goff[q] + gele) : gele;
1355 rG00[q] = g11[gq];
1356 rG11[q] = g22[gq];
1357 rG22[q] = g33[gq];
1358 rG01[q] = g12[gq];
1359 rG02[q] = g13[gq];
1360 rG12[q] = g23[gq];
1361 rH1[q] = h1[gp];
1362 }
1363 }
1364
1365#pragma unroll
1366 for (int c = 0; c < 3; c++) {
1367 /* Selected rather than indexed out of a pointer array: an array of
1368 pointers indexed by a loop counter is only free if the loop is fully
1369 unrolled, and this one carries barriers */
1370 const T * const cin = (c == 0) ? u : (c == 1) ? v : w;
1371 T * const cout = (c == 0) ? au : (c == 1) ? av : aw;
1372
1373 /* Element-local component, staged by the wavefronts that own it, over the
1374 same slot set the write-back at the end of the loop uses -- which is
1375 what makes the barrier between the two unnecessary, see there */
1376#pragma unroll
1377 for (int q = 0; q < SPT; q++) {
1378 if (goff[q] >= 0)
1379 shc[sh + gtid + q * GNTHR] = cin[goff[q] + ele];
1380 }
1381
1382 __syncthreads();
1383
1384 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout */
1385 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD>::run(shr + sh,
1386 shdx, shc + sh, lane, sub);
1387 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD>::run(shs + sh,
1388 shdy, shc + sh, lane, sub);
1389 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD>::run(sht + sh,
1390 shdz, shc + sh, lane, sub);
1391
1392 __syncthreads();
1393
1394 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht.
1395 The staged component is dead once the gradient is out and does not need
1396 clearing either: the first divergence contraction below overwrites it */
1397#pragma unroll
1398 for (int q = 0; q < SPT; q++) {
1399 if (goff[q] >= 0) {
1400 const int sl = sh + gtid + q * GNTHR;
1401 T G00, G11, G22, G01, G02, G12, H1;
1402 if (GREG) {
1403 /* GREG is a compile time constant, so only one of these two bodies
1404 is emitted; the clamp keeps the index inside the NG == 1 array
1405 that the other one never reads from */
1406 const int r = GREG ? q : 0;
1407 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
1408 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
1409 H1 = rH1[r];
1410 } else {
1411 const int gp = goff[q] + ele;
1412 const int gq = goff[q] + gele;
1413 G00 = g11[gq]; G11 = g22[gq]; G22 = g33[gq];
1414 G01 = g12[gq]; G02 = g13[gq]; G12 = g23[gq];
1415 H1 = h1[gp];
1416 }
1417 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
1418 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
1419 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
1420 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
1421 }
1422 }
1423
1424 __syncthreads();
1425
1426 /* Divergence: Dr^T Sr + Ds^T Ss + Dt^T St, the first one overwriting shc
1427 so that nothing has to clear it, see ax_helm_compr_mfma_elem */
1428 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD>::run(shc + sh,
1429 shdx, shr + sh, lane, sub);
1430 __syncthreads();
1431 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD>::run(shc + sh,
1432 shdy, shs + sh, lane, sub);
1433 __syncthreads();
1434 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD>::run(shc + sh,
1435 shdz, sht + sh, lane, sub);
1436 __syncthreads();
1437
1438 if (active) {
1439#pragma unroll
1440 for (int q = 0; q < SPT; q++) {
1441 if (goff[q] >= 0)
1442 cout[goff[q] + ele] = shc[sh + gtid + q * GNTHR];
1443 }
1444 }
1445
1446 /* Not required as the loops stand: the write-back above and the restage
1447 at the top of the next pass walk the same slot set per thread, so a
1448 thread only restages what it just read out itself, and no wavefront
1449 can run ahead into another's slots. Kept so that re-striding either
1450 loop -- coalescing the write-back over tid rather than gtid, say --
1451 cannot introduce a race silently */
1452 if (c < 2) {
1453 __syncthreads();
1454 }
1455 }
1456}
1457#endif // __gfx90a__ || __gfx942__
1458
1459/*
1460 * Compile-time dispatch onto the vector MFMA element kernel, see the note on
1461 * ax_helm_compr_mfma_dispatch above. The vector operator dispatches every
1462 * 2 <= LX <= 16 and the strategy covers 4 <= LX <= 12, so this no-op is
1463 * reached at compile time for rather more instantiations than the scalar
1464 * one's -- and must stay unreachable at runtime: a no-op times as free and
1465 * would win the comparison, leaving stale values in au, av and aw. The
1466 * autotuner only offers the strategy where mfma_lx_supported() and
1467 * hip_have_mfma() both say yes, which is exactly the set instantiated below.
1468 */
1469template< typename T, const int LX, const int NWF, const int TILE >
1471 __device__ static void run(T *, T *, T *,
1472 const T *, const T *, const T *,
1473 const T *, const T *, const T *, const T *,
1474 const T *, const T *, const T *,
1475 const T *, const T *, const T *,
1476 const int *, const int) {}
1477};
1478
1479#if defined(__gfx90a__) || defined(__gfx942__)
1480
1481/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
1482#define NEKO_AX_HELM_COMPR_MFMA_VECTOR_DISPATCH(TYPE, LXV) \
1483 template< const int NWF, const int TILE > \
1484 struct ax_helm_compr_mfma_vector_dispatch< TYPE, LXV, NWF, TILE > { \
1485 __device__ static void run(TYPE *au, TYPE *av, TYPE *aw, \
1486 const TYPE *u, const TYPE *v, const TYPE *w, \
1487 const TYPE *dx, const TYPE *dy, \
1488 const TYPE *dz, const TYPE *h1, \
1489 const TYPE *g11, const TYPE *g22, \
1490 const TYPE *g33, const TYPE *g12, \
1491 const TYPE *g13, const TYPE *g23, \
1492 const int *ind, const int nelv) { \
1493 ax_helm_compr_mfma_vector_elem< TYPE, LXV, NWF, TILE >(au, av, aw, \
1494 u, v, w, dx, dy, dz, h1, \
1495 g11, g22, g33, \
1496 g12, g13, g23, ind, nelv); \
1497 } \
1498 }
1499
1518
1519#endif // __gfx90a__ || __gfx942__
1520
1521/* Named after its CUDA counterpart ax_helm_kernel_dmma_vector rather than
1522 after the adjacent ax_helm_compr_kernel_vector_kstep, so that the matrix unit
1523 kernels read alike across the two backends.
1524
1525 Bare __launch_bounds__ rather than NEKO_EB_BOUNDS, for the reason given on
1526 ax_helm_compr_kernel_mfma: the three waves per SIMD the kstep kernels ask for
1527 would tighten the register budget of a kernel whose occupancy is already
1528 set by its LDS footprint */
1529template< typename T, const int LX, const int NWF, const int TILE >
1532 T * __restrict__ av,
1533 T * __restrict__ aw,
1534 const T * __restrict__ u,
1535 const T * __restrict__ v,
1536 const T * __restrict__ w,
1537 const T * __restrict__ dx,
1538 const T * __restrict__ dy,
1539 const T * __restrict__ dz,
1540 const T * __restrict__ h1,
1541 const T * __restrict__ g11,
1542 const T * __restrict__ g22,
1543 const T * __restrict__ g33,
1544 const T * __restrict__ g12,
1545 const T * __restrict__ g13,
1546 const T * __restrict__ g23,
1547 const int * __restrict__ ind,
1548 const int nelv) {
1549
1551 u, v, w,
1552 dx, dy, dz, h1,
1553 g11, g22, g33,
1554 g12, g13, g23,
1555 ind, nelv);
1556}
1557
1558/* The mass term of the vector operator, ax_helm_kernel_vector_part2, reads
1559 no geometric factors, so it is not duplicated here: the compressed vector
1560 operator is completed by the standard hip_ax_helm_vector_part2 */
1561#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_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)