Files
d4r/kernels/rr/rrswin_dec4_epilogues.md
woutermans 0fe3a28cb7 kernels/rr: staged-image Swin layer engine prototypes
Per-layer staged-image engine for the eleven translated denoiser Swin
layers: descriptors (verified offsets per phase), cut recipes that splice an
isolated native phase into NVIDIA's PTX, wave templates, epilogue notes and
GPU probes. The isolated phase-8 splice produces a byte-identical final
4K frame but is slower than the translated body (14.90 vs 6.59 ms) - a
correctness experiment, not a shipping improvement; docs/rr-engine.md
records the design and the next direct-converter win (B operands built
from transposed accumulators).
2026-10-12 12:47:53 +02:00

40 KiB
Raw Permalink Blame History

cuda_dldn_engine_swin_dec4_kernel — the epilogues of phases 1…62

Companion to kernels/rr/rr_layer_spec.py dec4. Everything below was read out of ~/.cache/d4r-rr-corpus/0019-PREPASS_ENTRYPOINT_NAME.ptx, entry cuda_dldn_engine_swin_dec4_kernel (the entry opens at file line 1016, its body holds statements s1…s24447, and s24447 is the ret).

dec4 is the widest of the eleven Swin layers: its attention/MLP width is 160 where dec3's is 128, so every count below is not interchangeable with dec3's. Where a region is the same code as dec3's, that is stated and the shared evidence is cited on dec4's own statements.

0. Conventions

  • sNNNN is a statement index in the linearisation rr_layer_spec.py documents; LNNNNN is the physical line of the same instruction, so every claim can be checked with sed -n 'LNNNNN,LNNNNN p' …ptx. (Example anchors: phase 1's 300 mma occupy only L1707–L3799, and s871 = L3807.)
  • An epilogue is the run of statements between one phase's last mma and the next phase's first mma; a phase's own weight loads live in the previous epilogue, and are named in place.
  • W denotes the prepared weight image: ld.param.u64 %rd2, [cuda_dldn_engine_swin_dec4_kernel_param_0+40] (s9) then cvta.to.global.u64 %rd3, %rd2 (s10). Every ld.weak.global.ca.v4.u32 reads W + 16*laneid + imm; tables and biases use W + ((laneid<<2)&12) + imm (canonical construction s16157–s16161).
  • S denotes the plane arena: ld.param.u64 %rd1050, [%rd1+-32] (s23917) then cvta.to.global.u64 %rd5, %rd1050 (s23918), with %rd1 = param_0+80 (s4), so %rd1-32 is param_0+48. This is the entry's only read of param_0+48.
  • SM denotes the 16384-byte shared array _ZZ33cuda_dldn_engine_swin_dec4_kernel33DldnEngineSwinEncParamsStructBaseE4smem (materialised by mov.u32 at s11611 and others).
  • %r9 = %tid.z (s19) is the slab index, and dec4 guards it to 0…3: s20 setp.gt.u32 %p3, %r9, 3 / s21 @%p3 bra $L__BB1_121. It scales the input buffer ×25600 (s438), a second input stride ×160 (s543), ×40 (s1052), the shared tile ×16 (s2301), ×2560 (s11503), ×2048 (<<11, s16069), ×512 (<<9, s16128) and the phase-2…11 weight group ×27648 (%r16781 = %r9*13824 s11607, %rd381 = %r16781*2 s11608, consumed at s11645 add.s64 %rd383, %rd2, %rd381).
  • Register conventions are the settled family ones: C/D reg0 = (row g, cols 2t,2t+1), reg1 = (row g+8, same cols), g = laneid>>2 (s37/s38), t = laneid&3.
  • Anything I could not pin down from the PTX is in §6.

1. Shared vocabulary

1.1 Addresses and parameters an epilogue touches

symbol how it is obtained statements
param+0, extents (ex, ey) ld.param.v2.u32 {%r1013, %r1014}, [param_0] s15
param+8, the e4m3 feature buffer ld.param.u64 %rd60, [%rd1+-72] → cvta.to.global.u64 %rd61, %rd60 s34–s35
param+24, the f16 image buffer (the skip input) ld.param.u64 %rd253, [%rd1+-56] → cvta.to.global.u64 %rd4, %rd253 s3640–s3641
param+40, W (weight image) ld.param.u64 %rd2, [param_0+40] → cvta.to.global.u64 %rd3, %rd2 s9–s10
param+48, S (plane arena) ld.param.u64 %rd1050, [%rd1+-32] → cvta.to.global.u64 %rd5, %rd1050 s23917–s23918
param+80, block origin (ox, oy) ld.param.v2.u16 {%rs222, %rs223}, [param_0+80] s5
param+88, low-res feature texture ld.param.u64 %rd1117, [%rd1129+8] with %rd1129 = param_0+80 (s24223) s24334
param+96, texture added unconditionally ld.param.u64 %rd1119, [%rd1129+16] s24410
param+104, texture added under a NaN guard ld.param.u64 %rd1121, [%rd1129+24] s24412
param+112, output surface ld.param.u64 %rd1123, [%rd1129+32] s24233
param+136, (sx, sy) pixel→uv scale ld.param.v2.f32 {%f1180, %f1181}, [%rd1129+56] s24328
SM (16384 B) mov.u32 %r16783, _ZZ33…E4smem s11611
per-lane weight address mul.wide.u32 %rd382, %laneid, 16 + add.s64 %rd384, %rd383, %rd382 s11644–s11646
per-lane table address shl.b32 %r, %laneid, 2 → cvt.u64.u32 → and.b64 %rd, %r, 12 → add.s64 %rd, W, %rd s16157–s16161
block origins %r1 = ctaid.x<<3 − ox (s7), %r2 = %r1 − 1 (s8), %r3 = ctaid.y<<3 − oy (s14), %r6 = %r3 − 1 (s16) s7–s16
%r7 = ex<<3, %r8 = ey*%r7 s17–s18 s17–s18

The param+80-relative form is worth stressing: dec4 keeps %rd1127 and %rd1129 (both = %rd1128/%rd1130 + 80, s24198 / s24223) and then reads the decoder parameters as [%rd1127-80] (= param+0), [%rd1129+8], [%rd1129+16], [%rd1129+24], [%rd1129+32], [%rd1129+56]. So +96 and +104 are exactly %rd1129+16 and %rd1129+24 (s24410 / s24412).

1.2 Constants

register first definition literal identity
%f476 s12177 mov.f32 %f476, 0f3C8CCB50 0.017186790704727173 score scale
%fd383 s12181 mov.f64 %fd383, 0dBFE1CC0000000000 −0.55615234375 clamp low
%fd385 s12186 mov.f64 %fd385, 0d3FE1CC0000000000 +0.55615234375 clamp high
%f478 s12192 mov.f32 %f478, 0f3F6D6000 0.92724609375 cubic coefficient
%f480 s12197 mov.f32 %f480, 0f3FB00000 1.375 cubic offset
%fd1 s10075 mov.f64 %fd1, 0d3F20000000000000 2⁻¹³ norm epsilon in E1
%fd386 s16920 mov.f64 %fd386, 0d3F20000000000000 2⁻¹³ norm epsilon in E21
%f1117 s17741 mov.f32 %f1117, 0f3ED306EB 0.4121621549129486 activation ramp
%f1118 s17744 mov.f32 %f1118, 0f3DA60DD6 0.0810810774564743 activation width
%f1119 s17747 mov.f32 %f1119, 0f3F000000 0.5 activation offset
%f1120 s17750 mov.f32 %f1120, 0f40000000 2.0 activation clamp

0f3FB8AA3B (log2e) occurs 9 times in the final epilogue, first at s24246; the negated form 0fBFB8AA3B once at s24415; 0f3F800000 (1.0) at s24251's add.ftz.f32. Neither layer ever uses the .relu requantiser: the string satfinite.relu does not occur.

1.3 Two quantisation helpers

  • cvt.rn.satfinite.e4m3x2.f16x2 — requantisation.
  • cvt.rn.f16x2.e4m3x2 — the inverse unpack (one e4m3 byte pair → f16x2).

2. Phase table summary

From rr_layer_spec.py dec4: 62 phases, 1606 mma, 24447 statements.

The tool prints M and N from the number of distinct A and B fragments, so the identity M/16 * N/8 * K/32 == mma is the dense-GEMM check:

  • holds for phases 2–12, 14–21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61;
  • fails for 23 phases — 1, 13, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62. For the even phases 22…60 the tool prints M=80 N=160 K=160 (5*20*5 = 500) against 20 mma: those phases are 5-step cascades, not dense GEMMs (see E22). For P62 it prints M=80 N=80 K=160 against 10 mma; P62 is a 5-fragment cascade (A = %r938…%r957) as §4/E62 describes.

Weight-image extent: the largest byte read from W is phase 62's second C seed at W + ((laneid<<2)&12) + 534800, i.e. the image is at least 534804 bytes. Phase 1's B block is [0, 25600), phase 1's C seeds are [102400, 102704], the score bias (phase 12's C) is [114240, 126028), the per-slab copy of the phase-2…11 weight group starts at 104000 + z*27648, and phase 62's last weight tile is [532224, 534796).

