Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ax_helm_kernel.h
Go to the documentation of this file.
1#ifndef __MATH_AX_HELM_KERNEL_H__
2#define __MATH_AX_HELM_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
45template< typename T, const int LX, const int CHUNKS >
47 const T * __restrict__ u,
48 const T * __restrict__ dx,
49 const T * __restrict__ dy,
50 const T * __restrict__ dz,
51 const T * __restrict__ dxt,
52 const T * __restrict__ dyt,
53 const T * __restrict__ dzt,
54 const T * __restrict__ h1,
55 const T * __restrict__ g11,
56 const T * __restrict__ g22,
57 const T * __restrict__ g33,
58 const T * __restrict__ g12,
59 const T * __restrict__ g13,
60 const T * __restrict__ g23) {
61
65
69
74
75 const int e = blockIdx.x;
76 const int iii = threadIdx.x;
77 const int nchunks = (LX * LX * LX - 1)/CHUNKS + 1;
78
79 if (iii<LX*LX) {
80 shdx[iii] = dx[iii];
81 shdy[iii] = dy[iii];
82 shdz[iii] = dz[iii];
83 }
84
85 {
86 int i = iii;
87 while (i < LX * LX * LX){
88 shu[i] = u[i+e*LX*LX*LX];
89 i = i + CHUNKS;
90 }
91 }
92
94
95 if (iii<LX*LX){
96 shdxt[iii] = dxt[iii];
97 shdyt[iii] = dyt[iii];
98 shdzt[iii] = dzt[iii];
99 }
100
101 for (int n=0; n<nchunks; n++){
102 const int ijk = iii+n*CHUNKS;
103 const int jk = ijk/LX;
104 const int i = ijk-jk*LX;
105 const int k = jk/LX;
106 const int j = jk-k*LX;
107 if (i<LX && j<LX && k<LX && ijk < LX*LX*LX){
108 const T G00 = g11[ijk+e*LX*LX*LX];
109 const T G11 = g22[ijk+e*LX*LX*LX];
110 const T G22 = g33[ijk+e*LX*LX*LX];
111 const T G01 = g12[ijk+e*LX*LX*LX];
112 const T G02 = g13[ijk+e*LX*LX*LX];
113 const T G12 = g23[ijk+e*LX*LX*LX];
114 const T H1 = h1[ijk+e*LX*LX*LX];
115 T rtmp = 0.0;
116 T stmp = 0.0;
117 T ttmp = 0.0;
118#pragma unroll
119 for (int l = 0; l<LX; l++){
120 rtmp = rtmp + shdx[i+l*LX] * shu[l+j*LX+k*LX*LX];
121 stmp = stmp + shdy[j+l*LX] * shu[i+l*LX+k*LX*LX];
122 ttmp = ttmp + shdz[k+l*LX] * shu[i+j*LX+l*LX*LX];
123 }
124 shur[ijk] = H1 * (G00 * rtmp + G01 * stmp + G02 * ttmp);
125 shus[ijk] = H1 * (G01 * rtmp + G11 * stmp + G12 * ttmp);
126 shut[ijk] = H1 * (G02 * rtmp + G12 * stmp + G22 * ttmp);
127 }
128 }
129
131
132 for (int n=0; n<nchunks; n++){
133 const int ijk = iii+n*CHUNKS;
134 const int jk = ijk/LX;
135 const int k = jk/LX;
136 const int j = jk-k*LX;
137 const int i = ijk-jk*LX;
138 if (i<LX && j<LX && k<LX && ijk <LX*LX*LX){
139 T wijke = 0.0;
140#pragma unroll
141 for (int l = 0; l<LX; l++){
142 wijke = wijke
143 + shdxt[i+l*LX] * shur[l+j*LX+k*LX*LX]
144 + shdyt[j+l*LX] * shus[i+l*LX+k*LX*LX]
145 + shdzt[k+l*LX] * shut[i+j*LX+l*LX*LX];
146 }
147 w[ijk+e*LX*LX*LX] = wijke;
148 }
149 }
150}
151
152template< typename T, const int LX, const int EB >
155 const T * __restrict__ u,
166 const int nelv) {
167
168 /* Element independent, one copy per block */
169 __shared__ T shdx[LX * LX];
172
173 /* One slice per element in the block */
177
178 static_assert(sizeof(shdx) +
179 sizeof(shdy) +
180 sizeof(shdz) +
181 sizeof(shu) +
182 sizeof(shur) +
183 sizeof(shus)
185 "kstep block exceeds the LDS budget");
186
190
191 /* Threads past the last element still have to reach the block wide
192 barriers below, so clamp their reads and drop their stores rather
193 than returning early. At EB == 1 the grid covers nelv exactly, so all
194 of this bookkeeping is constant folded and the kernel is exactly what
195 it was before blocking */
196 const int eb = (EB == 1) ? 0 : threadIdx.z;
197 const int e_blk = blockIdx.x * EB + eb;
198 const bool active = (EB == 1) ? true : (e_blk < nelv);
199 const int e = active ? e_blk : (nelv - 1);
200 const int j = threadIdx.y;
201 const int i = threadIdx.x;
202 const int ij = i + j*LX;
203 const int sh = eb*LX*LX;
204 const int ele = e*LX*LX*LX;
205
206 if (eb == 0) {
207 shdx[ij] = dx[ij];
208 shdy[ij] = dy[ij];
209 shdz[ij] = dz[ij];
210 }
211
212#pragma unroll
213 for(int k = 0; k < LX; ++k){
214 ru[k] = u[ij + k*LX*LX + ele];
215 rw[k] = 0.0;
216 }
217
218
220#pragma unroll
221 for (int k = 0; k < LX; ++k){
222 const int ijk = ij + k*LX*LX;
223 const T G00 = g11[ijk+ele];
224 const T G11 = g22[ijk+ele];
225 const T G22 = g33[ijk+ele];
226 const T G01 = g12[ijk+ele];
227 const T G02 = g13[ijk+ele];
228 const T G12 = g23[ijk+ele];
229 const T H1 = h1[ijk+ele];
230 T ttmp = 0.0;
231 shu[sh + ij] = ru[k];
232#pragma unroll
233 for (int l = 0; l < LX; l++){
234 ttmp += shdz[k+l*LX] * ru[l];
235 }
237
238 T rtmp = 0.0;
239 T stmp = 0.0;
240#pragma unroll
241 for (int l = 0; l < LX; l++){
242 rtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
243 stmp += shdy[j+l*LX] * shu[sh + i+l*LX];
244 }
245 shur[sh + ij] = H1
246 * (G00 * rtmp
247 + G01 * stmp
248 + G02 * ttmp);
249 shus[sh + ij] = H1
250 * (G01 * rtmp
251 + G11 * stmp
252 + G12 * ttmp);
253 rut = H1
254 * (G02 * rtmp
255 + G12 * stmp
256 + G22 * ttmp);
257
259
260 T wijke = 0.0;
261#pragma unroll
262 for (int l = 0; l < LX; l++){
263 wijke += shur[sh + l+j*LX] * shdx[l+i*LX];
264 rw[l] += rut * shdz[k+l*LX];
265 wijke += shus[sh + i+l*LX] * shdy[l + j*LX];
266 }
267 rw[k] += wijke;
268 }
269 if (active) {
270#pragma unroll
271 for (int k = 0; k < LX; ++k){
272 w[ij + k*LX*LX + ele] = rw[k];
273 }
274 }
275}
276
282template< typename T, const int LX, const int EB >
285 const T * __restrict__ u,
286 const T * __restrict__ dx,
287 const T * __restrict__ dy,
288 const T * __restrict__ dz,
289 const T * __restrict__ h1,
290 const T * __restrict__ g11,
291 const T * __restrict__ g22,
292 const T * __restrict__ g33,
293 const T * __restrict__ g12,
294 const T * __restrict__ g13,
295 const T * __restrict__ g23,
296 const int nelv) {
297
298 /* Element independent, one copy per block */
299 __shared__ T shdx[LX * (LX+1)];
300 __shared__ T shdy[LX * (LX+1)];
301 __shared__ T shdz[LX * (LX+1)];
302
303 /* One slice per element in the block */
304 __shared__ T shu[EB * LX * (LX+1)];
305 __shared__ T shur[EB * LX * LX]; // only accessed using fastest dimension
306 __shared__ T shus[EB * LX * (LX+1)];
307
308 static_assert(sizeof(shdx) +
309 sizeof(shdy) +
310 sizeof(shdz) +
311 sizeof(shu) +
312 sizeof(shur) +
313 sizeof(shus)
315 "kstep block exceeds the LDS budget");
316
317 T ru[LX];
318 T rw[LX];
319 T rut;
320
321 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
322 constant folded away and the kernel is exactly what it was before */
323 const int eb = (EB == 1) ? 0 : threadIdx.z;
324 const int e_blk = blockIdx.x * EB + eb;
325 const bool active = (EB == 1) ? true : (e_blk < nelv);
326 const int e = active ? e_blk : (nelv - 1);
327 const int j = threadIdx.y;
328 const int i = threadIdx.x;
329 const int ij = i + j*LX;
330 const int ij_p = i + j*(LX+1);
331 const int sh = eb*LX*LX;
332 const int sh_p = eb*LX*(LX+1);
333 const int ele = e*LX*LX*LX;
334
335 if (eb == 0) {
336 shdx[ij_p] = dx[ij];
337 shdy[ij_p] = dy[ij];
338 shdz[ij_p] = dz[ij];
339 }
340
341#pragma unroll
342 for(int k = 0; k < LX; ++k){
343 ru[k] = u[ij + k*LX*LX + ele];
344 rw[k] = 0.0;
345 }
346
347
349#pragma unroll
350 for (int k = 0; k < LX; ++k){
351 const int ijk = ij + k*LX*LX;
352 const T G00 = g11[ijk+ele];
353 const T G11 = g22[ijk+ele];
354 const T G22 = g33[ijk+ele];
355 const T G01 = g12[ijk+ele];
356 const T G02 = g13[ijk+ele];
357 const T G12 = g23[ijk+ele];
358 const T H1 = h1[ijk+ele];
359 T ttmp = 0.0;
360 shu[sh_p + ij_p] = ru[k];
361#pragma unroll
362 for (int l = 0; l < LX; l++){
363 ttmp += shdz[k+l*(LX+1)] * ru[l];
364 }
366
367 T rtmp = 0.0;
368 T stmp = 0.0;
369#pragma unroll
370 for (int l = 0; l < LX; l++){
371 rtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
372 stmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
373 }
374 shur[sh + ij] = H1
375 * (G00 * rtmp
376 + G01 * stmp
377 + G02 * ttmp);
378 shus[sh_p + ij_p] = H1
379 * (G01 * rtmp
380 + G11 * stmp
381 + G12 * ttmp);
382 rut = H1
383 * (G02 * rtmp
384 + G12 * stmp
385 + G22 * ttmp);
386
388
389 T wijke = 0.0;
390#pragma unroll
391 for (int l = 0; l < LX; l++){
392 wijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
393 rw[l] += rut * shdz[k+l*(LX+1)];
394 wijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
395 }
396 rw[k] += wijke;
397 }
398 if (active) {
399#pragma unroll
400 for (int k = 0; k < LX; ++k){
401 w[ij + k*LX*LX + ele] = rw[k];
402 }
403 }
404}
405
406
446#if defined(__gfx90a__) || defined(__gfx942__)
447
448/* Matrix-core axhelm for one element of order LX-1 (T = float or double).
449 *
450 * NWF cooperating wavefronts (blockDim = (64, NWF, 1)) process one element,
451 * sharing a single staged cube in LDS. The matrix-core column tiles are
452 * striped across the wavefronts (wf = threadIdx.y) via mfma_contract_sel, while
453 * the staging, geometry and write-back passes parallelise over the WPE*64
454 * threads that serve one element (gtid); only the derivative matrices are
455 * staged by the whole block. The three accumulating divergence contractions
456 * stay separated
457 * by __syncthreads and each wavefront owns disjoint output columns, so the
458 * accumulation is race-free. NWF = 1 reproduces the single-wavefront kernel.
459 *
460 * mfma_contract_sel routes double precision through the batched 4x4x4 matrix
461 * core (full M-utilisation) and single precision through the 16x16x4 tile. */
462template< typename T, const int LX, const int NWF, const int TILE >
464 const T * __restrict__ u,
465 const T * __restrict__ dx,
466 const T * __restrict__ dy,
467 const T * __restrict__ dz,
468 const T * __restrict__ h1,
469 const T * __restrict__ g11,
470 const T * __restrict__ g22,
471 const T * __restrict__ g33,
472 const T * __restrict__ g12,
473 const T * __restrict__ g13,
474 const T * __restrict__ g23,
475 const int nelv) {
476 const int LX2 = LX * LX;
477 const int LX3 = LX * LX * LX;
478
479 /* NWF wavefronts per block, WPE of them cooperating on one element and the
480 block covering EB elements, see the note in mfma_kernel.h. At LX = 4 the
481 contraction offers one column group, so WPE is 1 and every wavefront gets
482 an element of its own rather than idling. */
483 enum { PAD = NEKO_MFMA_PAD_N(LX, sizeof(T)),
484 CUBE = NEKO_MFMA_CUBE_N(LX, sizeof(T)),
485 DMAT = NEKO_MFMA_DMAT_N(LX, sizeof(T)),
486 EB = NEKO_MFMA_EB_N(NWF, LX, sizeof(T)),
487 WPE = NWF / EB,
488 GNTHR = WPE * 64, /* threads serving one element */
489 SPT = NEKO_MFMA_SPT_N(WPE, LX, sizeof(T)),
490 /* Geometry prefetched into registers, or read where it is used?
491 Same budget and the same macro the tuner logs with, see the note
492 on the prefetch below */
493 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
494 NG = GREG ? SPT : 1 };
495 static_assert(WPE * EB == NWF,
496 "wavefronts per block must split evenly over the elements");
497
501 __shared__ T shu[EB * CUBE]; // input u, later reused as output w
502 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
503 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
504 __shared__ T sht[EB * CUBE]; // d/dt -> St
505
506 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
507 sizeof(shu) + sizeof(shr) + sizeof(shs) + sizeof(sht)
509 "mfma block exceeds the shared memory budget");
510
511 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
512 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
513 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
514 const int nthr = NWF * 64;
515
516 const int eb = wf / WPE; // which element this wavefront serves
517 const int sub = wf % WPE; // its rank among that element's waves
518 const int gtid = sub * 64 + lane; // thread id within the element group
519
520 /* Threads past the last element still have to reach the block wide
521 barriers, so clamp their reads and drop their stores rather than
522 returning early. At EB == 1 the grid covers nelv exactly and this is
523 constant folded away */
524 const int e_blk = blockIdx.x * EB + eb;
525 const bool active = (EB == 1) ? true : (e_blk < nelv);
526 const int e = active ? e_blk : (nelv - 1);
527 const int ele = e * LX3;
528 const int sh = eb * CUBE;
529
530 /* The element point each of this thread's LDS slots holds, or -1 for a pad
531 slot, decoded once and reused by every pass below. The three passes walk
532 slots at unit stride in LDS -- which is what keeps them off each other's
533 banks once the cube is padded -- and this array is the translation back
534 to the caller's LX^3 storage, see mfma_slot_point(). Where the cube is not
535 padded the decode folds away to the identity and this is the old
536 p = gtid + q*GNTHR loop. */
537 int goff[SPT];
538#pragma unroll
539 for (int q = 0; q < SPT; q++) {
540 const int sl = gtid + q * GNTHR;
542 }
543
544 /* Reference derivative matrices, one copy shared by every element. The
545 staged copy carries the padded column stride, so the linear index of the
546 caller's LX x LX copy is decoded here; the global reads stay contiguous
547 and it is the LDS side that gains the stride, which is the point. */
548 for (int p = tid; p < LX2; p += nthr) {
549 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
550 shdx[sd] = dx[p];
551 shdy[sd] = dy[p];
552 shdz[sd] = dz[p];
553 }
554 /* Element-local field, staged by the wavefronts that own it */
555#pragma unroll
556 for (int q = 0; q < SPT; q++) {
557 if (goff[q] >= 0)
558 shu[sh + gtid + q * GNTHR] = u[goff[q] + ele];
559 }
560
562
563 /*
564 * The seven geometric factors, issued here rather than where they are used.
565 *
566 * They are needed by the pointwise pass, which sits behind the gradient
567 * contractions and a barrier, so reading them there exposes the full global
568 * latency with nothing to cover it: this kernel runs at three wavefronts per
569 * SIMD in double precision at LX = 8, a third of what the 1D kernel that
570 * beats it reaches, so there is no other occupancy to hide behind. Issued
571 * here they are in flight across the three contractions instead.
572 *
573 * The cost is 7 * SPT * (sizeof(T)/4) VGPRs, and past a budget that stops
574 * paying -- 189 values at LX = 12 on a single wavefront, against the 256 a
575 * lane addresses -- so it is chosen per instantiation by the same
576 * NEKO_MFMA_VECTOR_GREG the vector kernel uses, and reported by the tuner
577 * for the same reason: it is derived from the wavefront count rather than
578 * swept, so two neighbouring candidates can differ in more than block shape.
579 */
580 T rG00[NG], rG11[NG], rG22[NG];
581 T rG01[NG], rG02[NG], rG12[NG];
582 T rH1[NG];
583
584 if (GREG) {
585#pragma unroll
586 for (int q = 0; q < NG; q++) {
587 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
588 rG00[q] = g11[gp];
589 rG11[q] = g22[gp];
590 rG22[q] = g33[gp];
591 rG01[q] = g12[gp];
592 rG02[q] = g13[gp];
593 rG12[q] = g23[gp];
594 rH1[q] = h1[gp];
595 }
596 }
597
598 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout. */
599 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD>::run(shr + sh,
600 shdx, shu + sh, lane, sub);
601 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD>::run(shs + sh,
602 shdy, shu + sh, lane, sub);
603 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD>::run(sht + sh,
604 shdz, shu + sh, lane, sub);
605
607
608 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht. */
609#pragma unroll
610 for (int q = 0; q < SPT; q++) {
611 if (goff[q] >= 0) {
612 const int sl = sh + gtid + q * GNTHR;
613 T G00, G11, G22, G01, G02, G12, H1;
614 if (GREG) {
615 /* GREG is a compile time constant, so only one of these two bodies is
616 emitted; the clamp keeps the index inside the NG == 1 array that the
617 other one never reads from */
618 const int r = GREG ? q : 0;
619 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
620 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
621 H1 = rH1[r];
622 } else {
623 const int gp = goff[q] + ele;
624 G00 = g11[gp]; G11 = g22[gp]; G22 = g33[gp];
625 G01 = g12[gp]; G02 = g13[gp]; G12 = g23[gp];
626 H1 = h1[gp];
627 }
628 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
629 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
630 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
631 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
632 }
633 }
634
636
637 /* Divergence: w = Dr^T Sr + Ds^T Ss + Dt^T St, accumulated in shu (= w).
638 The first contraction overwrites rather than accumulates, which is what
639 lets the separate zeroing pass over the cube -- and the barrier after it
640 -- go away entirely; the summation order of the three is unchanged, so
641 this is not a reassociation. All four (TRANSPOSE, ACCUM) pairs are covered
642 by the gfx90a read-out, so the non-accumulating one is no less verified
643 than the accumulating one. */
644 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD>::run(shu + sh,
645 shdx, shr + sh, lane, sub);
647 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD>::run(shu + sh,
648 shdy, shs + sh, lane, sub);
650 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD>::run(shu + sh,
651 shdz, sht + sh, lane, sub);
653
654 if (active) {
655#pragma unroll
656 for (int q = 0; q < SPT; q++) {
657 if (goff[q] >= 0)
658 w[goff[q] + ele] = shu[sh + gtid + q * GNTHR];
659 }
660 }
661}
662#endif // __gfx90a__ || __gfx942__
663
664/*
665 * Compile-time dispatch onto the MFMA element kernel. The launch macros in
666 * ax_helm.hip are written for every LX the operator dispatches and for
667 * whatever `real` is, so every combination has to compile; the ones the
668 * strategy does not cover -- LX outside the supported range, a build without
669 * a matrix-core arch -- resolve to this no-op. The autotuner never selects
670 * the strategy for them, so the no-op is unreachable at runtime, see
671 * mfma_lx_supported() and hip_have_mfma() in mfma_kernel.h.
672 */
673template< typename T, const int LX, const int NWF, const int TILE >
675 __device__ static void run(T *, const T *, const T *, const T *, const T *,
676 const T *, const T *, const T *, const T *,
677 const T *, const T *, const T *, const int) {}
678};
679
680#if defined(__gfx90a__) || defined(__gfx942__)
681
682/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
683#define NEKO_AX_HELM_MFMA_DISPATCH(TYPE, LXV) \
684 template< const int NWF, const int TILE > \
685 struct ax_helm_mfma_dispatch< TYPE, LXV, NWF, TILE > { \
686 __device__ static void run(TYPE *w, const TYPE *u, \
687 const TYPE *dx, const TYPE *dy, \
688 const TYPE *dz, const TYPE *h1, \
689 const TYPE *g11, const TYPE *g22, \
690 const TYPE *g33, const TYPE *g12, \
691 const TYPE *g13, const TYPE *g23, \
692 const int nelv) { \
693 ax_helm_mfma_elem< TYPE, LXV, NWF, TILE >(w, u, dx, dy, dz, h1, \
694 g11, g22, g33, g12, g13, g23, \
695 nelv); \
696 } \
697 }
698
717
718#endif // __gfx90a__ || __gfx942__
719
720/*
721 * Note the bare __launch_bounds__ rather than NEKO_EB_BOUNDS: the kstep
722 * kernels ask for three waves per SIMD, and this kernel was validated on
723 * gfx90a/gfx942 without that constraint. Keep it byte-identical to the
724 * configuration that was confirmed on hardware.
725 */
726template< typename T, const int LX, const int NWF, const int TILE >
729 const T * __restrict__ u,
730 const T * __restrict__ dx,
731 const T * __restrict__ dy,
732 const T * __restrict__ dz,
733 const T * __restrict__ h1,
734 const T * __restrict__ g11,
735 const T * __restrict__ g22,
736 const T * __restrict__ g33,
737 const T * __restrict__ g12,
738 const T * __restrict__ g13,
739 const T * __restrict__ g23,
740 const int nelv) {
741
743 g11, g22, g33,
744 g12, g13, g23, nelv);
745}
746
747/*
748 * Vector versions
749 */
750
751template< typename T, const int LX, const int EB >
756 const T * __restrict__ u,
757 const T * __restrict__ v,
758 const T * __restrict__ w,
759 const T * __restrict__ dx,
760 const T * __restrict__ dy,
761 const T * __restrict__ dz,
762 const T * __restrict__ h1,
763 const T * __restrict__ g11,
764 const T * __restrict__ g22,
765 const T * __restrict__ g33,
766 const T * __restrict__ g12,
767 const T * __restrict__ g13,
768 const T * __restrict__ g23,
769 const int nelv) {
770
771 /* Element independent, one copy per block */
772 __shared__ T shdx[LX * LX];
773 __shared__ T shdy[LX * LX];
774 __shared__ T shdz[LX * LX];
775
776 /* One slice per element in the block */
777 __shared__ T shu[EB * LX * LX];
778 __shared__ T shur[EB * LX * LX];
779 __shared__ T shus[EB * LX * LX];
780
784
788
789 static_assert(sizeof(shdx) +
790 sizeof(shdy) +
791 sizeof(shdz) +
792 sizeof(shu) +
793 sizeof(shur) +
794 sizeof(shus) +
795 sizeof(shv) +
796 sizeof(shvr) +
797 sizeof(shvs) +
798 sizeof(shw) +
799 sizeof(shwr) +
800 sizeof(shws)
802 "kstep block exceeds the LDS budget");
803
804 T ru[LX];
806 T rw[LX];
807
811
812 T rut;
815
816 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
817 constant folded away and the kernel is exactly what it was before */
818 const int eb = (EB == 1) ? 0 : threadIdx.z;
819 const int e_blk = blockIdx.x * EB + eb;
820 const bool active = (EB == 1) ? true : (e_blk < nelv);
821 const int e = active ? e_blk : (nelv - 1);
822 const int j = threadIdx.y;
823 const int i = threadIdx.x;
824 const int ij = i + j*LX;
825 const int sh = eb*LX*LX;
826 const int ele = e*LX*LX*LX;
827
828 if (eb == 0) {
829 shdx[ij] = dx[ij];
830 shdy[ij] = dy[ij];
831 shdz[ij] = dz[ij];
832 }
833
834#pragma unroll
835 for(int k = 0; k < LX; ++k){
836 ru[k] = u[ij + k*LX*LX + ele];
837 ruw[k] = 0.0;
838
839 rv[k] = v[ij + k*LX*LX + ele];
840 rvw[k] = 0.0;
841
842 rw[k] = w[ij + k*LX*LX + ele];
843 rww[k] = 0.0;
844 }
845
846
848#pragma unroll
849 for (int k = 0; k < LX; ++k){
850 const int ijk = ij + k*LX*LX;
851 const T G00 = g11[ijk+ele];
852 const T G11 = g22[ijk+ele];
853 const T G22 = g33[ijk+ele];
854 const T G01 = g12[ijk+ele];
855 const T G02 = g13[ijk+ele];
856 const T G12 = g23[ijk+ele];
857 const T H1 = h1[ijk+ele];
858 T uttmp = 0.0;
859 T vttmp = 0.0;
860 T wttmp = 0.0;
861 shu[sh + ij] = ru[k];
862 shv[sh + ij] = rv[k];
863 shw[sh + ij] = rw[k];
864#pragma unroll
865 for (int l = 0; l < LX; l++){
866 uttmp += shdz[k+l*LX] * ru[l];
867 vttmp += shdz[k+l*LX] * rv[l];
868 wttmp += shdz[k+l*LX] * rw[l];
869 }
871
872 T urtmp = 0.0;
873 T ustmp = 0.0;
874
875 T vrtmp = 0.0;
876 T vstmp = 0.0;
877
878 T wrtmp = 0.0;
879 T wstmp = 0.0;
880#pragma unroll
881 for (int l = 0; l < LX; l++){
882 urtmp += shdx[i+l*LX] * shu[sh + l+j*LX];
883 ustmp += shdy[j+l*LX] * shu[sh + i+l*LX];
884
885 vrtmp += shdx[i+l*LX] * shv[sh + l+j*LX];
886 vstmp += shdy[j+l*LX] * shv[sh + i+l*LX];
887
888 wrtmp += shdx[i+l*LX] * shw[sh + l+j*LX];
889 wstmp += shdy[j+l*LX] * shw[sh + i+l*LX];
890 }
891
892 shur[sh + ij] = H1
893 * (G00 * urtmp
894 + G01 * ustmp
895 + G02 * uttmp);
896 shus[sh + ij] = H1
897 * (G01 * urtmp
898 + G11 * ustmp
899 + G12 * uttmp);
900 rut = H1
901 * (G02 * urtmp
902 + G12 * ustmp
903 + G22 * uttmp);
904
905 shvr[sh + ij] = H1
906 * (G00 * vrtmp
907 + G01 * vstmp
908 + G02 * vttmp);
909 shvs[sh + ij] = H1
910 * (G01 * vrtmp
911 + G11 * vstmp
912 + G12 * vttmp);
913 rvt = H1
914 * (G02 * vrtmp
915 + G12 * vstmp
916 + G22 * vttmp);
917
918 shwr[sh + ij] = H1
919 * (G00 * wrtmp
920 + G01 * wstmp
921 + G02 * wttmp);
922 shws[sh + ij] = H1
923 * (G01 * wrtmp
924 + G11 * wstmp
925 + G12 * wttmp);
926 rwt = H1
927 * (G02 * wrtmp
928 + G12 * wstmp
929 + G22 * wttmp);
930
932
933 T uwijke = 0.0;
934 T vwijke = 0.0;
935 T wwijke = 0.0;
936#pragma unroll
937 for (int l = 0; l < LX; l++){
938 uwijke += shur[sh + l+j*LX] * shdx[l+i*LX];
939 ruw[l] += rut * shdz[k+l*LX];
940 uwijke += shus[sh + i+l*LX] * shdy[l + j*LX];
941
942 vwijke += shvr[sh + l+j*LX] * shdx[l+i*LX];
943 rvw[l] += rvt * shdz[k+l*LX];
944 vwijke += shvs[sh + i+l*LX] * shdy[l + j*LX];
945
946 wwijke += shwr[sh + l+j*LX] * shdx[l+i*LX];
947 rww[l] += rwt * shdz[k+l*LX];
948 wwijke += shws[sh + i+l*LX] * shdy[l + j*LX];
949 }
950 ruw[k] += uwijke;
951 rvw[k] += vwijke;
952 rww[k] += wwijke;
953 }
954 if (active) {
955#pragma unroll
956 for (int k = 0; k < LX; ++k){
957 au[ij + k*LX*LX + ele] = ruw[k];
958 av[ij + k*LX*LX + ele] = rvw[k];
959 aw[ij + k*LX*LX + ele] = rww[k];
960 }
961 }
962}
963
964template< typename T, const int LX, const int EB >
967 T * __restrict__ av,
968 T * __restrict__ aw,
969 const T * __restrict__ u,
970 const T * __restrict__ v,
971 const T * __restrict__ w,
972 const T * __restrict__ dx,
973 const T * __restrict__ dy,
974 const T * __restrict__ dz,
975 const T * __restrict__ h1,
976 const T * __restrict__ g11,
977 const T * __restrict__ g22,
978 const T * __restrict__ g33,
979 const T * __restrict__ g12,
980 const T * __restrict__ g13,
981 const T * __restrict__ g23,
982 const int nelv) {
983
984 /* Element independent, one copy per block */
985 __shared__ T shdx[LX * (LX+1)];
986 __shared__ T shdy[LX * (LX+1)];
987 __shared__ T shdz[LX * (LX+1)];
988
989 /* One slice per element in the block */
990 __shared__ T shu[EB * LX * (LX+1)];
991 __shared__ T shur[EB * LX * LX];
992 __shared__ T shus[EB * LX * (LX+1)];
993
994 __shared__ T shv[EB * LX * (LX+1)];
995 __shared__ T shvr[EB * LX * LX];
996 __shared__ T shvs[EB * LX * (LX+1)];
997
998 __shared__ T shw[EB * LX * (LX+1)];
999 __shared__ T shwr[EB * LX * LX];
1000 __shared__ T shws[EB * LX * (LX+1)];
1001
1002 static_assert(sizeof(shdx) +
1003 sizeof(shdy) +
1004 sizeof(shdz) +
1005 sizeof(shu) +
1006 sizeof(shur) +
1007 sizeof(shus) +
1008 sizeof(shv) +
1009 sizeof(shvr) +
1010 sizeof(shvs) +
1011 sizeof(shw) +
1012 sizeof(shwr) +
1013 sizeof(shws)
1014 <= NEKO_EB_MAX_LDS,
1015 "kstep block exceeds the LDS budget");
1016
1017 T ru[LX];
1018 T rv[LX];
1019 T rw[LX];
1020
1021 T ruw[LX];
1022 T rvw[LX];
1023 T rww[LX];
1024
1025 T rut;
1026 T rvt;
1027 T rwt;
1028
1029 /* At EB == 1 the grid covers nelv exactly, so the blocking bookkeeping is
1030 constant folded away and the kernel is exactly what it was before */
1031 const int eb = (EB == 1) ? 0 : threadIdx.z;
1032 const int e_blk = blockIdx.x * EB + eb;
1033 const bool active = (EB == 1) ? true : (e_blk < nelv);
1034 const int e = active ? e_blk : (nelv - 1);
1035 const int j = threadIdx.y;
1036 const int i = threadIdx.x;
1037 const int ij = i + j*LX;
1038 const int ij_p = i + j*(LX+1);
1039 const int sh = eb*LX*LX;
1040 const int sh_p = eb*LX*(LX+1);
1041 const int ele = e*LX*LX*LX;
1042
1043 if (eb == 0) {
1044 shdx[ij_p] = dx[ij];
1045 shdy[ij_p] = dy[ij];
1046 shdz[ij_p] = dz[ij];
1047 }
1048
1049#pragma unroll
1050 for(int k = 0; k < LX; ++k){
1051 ru[k] = u[ij + k*LX*LX + ele];
1052 ruw[k] = 0.0;
1053
1054 rv[k] = v[ij + k*LX*LX + ele];
1055 rvw[k] = 0.0;
1056
1057 rw[k] = w[ij + k*LX*LX + ele];
1058 rww[k] = 0.0;
1059 }
1060
1061
1062 __syncthreads();
1063#pragma unroll
1064 for (int k = 0; k < LX; ++k){
1065 const int ijk = ij + k*LX*LX;
1066 const T G00 = g11[ijk+ele];
1067 const T G11 = g22[ijk+ele];
1068 const T G22 = g33[ijk+ele];
1069 const T G01 = g12[ijk+ele];
1070 const T G02 = g13[ijk+ele];
1071 const T G12 = g23[ijk+ele];
1072 const T H1 = h1[ijk+ele];
1073 T uttmp = 0.0;
1074 T vttmp = 0.0;
1075 T wttmp = 0.0;
1076 shu[sh_p + ij_p] = ru[k];
1077 shv[sh_p + ij_p] = rv[k];
1078 shw[sh_p + ij_p] = rw[k];
1079#pragma unroll
1080 for (int l = 0; l < LX; l++){
1081 uttmp += shdz[k+l*(LX+1)] * ru[l];
1082 vttmp += shdz[k+l*(LX+1)] * rv[l];
1083 wttmp += shdz[k+l*(LX+1)] * rw[l];
1084 }
1086
1087 T urtmp = 0.0;
1088 T ustmp = 0.0;
1089
1090 T vrtmp = 0.0;
1091 T vstmp = 0.0;
1092
1093 T wrtmp = 0.0;
1094 T wstmp = 0.0;
1095#pragma unroll
1096 for (int l = 0; l < LX; l++){
1097 urtmp += shdx[i+l*(LX+1)] * shu[sh_p + l+j*(LX+1)];
1098 ustmp += shdy[j+l*(LX+1)] * shu[sh_p + i+l*(LX+1)];
1099
1100 vrtmp += shdx[i+l*(LX+1)] * shv[sh_p + l+j*(LX+1)];
1101 vstmp += shdy[j+l*(LX+1)] * shv[sh_p + i+l*(LX+1)];
1102
1103 wrtmp += shdx[i+l*(LX+1)] * shw[sh_p + l+j*(LX+1)];
1104 wstmp += shdy[j+l*(LX+1)] * shw[sh_p + i+l*(LX+1)];
1105 }
1106
1107 shur[sh + ij] = H1
1108 * (G00 * urtmp
1109 + G01 * ustmp
1110 + G02 * uttmp);
1111 shus[sh_p + ij_p] = H1
1112 * (G01 * urtmp
1113 + G11 * ustmp
1114 + G12 * uttmp);
1115 rut = H1
1116 * (G02 * urtmp
1117 + G12 * ustmp
1118 + G22 * uttmp);
1119
1120 shvr[sh + ij] = H1
1121 * (G00 * vrtmp
1122 + G01 * vstmp
1123 + G02 * vttmp);
1124 shvs[sh_p + ij_p] = H1
1125 * (G01 * vrtmp
1126 + G11 * vstmp
1127 + G12 * vttmp);
1128 rvt = H1
1129 * (G02 * vrtmp
1130 + G12 * vstmp
1131 + G22 * vttmp);
1132
1133 shwr[sh + ij] = H1
1134 * (G00 * wrtmp
1135 + G01 * wstmp
1136 + G02 * wttmp);
1137 shws[sh_p + ij_p] = H1
1138 * (G01 * wrtmp
1139 + G11 * wstmp
1140 + G12 * wttmp);
1141 rwt = H1
1142 * (G02 * wrtmp
1143 + G12 * wstmp
1144 + G22 * wttmp);
1145
1147
1148 T uwijke = 0.0;
1149 T vwijke = 0.0;
1150 T wwijke = 0.0;
1151#pragma unroll
1152 for (int l = 0; l < LX; l++){
1153 uwijke += shur[sh + l+j*LX] * shdx[l+i*(LX+1)];
1154 ruw[l] += rut * shdz[k+l*(LX+1)];
1155 uwijke += shus[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1156
1157 vwijke += shvr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1158 rvw[l] += rvt * shdz[k+l*(LX+1)];
1159 vwijke += shvs[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1160
1161 wwijke += shwr[sh + l+j*LX] * shdx[l+i*(LX+1)];
1162 rww[l] += rwt * shdz[k+l*(LX+1)];
1163 wwijke += shws[sh_p + i+l*(LX+1)] * shdy[l + j*(LX+1)];
1164 }
1165 ruw[k] += uwijke;
1166 rvw[k] += vwijke;
1167 rww[k] += wwijke;
1168 }
1169 if (active) {
1170#pragma unroll
1171 for (int k = 0; k < LX; ++k){
1172 au[ij + k*LX*LX + ele] = ruw[k];
1173 av[ij + k*LX*LX + ele] = rvw[k];
1174 aw[ij + k*LX*LX + ele] = rww[k];
1175 }
1176 }
1177}
1178
1211#if defined(__gfx90a__) || defined(__gfx942__)
1212
1213/* Matrix-core vector axhelm for one element of order LX-1 (T = float or
1214 * double), see ax_helm_mfma_elem for the block geometry this shares. */
1215template< typename T, const int LX, const int NWF, const int TILE >
1217 T * __restrict__ av,
1218 T * __restrict__ aw,
1219 const T * __restrict__ u,
1220 const T * __restrict__ v,
1221 const T * __restrict__ w,
1222 const T * __restrict__ dx,
1223 const T * __restrict__ dy,
1224 const T * __restrict__ dz,
1225 const T * __restrict__ h1,
1226 const T * __restrict__ g11,
1227 const T * __restrict__ g22,
1228 const T * __restrict__ g33,
1229 const T * __restrict__ g12,
1230 const T * __restrict__ g13,
1231 const T * __restrict__ g23,
1232 const int nelv) {
1233 const int LX2 = LX * LX;
1234 const int LX3 = LX * LX * LX;
1235
1236 /* NWF wavefronts per block, WPE of them cooperating on one element and the
1237 block covering EB elements, exactly as in ax_helm_mfma_elem */
1238 enum { PAD = NEKO_MFMA_PAD_N(LX, sizeof(T)),
1239 CUBE = NEKO_MFMA_CUBE_N(LX, sizeof(T)),
1240 DMAT = NEKO_MFMA_DMAT_N(LX, sizeof(T)),
1241 EB = NEKO_MFMA_EB_N(NWF, LX, sizeof(T)),
1242 WPE = NWF / EB,
1243 GNTHR = WPE * 64, /* threads serving one element */
1244 SPT = NEKO_MFMA_SPT_N(WPE, LX, sizeof(T)),
1245 /* Geometry in registers across the components, or re-read per
1246 component? The same macro the tuner logs with, so the reported
1247 mode cannot drift from the compiled one */
1248 GREG = NEKO_MFMA_VECTOR_GREG_N(SPT, sizeof(T)),
1249 /* One slot when the factors are re-read, so the arrays below cost
1250 nothing in that case */
1251 NG = GREG ? SPT : 1 };
1252 static_assert(WPE * EB == NWF,
1253 "wavefronts per block must split evenly over the elements");
1254
1258 __shared__ T shc[EB * CUBE]; // component in, later its result out
1259 __shared__ T shr[EB * CUBE]; // d/dr -> Sr
1260 __shared__ T shs[EB * CUBE]; // d/ds -> Ss
1261 __shared__ T sht[EB * CUBE]; // d/dt -> St
1262
1263 static_assert(sizeof(shdx) + sizeof(shdy) + sizeof(shdz) +
1264 sizeof(shc) + sizeof(shr) + sizeof(shs) + sizeof(sht)
1265 <= NEKO_EB_MAX_LDS,
1266 "mfma vector block exceeds the shared memory budget");
1267
1268 const int lane = threadIdx.x; // 0..63 : lane within a wavefront
1269 const int wf = threadIdx.y; // 0..NWF-1 : which wavefront
1270 const int tid = wf * 64 + lane; // 0..NWF*64-1 : block-wide thread id
1271 const int nthr = NWF * 64;
1272
1273 const int eb = wf / WPE; // which element this wavefront serves
1274 const int sub = wf % WPE; // its rank among that element's waves
1275 const int gtid = sub * 64 + lane; // thread id within the element group
1276
1277 /* Threads past the last element still have to reach the block wide
1278 barriers, so clamp their reads and drop their stores rather than
1279 returning early. At EB == 1 the grid covers nelv exactly and this is
1280 constant folded away */
1281 const int e_blk = blockIdx.x * EB + eb;
1282 const bool active = (EB == 1) ? true : (e_blk < nelv);
1283 const int e = active ? e_blk : (nelv - 1);
1284 const int ele = e * LX3;
1285 const int sh = eb * CUBE;
1286
1287 /* The element point behind each of this thread's LDS slots, decoded once,
1288 see ax_helm_mfma_elem */
1289 int goff[SPT];
1290#pragma unroll
1291 for (int q = 0; q < SPT; q++) {
1292 const int sl = gtid + q * GNTHR;
1294 }
1295
1296 /* Reference derivative matrices, one copy shared by every element. The
1297 staged copy carries the padded column stride, so the linear index of the
1298 caller's LX x LX copy is decoded here; the global reads stay contiguous
1299 and it is the LDS side that gains the stride, which is the point. */
1300 for (int p = tid; p < LX2; p += nthr) {
1301 const int sd = mfma_dmat_idx<LX, PAD>(p % LX, p / LX);
1302 shdx[sd] = dx[p];
1303 shdy[sd] = dy[p];
1304 shdz[sd] = dz[p];
1305 }
1306
1307 /* The geometric factors, read once and reused by all three components. The
1308 pointwise pass below strides the slots the same way, so the value for slot
1309 gtid + q * GNTHR stays in slot q and no index array is needed */
1310 T rG00[NG], rG11[NG], rG22[NG];
1311 T rG01[NG], rG02[NG], rG12[NG];
1312 T rH1[NG];
1313
1314 if (GREG) {
1315#pragma unroll
1316 for (int q = 0; q < NG; q++) {
1317 const int gp = (goff[q] >= 0) ? (goff[q] + ele) : ele;
1318 rG00[q] = g11[gp];
1319 rG11[q] = g22[gp];
1320 rG22[q] = g33[gp];
1321 rG01[q] = g12[gp];
1322 rG02[q] = g13[gp];
1323 rG12[q] = g23[gp];
1324 rH1[q] = h1[gp];
1325 }
1326 }
1327
1328#pragma unroll
1329 for (int c = 0; c < 3; c++) {
1330 /* Selected rather than indexed out of a pointer array: an array of
1331 pointers indexed by a loop counter is only free if the loop is fully
1332 unrolled, and this one carries barriers */
1333 const T * const cin = (c == 0) ? u : (c == 1) ? v : w;
1334 T * const cout = (c == 0) ? au : (c == 1) ? av : aw;
1335
1336 /* Element-local component, staged by the wavefronts that own it, over the
1337 same slot set the write-back at the end of the loop uses -- which is
1338 what makes the barrier between the two unnecessary, see there */
1339#pragma unroll
1340 for (int q = 0; q < SPT; q++) {
1341 if (goff[q] >= 0)
1342 shc[sh + gtid + q * GNTHR] = cin[goff[q] + ele];
1343 }
1344
1345 __syncthreads();
1346
1347 /* Gradient: ur, us, ut in canonical i + SJ*j + SK*k layout */
1348 mfma_contract_sel<T, LX, 0, false, false, WPE, TILE, PAD>::run(shr + sh,
1349 shdx, shc + sh, lane, sub);
1350 mfma_contract_sel<T, LX, 1, false, false, WPE, TILE, PAD>::run(shs + sh,
1351 shdy, shc + sh, lane, sub);
1352 mfma_contract_sel<T, LX, 2, false, false, WPE, TILE, PAD>::run(sht + sh,
1353 shdz, shc + sh, lane, sub);
1354
1355 __syncthreads();
1356
1357 /* Geometry (pointwise): (ur,us,ut) -> (Sr,Ss,St), reusing shr/shs/sht.
1358 The staged component is dead once the gradient is out and does not need
1359 clearing either: the first divergence contraction below overwrites it */
1360#pragma unroll
1361 for (int q = 0; q < SPT; q++) {
1362 if (goff[q] >= 0) {
1363 const int sl = sh + gtid + q * GNTHR;
1364 T G00, G11, G22, G01, G02, G12, H1;
1365 if (GREG) {
1366 /* GREG is a compile time constant, so only one of these two bodies
1367 is emitted; the clamp keeps the index inside the NG == 1 array
1368 that the other one never reads from */
1369 const int r = GREG ? q : 0;
1370 G00 = rG00[r]; G11 = rG11[r]; G22 = rG22[r];
1371 G01 = rG01[r]; G02 = rG02[r]; G12 = rG12[r];
1372 H1 = rH1[r];
1373 } else {
1374 const int gp = goff[q] + ele;
1375 G00 = g11[gp]; G11 = g22[gp]; G22 = g33[gp];
1376 G01 = g12[gp]; G02 = g13[gp]; G12 = g23[gp];
1377 H1 = h1[gp];
1378 }
1379 const T rr = shr[sl], ss = shs[sl], tt = sht[sl];
1380 shr[sl] = H1 * (G00 * rr + G01 * ss + G02 * tt);
1381 shs[sl] = H1 * (G01 * rr + G11 * ss + G12 * tt);
1382 sht[sl] = H1 * (G02 * rr + G12 * ss + G22 * tt);
1383 }
1384 }
1385
1386 __syncthreads();
1387
1388 /* Divergence: Dr^T Sr + Ds^T Ss + Dt^T St, the first one overwriting shc
1389 so that nothing has to clear it, see ax_helm_mfma_elem */
1390 mfma_contract_sel<T, LX, 0, true, false, WPE, TILE, PAD>::run(shc + sh,
1391 shdx, shr + sh, lane, sub);
1392 __syncthreads();
1393 mfma_contract_sel<T, LX, 1, true, true, WPE, TILE, PAD>::run(shc + sh,
1394 shdy, shs + sh, lane, sub);
1395 __syncthreads();
1396 mfma_contract_sel<T, LX, 2, true, true, WPE, TILE, PAD>::run(shc + sh,
1397 shdz, sht + sh, lane, sub);
1398 __syncthreads();
1399
1400 if (active) {
1401#pragma unroll
1402 for (int q = 0; q < SPT; q++) {
1403 if (goff[q] >= 0)
1404 cout[goff[q] + ele] = shc[sh + gtid + q * GNTHR];
1405 }
1406 }
1407
1408 /* Not required as the loops stand: the write-back above and the restage
1409 at the top of the next pass walk the same slot set per thread, so a
1410 thread only restages what it just read out itself, and no wavefront
1411 can run ahead into another's slots. Kept so that re-striding either
1412 loop -- coalescing the write-back over tid rather than gtid, say --
1413 cannot introduce a race silently */
1414 if (c < 2) {
1415 __syncthreads();
1416 }
1417 }
1418}
1419#endif // __gfx90a__ || __gfx942__
1420
1421/*
1422 * Compile-time dispatch onto the vector MFMA element kernel, see the note on
1423 * ax_helm_mfma_dispatch above. The vector operator dispatches every
1424 * 2 <= LX <= 16 and the strategy covers 4 <= LX <= 12, so this no-op is
1425 * reached at compile time for rather more instantiations than the scalar
1426 * one's -- and must stay unreachable at runtime: a no-op times as free and
1427 * would win the comparison, leaving stale values in au, av and aw. The
1428 * autotuner only offers the strategy where mfma_lx_supported() and
1429 * hip_have_mfma() both say yes, which is exactly the set instantiated below.
1430 */
1431template< typename T, const int LX, const int NWF, const int TILE >
1433 __device__ static void run(T *, T *, T *,
1434 const T *, const T *, const T *,
1435 const T *, const T *, const T *, const T *,
1436 const T *, const T *, const T *,
1437 const T *, const T *, const T *, const int) {}
1438};
1439
1440#if defined(__gfx90a__) || defined(__gfx942__)
1441
1442/* Keep in sync with mfma_lx_supported() in mfma_kernel.h */
1443#define NEKO_AX_HELM_MFMA_VECTOR_DISPATCH(TYPE, LXV) \
1444 template< const int NWF, const int TILE > \
1445 struct ax_helm_mfma_vector_dispatch< TYPE, LXV, NWF, TILE > { \
1446 __device__ static void run(TYPE *au, TYPE *av, TYPE *aw, \
1447 const TYPE *u, const TYPE *v, const TYPE *w, \
1448 const TYPE *dx, const TYPE *dy, \
1449 const TYPE *dz, const TYPE *h1, \
1450 const TYPE *g11, const TYPE *g22, \
1451 const TYPE *g33, const TYPE *g12, \
1452 const TYPE *g13, const TYPE *g23, \
1453 const int nelv) { \
1454 ax_helm_mfma_vector_elem< TYPE, LXV, NWF, TILE >(au, av, aw, u, v, w, \
1455 dx, dy, dz, h1, \
1456 g11, g22, g33, \
1457 g12, g13, g23, nelv); \
1458 } \
1459 }
1460
1479
1480#endif // __gfx90a__ || __gfx942__
1481
1482/* Named after its CUDA counterpart ax_helm_kernel_dmma_vector rather than
1483 after the adjacent ax_helm_kernel_vector_kstep, so that the matrix unit
1484 kernels read alike across the two backends.
1485
1486 Bare __launch_bounds__ rather than NEKO_EB_BOUNDS, for the reason given on
1487 ax_helm_kernel_mfma: the three waves per SIMD the kstep kernels ask for
1488 would tighten the register budget of a kernel whose occupancy is already
1489 set by its LDS footprint */
1490template< typename T, const int LX, const int NWF, const int TILE >
1493 T * __restrict__ av,
1494 T * __restrict__ aw,
1495 const T * __restrict__ u,
1496 const T * __restrict__ v,
1497 const T * __restrict__ w,
1498 const T * __restrict__ dx,
1499 const T * __restrict__ dy,
1500 const T * __restrict__ dz,
1501 const T * __restrict__ h1,
1502 const T * __restrict__ g11,
1503 const T * __restrict__ g22,
1504 const T * __restrict__ g33,
1505 const T * __restrict__ g12,
1506 const T * __restrict__ g13,
1507 const T * __restrict__ g23,
1508 const int nelv) {
1509
1511 dx, dy, dz, h1,
1512 g11, g22, g33,
1513 g12, g13, g23, nelv);
1514}
1515
1516template< typename T >
1518 T * __restrict__ av,
1519 T * __restrict__ aw,
1520 const T * __restrict__ u,
1521 const T * __restrict__ v,
1522 const T * __restrict__ w,
1523 const T * __restrict__ h2,
1524 const T * __restrict__ B,
1525 const int n) {
1526
1527 const int idx = blockIdx.x * blockDim.x + threadIdx.x;
1528 const int str = blockDim.x * gridDim.x;
1529
1530 for (int i = idx; i < n; i += str) {
1531 au[i] = au[i] + h2[i] * B[i] * u[i];
1532 av[i] = av[i] + h2[i] * B[i] * v[i];
1533 aw[i] = aw[i] + h2[i] * B[i] * w[i];
1534 }
1535
1536}
1537#endif // __MATH_AX_HELM_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)
shdx[ij]
const bool active
const int ij_p
__shared__ T shus[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__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g23
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__ g22
__global__ void T *__restrict__ av
const int sh
T ru[LX]
const int eb
__shared__ T shv[EB *LX *LX]
T rv[LX]
__shared__ T shwr[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__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g13
__global__ void ax_helm_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)
__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 nelv
const int sh_p
__shared__ T shur[EB *LX *LX]
const int i
__shared__ T shvs[EB *LX *LX]
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
T rwt
T rw[LX]
__shared__ T shw[EB *LX *LX]
__shared__ T shws[EB *LX *LX]
__shared__ T shu[EB *LX *LX]
T rut
T ruw[LX]
const int e
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ w
const int e_blk
T rvt
T rvw[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
__global__ void T *__restrict__ T *__restrict__ aw
__shared__ T shvr[EB *LX *LX]
__global__ void const T *__restrict__ u
const int ele
__global__ void const T *__restrict__ const T *__restrict__ dx
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ dz
const int j
__shared__ T shdz[LX *LX]
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ dy
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ h1
__syncthreads()
__global__ void const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ const T *__restrict__ g11
__global__ void T *__restrict__ T *__restrict__ const T *__restrict__ const T *__restrict__ v
__global__ void ax_helm_kernel_vector_part2(T *__restrict__ au, T *__restrict__ av, T *__restrict__ aw, const T *__restrict__ u, const T *__restrict__ v, const T *__restrict__ w, const T *__restrict__ h2, const T *__restrict__ B, const int n)
__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__ 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_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)
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)