Phases that read no weight operand at all: 12 and 13. Phase 12's A and B both come from e4m3 packs of phase 10's D (E11), and phase 13's B is the movmatrix transpose of registers (E12); see §4/E11, E12.

3. Summary of epilogues

epilogue range stmts named operation
E1 s871–s11653 10783 requantise P1's D, scatter to SM, read back, blend param+24, inverse-std, requantise, stage for P2
E2–E10 7 stmts each 7 next-B weight loads only (s11678–s11900)
E11 s11957–s12127 171 score-bias C-seed loads + 80 packs of P10's D → P12 A and B
E12 s12176–s16019 3844 softmax of the 64×96 score block, V-transpose, pack
E13 s16068–s16341 274 stage P·V, P14 A from SM, next-B loads, column table + skip → P14 C
E14–E20 27 stmts each 27 P15…P21 A from SM + next-B loads (s16362–s16670)
E21 s16691–s17719 1029 RMS norm (², sum, rsqrt, gain) + packs + next B/C
E22 s17740–s18060 321 clamped cubic activation + column-bias add + packs + next B
E23 s18081–s18172 92 40 packs → P24 A, next-B, next-C bias
E24…E60 172/92 alternating E22/E23 repeated for P24…P60
E61 s23857–s24154 298 pack → plane-arena scatter (param+48) → next B/C
E62 s24165–s24446 282 SM stage, softmax over 5 taps, 3×3 texture gather, aux textures, surface write

4. The epilogues

E1 — s871–s11653: patch expand, image blend, inverse standard deviation

10783 statements (44 % of the entry), the same pipeline as dec3's E1 with every count scaled by 160/128 = 1.25.

Evidence:

s871   L3807  cvt.rn.satfinite.e4m3x2.f16x2 %rs226, %r1298
s875   L3818  mov.b32 %r10, {%rs228, %rs229}
s1090  L4268  st.shared.v2.u16 [%r4580], {%rs226, %rs227}
s1117  L4297  st.shared.u32 [%r4593], %r10
s2295  L5592  bar.sync 0
s2340  L5647  ld.shared.v2.u16 {%rs346, %rs347}, [%r5246]
s3640  L7252  ld.param.u64 %rd253, [%rd1+-56]
s3794  L7417  ld.global.v2.u16 {%rs426, %rs427}, [%rd255]
s9055  L12990 cvt.rn.f16x2.e4m3x2 %r9582, %rs426
s9217  L13472 add.f16 …                        // first of 324
s10018 L15459 shfl.sync.bfly.b32 %r9963,%r9959,%r10029,%r9966,%r10040
s10075 L15607 mov.f64 %fd1, 0d3F20000000000000
s10321 L16341 rsqrt.approx.ftz.f32 fl, fl
s10963 L17382 ld.global.v2.u16 {%rs3159, %rs3160}, [%rd336+103696]
s11503 L18962 mul.lo.s32 %r936, %r9, 2560
s11521 L18998 st.shared.v4.u32 [%r10307], {%r10311, %r10310, %r10309, %r10308}
s11606 L19151 bar.sync 0
s11613 L19160 ld.shared.v4.u32 {%r16785, %r16786, %r16787, %r16788}, [%r16784]
s11651 L19201 ld.weak.global.ca.v4.u32 { %r10356,%r10357,%r10358,%r10359},[%rd337]
range stmts what operations (count)
s871–s1048 178 requantise phase 1's D and pack 120 cvt.rn.satfinite.e4m3x2.f16x2, 58 mov.b32 → %r10…%r67
s1049–s1089 41 lane / %r9 → shared-index setup for the first store mov.u32 %r4338, %laneid (s1049), mul.wide.u32 %rd204, %r4338, -1431655765 (s1051)
s1090–s2295 1206 guarded scatter into SM, one basic block per store 58 st.shared.u32 + 2 st.shared.v2.u16
s2295 1 bar.sync 0 (L5592) —
s2296–s3639 1344 read back a window from SM (5-way switch per load) 40 ld.shared.v2.u16 (s2340…s3639)
s3640–s3641 2 the param+24 buffer pointer ld.param.u64 %rd253, [%rd1+-56], cvta
s3642–s9054 5413 clamped-coordinate gather from the param+24 buffer 40 ld.global.v2.u16 (s3794…s9054)
s9055–s9214 160 dequantise both sources 160 cvt.rn.f16x2.e4m3x2 → %r9582…%r9741
s9215–s10017 803 blend + reduction tree 480 mul.f16, 324 add.f16, 84 add.f16x2
s10018–s10074 57 cross-lane sums 8 shfl.sync.bfly.b32 (s10018…s10071)
s10075–s10076 2 epsilon 2⁻¹³ mov.f64 %fd1, 0d3F20000000000000 (s10075)
s10077–s10320 244 epsilon add + packing of the sums 160 add.f16, 81 mov.b32
s10321–s10956 636 inverse standard deviation 160 rsqrt.approx.ftz.f32 (s10321…s10954)
s10957–s10982 26 per-column gain read 20 ld.global.v2.u16 at W + ((laneid<<2)&12) + {103680…103984} (s10963…s10982)
s10983–s11502 520 normalise and combine 320 mul.f16, 160 mov.b32, 40 cvt.rn.satfinite.e4m3x2.f16x2
s11503–s11605 103 requantise and stage for phase 2 10 st.shared.v4.u32
s11606 1 bar.sync 0 (L19151) —
s11607–s11642 36 phase 2's A fragments 30 ld.shared.v4.u32 → %r16785…%r16904
s11643–s11653 11 phase 2's B 2 ld.weak.global.ca.v4.u32 at W + z*27648 + 16*laneid + {104000, 104512}

The scatter (s1090–s2295). The 60 stores are guarded (setp.gt.u32 %p, …, 9 at s1052 and its siblings) and the index is byte = 4 * ( (col & 3) | (40*row + 400*(col>>2)) ) into SM (shr.u32 %r4584, %r79, 2 / mul.lo.s32 %r4585, %r4584, 400 / mad.lo.s32 %r4586, %r80, 40, %r4585 / shl.b32 %r4587, %r81, 2 / add.s32 %r4588, %r4587, %r4586 / and.b32 %r4589, %r79, 3 / or.b32 %r4590, %r4589, %r4588 — s1107–s1113). So dec4 writes 10-by-10 sub-blocks of 400 bytes and 58 distinct (row, col) triples, versus dec3's literal +{0,1600,…,11200} plane offsets. As in dec3 the (row, col) order is a permutation of laneid derived with the golden-ratio multiplier (mul.wide.u32 %rd204, %r4338, -1431655765, s1051). The store count differs from dec3's 48 because dec4's phase 1 produces 800 columns instead of 640.

The param+24 gather (s3642–s9054). %rd1-56 is param_0+24 (s4, s3640); %rd4 = cvta.to.global.u64(%rd253) (s3641). Each of the 40 loads computes a clamped halo index and issues mul.wide.u32 %rd254, %r34933, 4 / add.s64 %rd255, %rd4, %rd254 / ld.global.v2.u16 {%rs426, %rs427}, [%rd255] (s3792–s3794). The gather's interior index is clamped_row * %r7 + 8*clamped_col + min(laneid&3, 7), i.e. the same 8-u32-per-image-row f16 buffer as dec3's (§4/E1 of rrswin_dec3_epilogues.md).

The inverse-std block. The residual add.f16 fold (first at s9217) yields a value that is squared (mul.f16, s9620ff), reduced in-lane, summed with the two-step butterfly shfl.sync.bfly (s10018, s10071), offset by 2⁻¹³ (s10075) and inverted by 160 rsqrt.approx.ftz.f32 (s10321…s10954). The gain table is 20 ld.global.v2.u16 at W + ((laneid<<2)&12) + {103680…103984} (s10963…s10982) — 40 f16 per lane = 160 columns.

Staging for phase 2. %r936 = %r9*2560 (s11503); the 10 st.shared.v4.u32 write at SM + z*2048 + 16*laneid + {0,512,1024,1536} (s11521, s11533, s11545, s11557) and at +{10240,10752,11264,11776,12288} in a second group guarded by setp.gt.u32 %p626, %r10328, 31 with %r10328 = %r9<<4 (s11574–s11575), i.e. only z ∈ {0,1}. After bar.sync (s11606) the 30 ld.shared.v4.u32 at SM + 16*laneid + 512*i (s11613…s11642) become phase 2's A %r16785…%r16904 (30 A fragments = 6 m-tiles × 5 k-steps), and the 2 ld.weak.global.ca.v4.u32 at s11651/s11653 are phase 2's B.

E2–E10 — s11678…s11900: next-B weight loads only

Seven statements each, exactly dec3's E2–E8 shape: 8 B fragments (16 registers) per phase, read as W + z*27648 + 16*laneid + base + 512*i. The bases are 104000 (P2), 109120 (P3), 105024 (P4), 110144 (P5), 106048 (P6), 111168 (P7), 107072 (P8), 112192 (P9), 108096 (P10), 113216 (P11) — the even phases walk 104000, 105024, 106048, 107072, 108096 and the odd phases 109120, 110144, 111168, 112192, 113216, i.e. five k-group pairs (dec3 had four), so phases 2–11 together form one 96×64×160 GEMM split into ten 96×32×32 phases chained through C (verified from the operand sets: P2/P3 use A fragment set 0, P4/P5 set 1, P6/P7 set 2, P8/P9 set 3, P10/P11 set 4; P2/P3 have C = the single zero register %r16707 while P4…P11 take the previous phase's D).

E11 — s11957–s12127: score-bias seeds and 80 packs of phase 10's D

171 statements, 24 weight loads + 80 packs — the dec3 E9 design with the wider N.

s11961 L21128 ld.weak.global.ca.v4.u32 { … }, [%rd…]   // first of 24
s12008 L21313 cvt.rn.satfinite.e4m3x2.f16x2 …          // first of 80 packs
  • 24 loads at W + z*27648 + 16*laneid + 114240 + 512*i, i = 0…23 (s11960 add.s64 %rd357, %rd404, 114240 with %rd404 = %rd383 + 16*laneid, %rd383 = W + z*27648; s11961 the first load, s11962 the next at +114752) → phase 12's C seeds (96 registers = 24 C pairs). The block is [114240, 126028). Note this is a different kind of C operand from phase 1's: phase 1 seeds each mma with a single u32 duplicated into both halves ({%r…, %r…}), loaded as 20 ld.global.u32 at [%rd203 + {102400, 102416, …, 102704}] (s551–s570), whereas phase 12 takes 24 full 512-byte tiles.
  • 80 packs whose sources are phase 10's D, not phase 11's; they split into phase 12's A fragments (%r13134…%r13497, 16 registers) and B fragments (%r13388…%r13499, 24 registers = 12 fragments). Phase 11's own D reaches E12's movmatrix block instead.
  • The C-seed layout is the same as dec3's §4/E9: byte = base + 512*(4*(n>>1) + m) + 16*laneid + 8*(n&1), so the tile index runs over 4 m-tiles and 6 (n>>1) groups = 24 tiles = 12288 bytes.

E12 — s12176–s16019: softmax of the 64×96 score block, V-transpose, pack

Phase 12 = 48 mma of 64×96 with C = the 24 bias pairs (E11). The epilogue has no memory access at all (0 ld.*, 0 st.*) and no bar.sync. Its op histogram is byte-for-byte dec3's E10: 1048 mov.b32, 672 cvt.rn.f16.f32, 384 cvt.rn.f16.f64, 192 each of fma.rn.f16x2 / cvt.f32.f16 / rcp.approx.ftz.f32 / mul.f16, 144 packs, 112 shl.b32, 104 add.f16x2, 96 each of mul.f16x2 / max.f16x2 / min.f16x2 / neg.f16x2 / and.b32, 48 movmatrix, 16 shfl.sync.bfly.b32, 8 add.f16.

s12177 L21724 mov.f32 %f476, 0f3C8CCB50
s12178 L21727 cvt.rn.f16.f32 low, %f476
s12182 L21737 cvt.rn.f16.f64 %rs3280, %fd383
s14381 L28593 shfl.sync.bfly.b32 %r15668,%r15664,%r15820,%r15671,%r15831   // first of 16
s14511 L28919 rcp.approx.ftz.f32 fl, fl                                    // first of 192
s15278 L30164 mul.f16 %rs4074,%rs4075,%rs4076                              // first of 192
s15756 L31218 movmatrix.sync.trans.aligned.m8n8.b16 %r16028, %r12605       // first of 48
s15804 L31362 cvt.rn.satfinite.e4m3x2.f16x2 %rs4648, %r16036               // first of 144 packs

Sub-sections (the same partitioning dec3's §4/E10 documents):

range what
s12176–s14293 96 per-element groups: scale, clamp, cubic, exponent extract (all 96 max.f16x2 ≤ s14278, min.f16x2 ≤ s14282, neg.f16x2 ≤ s14283, and.b32 ≤ s14293)
s14294–s14508 in-lane partial sums and the butterfly: 104 add.f16x2 (s14294–s14503), 16 add.s32 (s14378–s14500), 16 or.b32 (s14379–s14501), 16 shfl.sync.bfly.b32 (s14381–s14502) → 8 row sums, 8 add.f16 (s14390–s14505)
s14509–s15277 192 cvt.f32.f16 (s14509–s15270) → 192 rcp.approx.ftz.f32 (s14511–s15272) → 192 cvt.rn.f16.f32 (…s15274) = 96 reciprocals
s15278–s15755 192 mul.f16 (s15278–s15754) → 96 probabilities
s15756–s15803 48 movmatrix transposing phase 11's D
s15804–s16019 144 cvt.rn.satfinite.e4m3x2.f16x2 packs (s15804–s16018)

(The boundaries above are the runs between the first/last occurrence of each op, verified with the lineariser; the exponent region's end is fixed by the last and.b32 at s14293 and the sum region's by the first add.f16x2 at s14294.)

Per element the exponent is the clamped cubic of dec3's §4/E10:

m      = clamp(f16(x * 0.017181396484375), -0.55615234375, +0.55615234375)
t      = f16(m*(-m) + 0.92724609375)
u      = f16(m*t + 1.375)
expval = f16 from bits ((bits(u)<<5) & 0x7FE07FE0)

and there is no row maximum: max.f16x2 (96×) and min.f16x2 (96×) are the clamp, and the only warp shuffles are the 16 shfl.sync.bfly above. Contrast E62, which does compute a real row max over 5 taps.

E13 — s16068–s16341: stage P·V, seed phase 14

274 statements.

s16068 L32201 bar.sync 0
s16069 L32202 shl.b32 %r17033, %r9, 11
s16070 L32203 add.s32 %r17034, %r16783, %r17033
s16074 L32210 cvt.rn.satfinite.e4m3x2.f16x2 %rs4792, %r16368   // first of 32 packs
s16086 L32237 st.shared.v4.u32 [%r17036], {%r17040, %r17039, %r17038, %r17037}
s16128 L32328 shl.b32 %r34545, %r9, 9
s16133 L32335 ld.shared.v4.u32 {%r34550, %r34551, %r34552, %r34553}, [%r34549]
s16138 L32343 ld.weak.global.ca.v4.u32 { %r17055,%r17056,%r17057,%r17058},[%rd405]
s16162 L32388 ld.global.v2.u16 {%rs7270, %rs7271}, [%rd889+325200]   // first of 20
s16183 L32410 add.f16 %rs4826,%rs7309,%rs4828                        // first of 80
  • s16068 bar.sync; s16069–s16073 build %r17036 = SM + z*2048 + 16*laneid;
  • 32 cvt.rn.satfinite.e4m3x2.f16x2 packing phase 13's D and 4 st.shared.v4.u32 at SM + z*2048 + 16*laneid + {0,512,1024,1536} (s16086, s16100, s16114, s16127);
  • s16128–s16132 %r34549 = SM + z*512 + 16*laneid; s16133 ld.shared.v4.u32 → phase 14's A fragment #0 (%r34550…%r34553);
  • s16137–s16156 10 weight loads at W + 16*laneid + {126528, 127040, 128064, 128576, 129088, 129600, 130112, 130624, 131136} → phase 14's B (20 fragments = 40 registers);
  • s16161–s16181 20 table loads at W + ((laneid<<2)&12) + {325184, 325200, … 325488} (16-byte stride) → 40 f16 per lane = a per-column table;
  • s16182–s16341 80 add.f16 → 40 b32 %r17104, %r17105, … = phase 14's C seeds, exactly table[col] + residual (s16183 add.f16 %rs4826,%rs7309,%rs4828 where the second operand is the E1 residual register).

So phase 14: D = A·B + (skip_residual + column_table).

E14–E20 — s16362–s16670: A from SM + next-B, seven times

27 statements each: one ld.shared.v4.u32 (one A fragment, %r34557…%r34560 in E14, then %r34563…, %r34569…, %r34575…, %r34581…, %r34587…, %r34593…), 10 ld.weak.global.ca.v4.u32 (the next phase's B, 20 fragments = 40 registers) and the address arithmetic. Example, E14 (s16362–s16388): s16365 ld.shared.v4.u32, s16370 ld.weak.global.ca.v4.u32 (first of 10).

Together with E13 this supplies the A k-fragments of phases 14–21, which are one GEMM 16×160×256 split across eight mma phases chained through C (P15 C = P14's D, …, P21 C = P20's D). The B blocks are 126528 (P14), 154176 (P15), 181824 (P16), 209472 (P17), 237120 (P18), 264768 (P19), 292416 (P20), 320064 (P21).

E21 — s16691–s17719: RMS norm before the MLP

1029 statements, no stores. The dec3 E15 design scaled by 160/128.

s16691 L34285 shl.b32 %r34597, %r9, 1
s16693 L34287 mov.b32 {%rs6072, %rs6075}, %r18790   // first of 302
s16694 L34289 mul.f16 %rs5066,%rs6075,%rs6075        // first of 240 squares
s16895 L34846 shfl.sync.bfly.b32 %r19103,%r19099,%r15820,%r19106,%r15831   // first of 4
s16920 L34912 mov.f64 %fd386, 0d3F20000000000000     // eps = 2^-13
s16921 L34914 cvt.rn.f16.f64 %rs5309, %fd386
s17046 L35286 rsqrt.approx.ftz.f32 fl, fl            // first of 80
s17368 L35807 ld.global.v2.u16 {%rs7310, %rs7311}, [%rd906+325520]   // first of 20 gain loads
s17632 L36554 ld.weak.global.ca.v4.u32 { %r19224,%r19225,%r19226,%r19227},[%rd485]  // first of 10 next-B
s17656 L36599 ld.global.u32 %r19314, [%rd911+330944] // first of 4 next-C bias words
s17660 L36604 cvt.rn.satfinite.e4m3x2.f16x2 %rs6031, %r33783   // first of 40 packs
range stmts op
s16691–s16903 213 lane/origin setup and 160 mul.f16 squares (the remaining 80 of the 240 are in the normalise)
s16853–s17040 188 in-lane/butterfly sums: 42 add.f16x2 (s16853–s16917), 4 or.b32 (s16894–s16915), 4 shfl.sync.bfly.b32 (s16895–s16916), 82 add.f16 (s16904–s17040)
s16920–s16921 2 eps 2⁻¹³ in %fd386 (cvt.rn.f16.f64 %rs5309, %fd386 at s16921)
s17044–s17361 318 80 cvt.f32.f16 (s17044–s17357) → 80 rsqrt.approx.ftz.f32 (s17046–s17359) → 80 cvt.rn.f16.f32 (s17048–s17361)
s17368–s17387 20 gain loads W + ((laneid<<2)&12) + {325504…325808} (+325504 appears last, +325520 first)
s17632–s17659 28 10 ld.weak.global.ca.v4.u32 (phase 22's B, 320064 + 512*i, s17632–s17650) then 4 ld.global.u32 at 330944/330960/330976/330992 (phase 22's C bias word, s17656–s17659)
s17660–s17718 59 40 packs → phase 22's A

What it computes — the same expression as dec3's §4/E15:

sumsq[row] = Σ_c x[row,c]²                             (f16, in-lane tree + 4-lane butterfly)
inv        = rsqrt.approx.ftz.f32(f32(f16(sumsq + 2⁻¹³)))   // rounded to f16
out[row,c] = x[row,c] * inv * gain[c]

with no mean subtraction and no 1/N, and the gain is a per-column table read with the W + ((laneid<<2)&12) + off pattern. The norm's bias is not added here: it is phase 22's C seed, loaded at s17656.

E22 — s17740–s18060: clamped cubic activation + column bias

321 statements.

s17740 L36884 mov.u32 %r19505, %laneid
s17741 L36886 mov.f32 %f1117, 0f3ED306EB
s17744 L36893 mov.f32 %f1118, 0f3DA60DD6
s17747 L36900 mov.f32 %f1119, 0f3F000000
s17750 L36907 mov.f32 %f1120, 0f40000000
s17753 L36915 neg.f16x2 %r19510,%r19509      // first of 8
s17754 max.f16x2 …                           // first of 8
s17755 min.f16x2 …                           // first of 8
s17756 abs.f16x2 …                           // first of 8
s17757 L36931 mul.f16x2 %r19520,%r19507,%r19518   // first of 24
s17758 sub.f16x2 …                           // first of 8
s17760 add.f16x2 …                           // first of 8
s17883 add.s64 …                             // column-table addressing
s17885 L37377 ld.weak.global.ca.v4.u32 …     // first of 10 next-B loads
s17909 L37422 ld.global.v2.u16 {%rs7350, %rs7351}, [%rd916+336144]   // first of 20 table loads
s17929 L37469 add.f16 …                      // first of 80 bias adds
s18049 L37803 cvt.rn.satfinite.e4m3x2.f16x2 %rs6311, %r19648   // first of 8 packs

Per element, over each of the 8 f16x2 groups of phase 22's D:

y   = clamp(x, -2, +2)                              // neg/max/min, s17753–s17755
g   = 0.5 + y * (0.412109375 - 0.0810546875 * |y|)  // abs/mul/sub/mul/add, s17756–s17760
out = x * g

g is a cubic ramp, not a sigmoid: g(+2) = 1 ⇒ out = x for x ≥ 2; g(-2) = 0 ⇒ out = 0 for x ≤ -2. Reproduce the formula, not SiLU/GELU. The constants %f1117…%f1120 are defined here and are re-read as cvt.rn.f16.f32 low, %f1117…%f1120 by every later activation epilogue.

Then 20 table loads (s17909 = +336144) and 80 add.f16 (s17929–s18047) write the table over the activation inputs, producing phase 23's C seeds; the final 8 packs (s18049…) build phase 23's A (one 4-register fragment); and the 10 ld.weak.global.ca.v4.u32 from s17885 are phase 23's B block [331008, 336128).

E23 — s18081–s18172: packs, then next B/C

92 statements:

s18081 L37971 mov.u32 %r19988, %laneid
s18085 L37979 ld.weak.global.ca.v4.u32 …      // first of 10 next-B loads
s18109 L38022 ld.global.u32 %r20079, [%rd921+341568]   // first of 4 next-C bias words
s18113 L38027 cvt.rn.satfinite.e4m3x2.f16x2 %rs6319, %r33783   // first of 40 packs

40 packs (20 b32) → phase 24's A; 10 ld.weak.global.ca.v4.u32 → phase 24's B [336448, 341568); 4 ld.global.u32 at 341568/341584/341600/341616 → phase 24's C bias.

E24 … E60 — the repeating MLP stack

The pattern (activation, first one with a table add) → (40 packs + next B/C) repeats with a period of two phases. Numbering the phases: the even phases 22, 24, …, 60 each have a 321/172-statement activation epilogue; the odd phases 23, 25, …, 59 each have a 92-statement pack epilogue. Verified op histograms:

  • P22 epi (s17740–s18060, 321): 80 add.f16, 76 mov.b32, 32 cvt.rn.f16.f32, 24 mul.f16x2, 20 ld.global.v2.u16, 10 ld.weak, 8 each of neg/max/min/abs/sub/add.f16x2, 8 packs.
  • P23 epi (s18081–s18172, 92): 40 packs, 20 mov.b32, 10 ld.weak, 4 ld.global.u32.
  • P24 epi (s18193–s18364, 172): 172 statements with 8 packs and 10 ld.weak and no ld.global.v2.u16, i.e. no column table add — the residual is carried by the C operand that E23 loaded.

The B blocks advance 4128 bytes per phase: 336448 (P24), 341632 (P25), 346752 (P26), 351936 (P27), 357056 (P28), … 521920 (P60), 527104 (P61), 532224 (P62), and each phase's C bias words sit in the 48-byte window 48 bytes below the next phase's B base (e.g. P24's C at 341568..341616, loaded at s18109).

E61 — s23857–s24154: pack → plane-arena scatter → next B/C

298 statements.

s23857 L57579 cvt.rn.satfinite.e4m3x2.f16x2 %rs7231, %r34324   // first of 40 packs
s23917 L57718 ld.param.u64 %rd1050, [%rd1+-32]                  // S = plane arena (param+48)
s23918 L57719 cvta.to.global.u64 %rd5, %rd1050
s23920 L57723 shr.u32 %r958, %r34544, 2                         // g = laneid>>2
s23921 L57724 and.b32 %r959, %r34544, 3                         // t = laneid&3
s23924 L57727 add.s32 %r34630, %r937, %r34628                   // y = %r937 + (laneid>>5)
s23930 L57733 add.s32 %r34631, %r1, %r34629                     // x = %r1 + (g&7)
s23943 L57747 st.global.u32 [%rd1052], %r938                    // first of 20 stores
s24139 L57579-… ld.weak.global.ca.v4.u32                        // first of 5 next-B loads
s24153 ld.global.u32 …                                          // first of 2 next-C seeds
  • 40 packs → 20 b32 %r938…%r957, which are simultaneously phase 62's A fragments — the same 20 registers are scattered to memory and fed to the last GEMM.
  • S = cvta.to.global.u64(param_0+48) via %rd1-32 (s23917–s23918) — the entry's only read of param_0+48.
  • 20 st.global.u32 (first at s23943 with value %r938), each guarded by setp.lt.s32 %p, %r34630, 0 / setp.ge.u32 %p, %r34630, %r1014 / setp.gt.u32 %p, %r34544, 63 combined with or.pred (s23925–s23929) and the matching x guards, then a selp.b32 …, -1, …, %p1 + setp.lt.s32 + bra that turns a guard failure into "skip the store".
  • The index is idx = (8*ex)*y + 8*x + d with %r7 = ex<<3 (s17), y = %r937 + (laneid>>5) where %r937 = %r3 + 2*%r9 (s16691–s16692), and x = %r1 + ((laneid>>2)&7), d = t (or d = t|4), exactly dec3's §5 formula.
  • after the stores: 5 ld.weak.global.ca.v4.u32 at W + 16*laneid + {532224, 532736, 533248, 533760, 534272} (s24138–s24147, %r34660…%r34679) = phase 62's B (10 fragments = 20 registers), then 2 ld.global.u32 at W + ((laneid<<2)&12) + {534784, 534800} (s24153–s24154, %r34690/%r34700) = phase 62's C seeds.

E62 — s24165–s24446: softmax over five taps, 3×3 texture gather, surface write

282 statements; the entry's last statement is the ret.

s24165 L58082 bar.sync 0
s24170 L58089 shl.b32 %r997, %r34888, 4
s24177 L58096 st.shared.u32 [%r998], %r34761
s24181 L58101 st.shared.u32 [%r998+16], %r34771
s24198 L58121 add.s64 %rd1127, %rd1128, 80
s24209 L58132 add.s32 %r1003, %r1, %r1001                       // x
s24210 L58133 add.s32 %r1004, %r3, %r1002                       // y
s24215 L58138 ld.param.v2.u32 {%r34806, %r34807}, [%rd1127+-80]  // ex, ey
s24223 L58147 add.s64 %rd1129, %rd1130, 80
s24233 L58157 ld.param.u64 %rd1123, [%rd1129+32]                 // output surface (param+112)
s24246 L58191 (first of 12 mul.ftz.f32 by 0f3FB8AA3B)
s24247 L58192 ex2.approx.ftz.f32
s24328 L58360 ld.param.v2.f32 {%f1180, %f1181}, [%rd1129+56]     // param+136 scale pair
s24334 L58366 ld.param.u64 %rd1117, [%rd1129+8]                  // param+88 low-res texture
s24336 L58369 tex.base.2d.v4.f16.f32 {%rs7418,…,}, [%rd1101, {%f1140,%f1141}]
s24410 L58541 ld.param.u64 %rd1119, [%rd1129+16]                 // param+96
s24411 L58543 tex.base.2d.v4.f16.s32 {%rs7562,…,}, [%rd1119, {%r1003,%r1004}]
s24412 L58545 ld.param.u64 %rd1121, [%rd1129+24]                 // param+104
s24413 L58547 tex.base.2d.v4.f16.s32 {%rs7566,…,}, [%rd1121, {%r1003,%r1004}]
s24445 L58635 sust.b.2d.v4.b16.zero [%rd1123, {%r34877,%r1004}], {%rs7619,%rs7620,%rs7621,%rs7618}

Op histogram (s24165–s24446): 30 fma.rn.f16, 17 mov.b32, 16 add.s32, 13 setp.lt.s32, 12 each of mul.ftz.f32 / cvt.rn.f16.f32 / selp.b32, 10 each of cvt.f32.f16 / ex2.approx.ftz.f32 / add.ftz.f32, 9 tex.base.2d.v4.f16.f32, 8 mov.u64, 6 each of mov.u32 / and.b32 / shl.b32 / bra / cvt.rn.f32.s32 / fma.rn.ftz.f32, 5 ld.shared.u32, 5 sub.f16x2, 5 mul.f16x2, and 1 sust.

range stmts what
s24165–s24197 33 bar.sync (s24165) + stage phase 62's D into SM at (48*y + 6*x) slots (4 st.shared.u32, s24177–s24194)
s24198–s24227 30 pointer setup: %rd1127 = %rd1128 + 80 (s24198), second bar.sync (s24199), the integer coordinates %r1003 = %r1+%r1001 / %r1004 = %r3+%r1002 (s24209–s24210), (ex, ey) read from %rd1127-80 (s24215), bounds guard (s24216–s24222), %rd1129 = %rd1130 + 80 (s24223)
s24228–s24241 14 surface pointer param+112 = [%rd1129+32] (s24233), 5 ld.shared.u32 reading 5 consecutive taps (s24228–s24234), and the row max: 3 max.f16x2 (s24236–s24238) + 2 max.f16 (s24240–s24241)
s24233 / s24243–s24326 — surface pointer param+112 (s24233); softmax: 5 sub.f16x2 (s24243–s24295), 12 mul.ftz.f32 (s24246–), 10 ex2.approx.ftz.f32 (s24247–), div.approx.ftz.f32 (s24304, s24418), 5 mul.f16x2 (s24307–s24311), 3 cvt.rn.f16.s32 (s24312–s24314), 12 selp.b32 (s24319–)
s24327–s24333 7 clamped coordinates clamp(p-1, 1, ex-2) / clamp(p-1, 1, ey-2) (6 cvt.rn.f32.s32 s24327–, 6 fma.rn.ftz.f32 s24330–, ld.param.v2.f32 s24328)
s24334–s24410 77 9 tex.base.2d.v4.f16.f32 (s24336–s24406) at the 3×3 stencil of param+88, accumulated with 30 fma.rn.f16 (s24338–s24427)
s24411–s24413 3 2 tex.base.2d.v4.f16.s32 at param+96 (s24410–s24411) and param+104 (s24412–s24413), both at {%r1003, %r1004}
s24414–s24444 31 sigmoid-like blend (s24418 div.approx, s24421 sub.f16, 3 mul.f16 s24422–s24426, 3 fma.rn.f16) then the per-channel NaN guard on param+104: 3 set.nan.f16.f16 (s24428–s24438) → setp.ne.s16 → @p bra → 3 add.f16 (s24431–s24441)
s24445 1 sust.b.2d.v4.b16.zero surface write

Where the +96/+104 reads go. %rd1129+16 = param+96 (s24410) and %rd1129+24 = param+104 (s24412). Both are tex.base.2d.v4.f16.s32 reads at the same integer coordinate {%r1003, %r1004} = (x, y) (s24209–s24210); param+96's channels are added unconditionally, while param+104's are added per channel only when the channel is not NaN (set.nan.f16.f16). The surface written is %rd1123 = param+112 (s24233), and the write lands at (%r34877, %r1004) with 0 in the fourth channel (s24445).

What the +24 skip input supplies. param_0+24 (reached as %rd1-56, s3640) supplies the f16 image values that E1 adds to the shared-staged features (s9217 onward) to form the residual. That residual is used in E1's own normalisation (mul.f16) and is added to the per-column table at W+325184 in E13 (s16182–s16341) to seed phase 14's C operand.

5. The plane-arena writes (param_0+48)

S = cvta.to.global.u64(%rd1050) with %rd1050 = ld.param.u64 [%rd1+-32] (s23917–s23918) is the only occurrence of param_0+48 in the entry. There are exactly two store groups in the whole entry:

(i) E61, 20 × st.global.u32 [%rd1052 + …], v — s23943 … s24129. The first is s23943 L57747 st.global.u32 [%rd1052], %r938 and the last is s24129 L57971 st.global.u32 [%rd1090], %r957; the values are %r938…%r957 in file order (the same 20 registers that become phase 62's A). Each is guarded as described in §4/E61:

idx = (8*ex)*y + 8*x + d,  guard  y >= 0, y < ey, laneid <= 63, x >= 0, x < ex
y = %r937 + (laneid>>5),  %r937 = %r3 + 2*%r9,  x = %r1 + ((laneid>>2)&7),
d = t (first of each pair) | d = t|4, with t = laneid&3
where ex = %r1013, ey = %r1014 (s15), %r1 = block origin x (s7), %r3 = block origin y (s14)

(ii) E62, the shared staging at s24177/s24181/s24190/s24194: st.shared.u32 into SM, not the arena.

What is written: phase 61's requantised 16×160 output (20 u32 per lane). What reads it back: nothing inside this kernel — param_0+48 is never the base of an ld.*, and phase 62's A comes from registers (%r938…%r957, E61's own packs). See §6.

6. Uncharacterised

U1. The shared-memory layout of E1's first scatter. dec4 writes with byte = 4 * ((col&3) | (40*row + 400*(col>>2))) (s1107–s1113) into a 16 KB array, rather than dec3's literal +{0,1600,…,11200} plane offsets. I did not reduce the (row, col) permutation of laneid (built with mul.wide.u32 %rd204, %r4338, -1431655765, s1051) to a closed form, nor map each plane to a source element of the 800-column phase-1 output. Missing: a shared-memory dump or the producer's D layout.

U2. %r16781 = %r9*13824 (s11607) feeding %rd381 (s11608). %rd381 is used at s11645 as the slab term W + z*27648 for phase 2's B. The derivation 13824*2 looks like a per-slab stride expressed for a different tile width, so the constant 13824 is presumably the per-slab output-tile size divided by 2, but nothing in the PTX states it. Missing: the weight-image layout description.

U3. The identity of the per-column tables. W+325184 (E13), W+336144 (E22), W+103680…103984 (E1's gain), W+325504…325808 (E21's gain) are all read with the same W + ((laneid<<2)&12) + off pattern. Their roles are computable (E13: added to the skip before phase 14; E22: added after the activation; E1/E21: multiply the inverse std) but their names are not. The same is true of the GEMM biases at 102400…102704 (P12), 126528-adjacent words and 534784/534800 (P62). Missing: the weight image's _prep permutation or the model's parameter names.

U4. The identity of the 12288-byte score bias. Phase 12 adds a 12288-byte f16 table (W + z*27648 + 114240 … 126028) as its GEMM C operand, laid out as §4/E11 describes. Whether it is a relative position bias, a learned score bias, or a per-lane constant cannot be decided from the PTX.

U5. The geometry of the banded phases. Twenty-three phases print an M/N/K that fails M/16·N/8·K/32 == mma (P1, 13, 22–60 even, 62). For the even phases 22…60 I verified the 5-step cascade shape (A = 5 fragments, B = 20 fragments, mma = 20 = 5*4 with the C-chain running over the A fragments), and for P62 likewise (A = 5 fragments, B = 10 fragments, mma = 10 = 5*2). For P1 (300 mma, 100 B fragments, 250 distinct C/D pairs) and P13 (48 mma, C set of 65 registers) I did not reduce the full (M, N, K) reading. The epilogue contracts above do not depend on resolving this.

U6. The logical meaning of E62's blend. E62 computes a blend of a 3×3 tex.base.2d.v4.f16.f32 gather of param+88 (weighted by a 5-tap softmax) with the param+96 texture and, per channel under a NaN guard, the param+104 texture, then writes param+112 at (8·x, y). The PTX gives the arithmetic exactly but not which model term this is. Missing: the high-level source or a reference.

U7. The count of distinct input-buffer strides. dec4's prologue/E1 use ×25600 (s438), ×160 (s543) and ×40 (s1052) for %r9 in addition to the shared and weight strides. I did not determine which of the three addresses the prologue's gather (param+8) versus E1's gather (param+24) versus some other buffer. Missing: a mapping from %r9 to buffer identity.

7. Quick acceptance index

requirement where
patch expand §4/E1 (s871–s11653) and phase 1 (§2)
the norms E1's rsqrt block (s9215–s10956, eps s10075) and E21 (s16691–s17719, eps s16920)
the attention E11 (score seeds + packs), E12 (softmax + movmatrix s15756–s15803 + packs), E13 (P·V staging)
the activations E22, E24, …, E60 — the clamped cubic `x·(0.5 + y·(0.412109375 − 0.0810546875·
the per-launch table in the plane arena §5, s23917/s23943ff (param_0+48)
every epilogue of every phase §3 summary table + §4 (E1–E62)
texture reads at +96/+104 and where the surface write goes §4/E62, s24410–s24413 and s24233/s24445
what the skip input at +24 supplies §1.1 (s3640) and §4/E1, E13
phase table M/N/K arithmetic checked §2 (23 phases fail; listed)
weight-image extent §2 (≥ 534804 bytes; last read s24153 + 2 words)
phases with no weight operand §2: 12 and 13
uncharacterised §6