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).
79 KiB
cuda_dldn_engine_swin_enc2_kernel — the epilogues of phases 1…38
Companion to kernels/rr/rr_layer_spec.py enc2. Everything below was read out of
~/.cache/d4r-rr-corpus/0015-PREPASS_ENTRYPOINT_NAME.ptx, entry
cuda_dldn_engine_swin_enc2_kernel (14199 statements, file lines 1018–34562).
0. Conventions
sNNNNis a statement index in the linearisationrr_layer_spec.pydocuments (statements of the entry body; a wrapped{ … }block and a 4-linemma.synccount as one statement, numbered at their first line).LNNNNNis the physical line of the same instruction in the corpus file, so every claim can be checked withsed -n 'LNNNNN,LNNNNN p' …ptx.- An epilogue is the run of statements between one phase's last
mmaand the next phase's firstmma. The phase table (rr_layer_spec.py enc2) gives the ranges; this file names what each one computes. Wdenotes the prepared weight image:ld.param.u64 %rd2, [cuda_dldn_engine_swin_enc2_kernel_param_0+40](s8),cvta.to.global.u64 %rd3, %rd2(s9). Everyld.weak.global.ca.v4.u32in an epilogue readsW + 16*laneid + imm; everyld.global.v2.u16/ld.global.u32table load readsW + ((laneid<<2)&12) + imm.Wzdenotes the per-%tid.zweight slab:%rd269 = %rd2 + 2*(%tid.z * 10752) + 16*laneid(s4589–s4590, s4613–s4615) =W + 21504*%tid.z + 16*laneid. Phases 1…7 add offsets to this pointer; phases 9…36 add them toW + 16*laneiddirectly (s8976–s8978); phase 37 toW + 12288*%tid.z + 16*laneid(s13634–s13639). Every byte offset in the phase table is relative to the pointer named here, so the phase-table numbers do not include the%tid.zterm. See §6 U5.Sdenotes the plane arena:ld.param.u64 %rd383, [%rd1+-32](s12865) with%rd1 = param_0 + 80(s4) →param_0+48, thencvta.to.global.u64 %rd5, %rd383(s12866).Idenotes the input image:ld.param.u64 %rd10, [param_0+8](s17) →%rd4(s18).Odenotes the epilogue-37 destination:ld.param.u64 %rd6, [%rd492+56](s13632) →param_0+56.- The mma fragment layouts and the weight-addressing formula are the settled ones
(see
rr_layer_spec.py --helpand the enc0 companion): C/Dreg0 = (row g, cols 2t,2t+1),reg1 = (row g+8, same cols),g = laneid>>2,t = laneid&3. - Geometry. Writing
m = M/16,n = N/8,k = K/32for a phase, the mma chain fixes the three exactly:|A fragments| = m*k,|B fragments| = n*k, accumulator chains (maximal C←D dataflow runs)= m*n, each of lengthk, andmma = m*n*k.rr_layer_spec.pyprintsM = 16*|A fragments|andN = 8*|B fragments|, i.e. it omits the division byk, so its M and N columns are too large by a factorK/32whenever a phase makes more than one k32 step. Every row is re-derived from the chain in §2. - Anything I could not pin down from the PTX is in §6 under uncharacterised.
1. Shared vocabulary
1.1 Addresses and parameters an epilogue touches
| symbol | how it is obtained | statements |
|---|---|---|
W (weight image) |
ld.param.u64 %rd2, [param_0+40] / cvta.to.global.u64 %rd3, %rd2 |
s8 / s9 |
Wz = W + 21504*%tid.z + 16*laneid |
mul.lo.s32 %r16116, %r10, 10752 (%r10 = %tid.z, s22), mul.wide.u32 %rd266, %r16116, 2, add.s64 %rd268, %rd2, %rd266, add.s64 %rd269, %rd268, %rd267 (%rd267 = 16*laneid) |
s4589–s4590, s4613–s4615 |
I = input image |
ld.param.u64 %rd10, [param_0+8] / cvta.to.global.u64 %rd4, %rd10 |
s17–s18 |
S = plane arena (param_0+48) |
add.s64 %rd1, %rd8, 80 (%rd8 = param_0, s1), ld.param.u64 %rd383, [%rd1+-32], cvta.to.global.u64 %rd5, %rd383 |
s4, s12865–s12866 |
O = param_0+56 |
ld.param.u64 %rd6, [%rd492+56] (%rd492 = param_0, s12803) |
s13632 |
T = texture (param_0+88) |
ld.param.u64 %rd480, [%rd491+88] (%rd491 = param_0, s13941) |
s13941, s14056 |
| surfaces | ld.param.u64 [%rd491+112] (s13953), [+120] (s14178), [+128] (s14179) |
s13953, s14178–s14179 |
extents (ex, ey) |
ld.param.v2.u32 {%r322, %r323}, [param_0+0] |
s14 |
| block origins | sub.s32 %r1, %r317, %r318 (%r317 = 8*ctaid.x) and sub.s32 %r2, %r321, %r319 (%r321 = 8*ctaid.y) |
s3–s7, s11–s13 |
| patch origin offsets | ld.param.v2.u16 {%rs126, %rs127}, [param_0+80] |
s5 |
%r5 = 8*ex, %r6 = ey*(8*ex) |
shl.b32 %r5, %r322, 3, mul.lo.s32 %r6, %r323, %r5 |
s15–s16 |
g = laneid>>2, t = laneid&3 |
shr.u32 %r8, %r315, 2, and.b32 %r9, %r315, 3 |
s20–s21 |
%r182 = %r2 + 2*%tid.z |
shl.b32 %r181, %r10, 1, add.s32 %r182, %r2, %r181 |
s9184–s9185 |
%r17795 = %tid.z<<5 |
mov.u32 %r17745, %tid.z, shl.b32 %r17795, %r17745, 5 |
s13135, s13139 |
The three scalar-table forms matter and are used throughout:
ld.weak.global.ca.v4.u32atptr + 512*ireads one N-tile of a K=32 slice = 16 bytes/lane = 2 B fragments or 2 C pairs (the tool's own comment,rr_layer_spec.py:189–192).ld.global.v2.u16atW + ((laneid<<2)&12) + offreads 4 bytes/lane, the four lanes with equallaneid&3reading the same word, so a run of such loads at 16-byte stride is one per-column vector (a 96-element f16 table is 12 such loads: 12 × 4 bytes × 4 distinct lane offsets = 192 bytes).ld.global.u32at the same addressing reads one word = 2 f16 = one per-column bias pair, used in both halves of an mma C operand, i.e. replicated across the 16 rows of the tile (s9820{%r9477, %r9477}, s13871{%r17465, %r17465}).
1.2 Constants that survive between epilogues
| register | first definition | f32/f64 literal | value used (after the cvt) |
read by |
|---|---|---|---|---|
%f476 |
s5020 mov.f32 %f476, 0f3C8CCB50 |
0.017186790704727173 | f16 0x2466 = 0.017181396484375 |
E7 |
%fd383 |
s5024 mov.f64 %fd383, 0dBFE1CC0000000000 |
−0.55615234375 | f16 0xB873 = −0.55615234375 |
E7 |
%fd385 |
s5029 mov.f64 %fd385, 0d3FE1CC0000000000 |
+0.55615234375 | f16 0x3873 = +0.55615234375 |
E7 |
%f478 |
s5035 mov.f32 %f478, 0f3F6D6000 |
0.92724609375 | f16 0x3B6B = 0.92724609375 |
E7 |
%f480 |
s5040 mov.f32 %f480, 0f3FB00000 |
1.375 | f16 0x3D80 = 1.375 |
E7 |
%f861 |
s9833 mov.f32 %f861, 0f3ED306EB |
0.4121621549129486 | f16 0x3698 = 0.412109375 |
E13…E35 |
%f862 |
s9836 mov.f32 %f862, 0f3DA60DD6 |
0.0810810774564743 | f16 0x2D30 = 0.0810546875 |
E13…E35 |
%f863 |
s9839 mov.f32 %f863, 0f3F000000 |
0.5 | f16 0x3800 = 0.5 |
E13…E35 |
%f864 |
s9842 mov.f32 %f864, 0f40000000 |
2.0 | f16 0x4000 = 2.0 |
E13…E35 |
%fd1 |
s3662 mov.f64 %fd1, 0d3F20000000000000 |
2⁻¹³ | f16 0x0800 = 0.0001220703125 |
prologue, E12 |
%f861…%f864 are defined inside E13 (s9833–s9842) and re-read by the cvt
of every later 164-statement epilogue (E15 s10174–s10181, E17 s10422–…, and so
on to E35), so those epilogues contain cvt.rn.f16.f32 but only 2 mov.u32.
Also live across the region: %r3037 = 1 (s3318), %r3046 = 2 (s3611),
%r3048 = −1 (s3604) — the delta / segmask / membermask operands of every
shfl.sync.bfly.b32; and %r8606 = 0 (s4616), the zero C operand of phases 1,
2 and 8.
1.3 Two quantisation helpers
cvt.rn.satfinite.e4m3x2.f16x2— the requantisation used everywhere (f16x2_to_e4m3x2_satfinite_bitsinptx/lib/zluda_ptx_impl.cpp).cvt.rn.f16x2.e4m3x2— the other direction (f16x2 → e4m3x2), used only in the prologue to quantise the input patch (s3320 ff.).rcp.approx.ftz.f32,rsqrt.approx.ftz.f32,ex2.approx.ftz.f32,div.approx.ftz.f32— always wrapped ascvt.f32.f16 → op → cvt.rn.f16.f32(round each step through f16).
2. Summary
2.1 The 38 epilogues
| epilogue | range | stmts | named operation |
|---|---|---|---|
| E1 | s4645–s4651 | 7 | phase 2 B weight loads |
| E2 | s4676–s4682 | 7 | phase 3 B weight loads |
| E3 | s4707–s4713 | 7 | phase 4 B weight loads |
| E4 | s4738–s4744 | 7 | phase 5 B weight loads |
| E5 | s4769–s4775 | 7 | phase 6 B weight loads |
| E6 | s4800–s4970 | 171 | score-bias C-seed loads + packs of phase-5/6 D → phase 7 A and B |
| E7 | s5019–s8861 | 3843 | softmax of the 64×96 score, V transpose, packs → phase 8 A and B |
| E8 | s8910–s9078 | 169 | phase-8 D → shared, phase 9 A read, phase 9 B loads, per-column table add → phase 9 C |
| E9 | s9091–s9109 | 19 | phase 10 A from shared + phase 10 B loads |
| E10 | s9122–s9140 | 19 | phase 11 A from shared + phase 11 B loads |
| E11 | s9153–s9171 | 19 | phase 12 A from shared + phase 12 B loads |
| E12 | s9184–s9819 | 636 | RMS norm of phase-12 D + gain, packs; phase 13 A/C and phase 15 B |
| E13 | s9832–s10088 | 257 | clamped cubic on phase-13 D + packs; residual+table → phase 14 C; phase 14 B |
| E14 | s10101–s10160 | 60 | phase 15 B + C loads; 24 packs of the normed tile → phase 15 A |
| E15 | s10173–s10336 | 164 | clamped cubic on phase-15 D + packs; phase 16 B |
| E16 | s10349–s10408 | 60 | phase 17 B + C loads; packs of the normed tile → phase 17 A |
| E17 | s10421–s10584 | 164 | clamped cubic on phase-17 D + packs; phase 18 B |
| E18 | s10597–s10656 | 60 | phase 19 B + C loads; packs of the normed tile → phase 19 A |
| E19 | s10669–s10832 | 164 | clamped cubic on phase-19 D + packs; phase 20 B |
| E20 | s10845–s10904 | 60 | phase 21 B + C loads; packs of the normed tile → phase 21 A |
| E21 | s10917–s11080 | 164 | clamped cubic on phase-21 D + packs; phase 22 B |
| E22 | s11093–s11152 | 60 | phase 23 B + C loads; packs of the normed tile → phase 23 A |
| E23 | s11165–s11328 | 164 | clamped cubic on phase-23 D + packs; phase 24 B |
| E24 | s11341–s11400 | 60 | phase 25 B + C loads; packs of the normed tile → phase 25 A |
| E25 | s11413–s11576 | 164 | clamped cubic on phase-25 D + packs; phase 26 B |
| E26 | s11589–s11648 | 60 | phase 27 B + C loads; packs of the normed tile → phase 27 A |
| E27 | s11661–s11824 | 164 | clamped cubic on phase-27 D + packs; phase 28 B |
| E28 | s11837–s11896 | 60 | phase 29 B + C loads; packs of the normed tile → phase 29 A |
| E29 | s11909–s12072 | 164 | clamped cubic on phase-29 D + packs; phase 30 B |
| E30 | s12085–s12144 | 60 | phase 31 B + C loads; packs of the normed tile → phase 31 A |
| E31 | s12157–s12320 | 164 | clamped cubic on phase-31 D + packs; phase 32 B |
| E32 | s12333–s12392 | 60 | phase 33 B + C loads; packs of the normed tile → phase 33 A |
| E33 | s12405–s12568 | 164 | clamped cubic on phase-33 D + packs; phase 34 B |
| E34 | s12581–s12640 | 60 | phase 35 B + C loads; packs of the normed tile → phase 35 A |
| E35 | s12653–s12816 | 164 | clamped cubic on phase-35 D + packs; phase 36 B |
| E36 | s12829–s13698 | 870 | pack phase-36 D → arena scatter + shared staging; phase 37 A/B/C |
| E37 | s13747–s13870 | 124 | pack phase-37 D → 4 stores to param+56; phase 38 B/C |
| E38 | s13877–s14199 | 323 | shared staging, real row max, softmax, 9 texture reads, 3×3 gather, sigmoid/0.25/lane sum, 3 surface writes |
2.2 Phase geometry re-derived from the mma chain
|A| and |B| are the distinct A-quad and B-pair counts;
chains/len come from the C←D dataflow runs; mma is the emitted count.
spec is the phase table's (M, N, K, tiles, mma).
| # | A-quads | B-pairs | chains×len | k | re-derived (M,N,K,tiles) | spec (M,N,K,tiles) | M/16·N/8·K/32 vs mma |
|---|---|---|---|---|---|---|---|
| 1–6 | 6 | 4 | 24×1 | 1 | 96,32,32,24 | 96,32,32,24 | 6·4·1 = 24 = mma ✔ |
| 7 | 4 | 12 | 48×1 | 1 | 64,96,32,48 | 64,96,32,48 | 4·12·1 = 48 = mma ✔ |
| 8 | 12 | 12 | 16×3 | 3 | 64,32,96,16 | 192,96,96,16 | 4·4·3 = 48 = mma ✔ (spec M,N too big) |
| 9–12 | 1 | 12 | 12×1 | 1 | 16,96,32,12 | 16,96,32,12 | 1·12·1 = 12 = mma ✔ |
| 13,15,…,35 | 3 | 12 | 4×3 | 3 | 16,32,96,4 | 48,96,96,4 | 1·4·3 = 12 = mma ✔ (spec M,N too big) |
| 14,16,…,36 | 1 | 12 | 12×1 | 1 | 16,96,32,12 | 16,96,32,12 | 1·12·1 = 12 = mma ✔ |
| 37 | 12 | 48 | 4×12 | 12 | 16,32,384,4 | 192,384,384,4 | 1·4·12 = 48 = mma ✔ (spec M,N too big) |
| 38 | 3 | 6 | 2×3 | 3 | 16,16,96,2 | 48,48,96,2 | 1·2·3 = 6 = mma ✔ (spec M,N too big) |
Explicit answer to the flagged rows: in every one of the 38 rows the identity
mma = (M/16)·(N/8)·(K/32) holds, and none of the phase-table rows is
arithmetically inconsistent. What is wrong in the table is M and N (and
hence the full/band label the tool derives from them): the tool sets
M = 16·|A|, N = 8·|B| (rr_layer_spec.py:481, :590–592), which counts every
k32 step as a separate m- and n-tile, so whenever K > 32 its M and N are too
large by exactly the factor k = K/32 and its dense test
(nmma == nA*nB*lens[0], :593) is too strict. Dividing out k — which the
chain's own m·k = |A|, n·k = |B|, m·n = chains fixes uniquely — gives the
row marked re-derived, and every one then satisfies the identity:
- rows 1–7, 9–12, 14, 16, …, 36 are correct as printed (
k = 1); - row 8:
|A|=12, |B|=12, k=3→ M=64, N=32, K=96 (tool: 192, 96, 96) — dense, 4·4·3 = 48; - rows 13, 15, …, 35:
|A|=3, |B|=12, k=3→ M=16, N=32, K=96 (tool: 48, 96, 96) — dense, 1·4·3 = 12; the tool'sK = 96is right, only M and N are 3× too large; - row 37:
|A|=12, |B|=48, k=12→ M=16, N=32, K=384 (tool: 192, 384, 384) — dense, 1·4·12 = 48; again only M and N are wrong (12×); - row 38:
|A|=3, |B|=6, k=3→ M=16, N=16, K=96 (tool: 48, 48, 96) — dense, 1·2·3 = 6.
The operand provenance of the corrected rows is checked separately from the
counts: for row 8 the 12 A quads are E7's probability packs of a 64×96 matrix
(§3/E7(g)) and the 12 B pairs its movmatrix transposes of a 96×32 matrix, giving
4 m-tiles × 3 k and 4 n-tiles × 3 k directly; for rows 13…35 the 3 A quads are
E12's three 16×32 e4m3 slices of the 16×96 normed tile (§3/E12) → 1 m-tile × 3 k;
for row 37 the 12 A quads are read as 48 ld.shared.u32 from the staged phase-36
output and the 48 B pairs come from 24 512-byte tiles → 1 m-tile × 12 k and 4
n-tiles × 12 k; for row 38 the 3 A quads are staged from phase 37's chain-final D
and the 6 B pairs from 3 tile loads → 1 × 3 and 2 × 3. The only thing the operand
provenance does not settle is which (n, k) each 512-byte B tile holds for rows
13…35 and 37 — see §6 U1.
3. The epilogues
E1 — s4645–s4651: phase 2 B weight loads
s4645 L7837: mov.u32 %r3499, %laneid
s4646 L7839: mul.wide.u32 %rd270, %r3499, 16
s4647 L7840: add.s64 %rd271, %rd268, %rd270
s4648 L7841: add.s64 %rd64, %rd271, 3264
s4649 L7843: ld.weak.global.ca.v4.u32 { %r3500,%r3501,%r3502,%r3503},[%rd64]
s4650 L7845: add.s64 %rd65, %rd271, 3776
s4651 L7847: ld.weak.global.ca.v4.u32 { %r3504,%r3505,%r3506,%r3507},[%rd65]
Two 16-byte/lane fragments of Wz + {3264, 3776} = the weight block
[3264, 4296) — phase 2's B operand (%r3500,%r3501 … %r3506,%r3507), which
phase 2 consumes at s4652–s4675. %rd268 is the Wz pointer of §1.1, so this
is W + 21504*%tid.z + 16*laneid + off. Nothing else.
E2 — s4676–s4682: phase 3 B weight loads
Same shape, base register %rd268, offsets +1216 (s4679) and +1728 (s4681)
→ %r3749…%r3756. Weight block [1216, 2248).
E3 — s4707–s4713: phase 4 B weight loads
Offsets +4288 (s4710), +4800 (s4712) → %r3998…%r4005. Block
[4288, 5320).
E4 — s4738–s4744: phase 5 B weight loads
Offsets +2240 (s4741), +2752 (s4743) → %r4247…%r4254. Block
[2240, 3272).
E5 — s4769–s4775: phase 6 B weight loads
Offsets +5312 (s4772), +5824 (s4774) → %r4496…%r4503. Block
[5312, 6344).
E6 — s4800–s4970: score-bias seeds + packs of phases 5 and 6
171 statements: 24 weight loads, 80 cvt.rn.satfinite.e4m3x2.f16x2, 40
mov.b32, and the address setup. Two independent things happen.
(a) The phase-7 score bias, s4800–s4850. 24 ld.weak.global.ca.v4.u32 at
Wz + 16*laneid + 6336 + 512*i, i = 0…23:
s4803 L8746: add.s64 %rd74, %rd281, 6336
s4804 L8748: ld.weak.global.ca.v4.u32 { %r4745,%r4746,%r4747,%r4748},[%rd74]
… +6848, +7360, +7888, … +18112 …
s4850 L8840: ld.weak.global.ca.v4.u32 { %r4837,%r4838,%r4839,%r4840},[%rd97]
24 × 512 bytes = 12288 bytes = 6144 f16, the byte block [6336, 18624) of
the slab. The 96 registers %r4745…%r4840 are phase 7's C operand: each
mma uses one distinct C pair and the chain analysis of §2.2 shows all 48 pairs
are distinct (grid dump: C frags: %r4745,%r4746 … %r4839,%r4840). So the
score GEMM adds a full 64×96 f16 additive bias, one value per score element,
laid out tile-major: with m = m-tile 0…3 and n = n-tile 0…11,
C(m,n) = the register pair at Wz + 6336 + 512*(4*(n>>1) + m) + 16*laneid + 4*((n&1)*2 + j), j = 0,1
byte base of tile (m,n) = 6336 + 512*(4*(n>>1) + m), 48 tiles × 512 B = 12288 B.
Verified directly on the emitted chain: s4971 uses C={%r4745,%r4746} with
A=%r5033…, B={%r5287,%r5288}; s4983 (the same m-tile 1 step later) uses
C={%r4749,%r4750}; s4995 C={%r4753,%r4754}; s5007 C={%r4757,%r4758} —
stride 4 registers per m-tile, 2 registers per n-tile. This is the same layout
formula enc0's E9 derived for its 12288-byte score bias
(C(m,n) = load_tile(4*(n>>1)+m) + 4*(n&1)); only the base differs (6336 here,
8384 there). What the table is cannot be decided from the PTX — see §6 U2.
(b) Phase 7's operands are register-resident, s4851–s4970. 80 cvt +
40 mov.b32 produce both A and B of phase 7 from phase 5's and phase 6's D:
s4851 L8843: cvt.rn.satfinite.e4m3x2.f16x2 %rs1462, %r4275
s4852 L8846: cvt.rn.satfinite.e4m3x2.f16x2 %rs1461, %r4255
s4853 L8848: mov.b32 %r5287, {%rs1461, %rs1462} // phase 7 B frag 0
…
s4925 L9016: mov.b32 %r5033, {%rs1509, %rs1510} // phase 7 A frag 0
%r5287 and %r5033 are bit-identical: both are
{pack(%r4255), pack(%r4275)} (s4852–s4853 vs s4924–s4925, with %r4255 ← the
phase-5 D of s4745 and %r4275 ← that of s4747). Every further pair follows
the same pattern with the two halves swapped: s4865 %r5307 = {pack(%r4295), pack(%r4315)} against s4937 %r5153 = {pack(%r4315), pack(%r4295)}. So phase 7's B operand carries the same data as its A operand
(re-ordered for the row/col fragment layouts), i.e. the score GEMM multiplies one
projection by its own transpose; and 4 A quads + 12 B pairs = 64×32 · 32×96.
Inputs: phase 5's D %r4255…%r4486, phase 6's D %r4504…%r4735, Wz.
Outputs: the 48 C seeds %r4745…%r4840, the 4 A quads
(%r5033…%r5036, %r5153…%r5156, %r5273…%r5276, %r5393…%r5396) and the 12
B pairs (%r5287…%r5398). Memory: 24 weight loads.
E7 — s5019–s8861: softmax of the 64×96 score, then the V transpose
3843 statements, no memory access at all (0 ld.*, 0 st.*). Sub-sections,
verified by counting each op's first and last statement:
| range | stmts | what |
|---|---|---|
| s5019–s7136 | 2118 | 96 per-element groups: scale, clamp, cubic, exponent extraction |
| s7137–s7222 | 86 | in-lane partial sum tree |
| s7223–s7344 | 122 | 16 shfl.sync.bfly → 8 row sums |
| s7345–s8117 | 773 | 192 rcp.approx.ftz.f32 → 96 reciprocals |
| s8118–s8597 | 480 | 192/288 mul.f16 → 96 probabilities |
| s8598–s8645 | 48 | movmatrix transpose of phase 6's D |
| s8646–s8861 | 216 | 144 cvt.rn.satfinite.e4m3x2.f16x2 packs |
(a) per-element exponent, s5019–s7136. 96 groups of 22 statements. A group:
s5020 L9461: mov.f32 %f476, 0f3C8CCB50
s5021 L9464: cvt.rn.f16.f32 low, %f476
s5022 L9465: mov.b32 %r5402, {low,low}
s5023 L9469: mul.f16x2 %r5403,%r4921,%r5402
s5025 L9474: cvt.rn.f16.f64 %rs1542, %fd383 // -0.55615234375
s5028 L9483: max.f16x2 %r5406,%r5403,%r5408
s5033 L9497: min.f16x2 %r5409,%r5406,%r5411 // +0.55615234375
s5034 L9501: neg.f16x2 %r5412,%r5409
s5039 L9515: fma.rn.f16x2 %r5414,%r5409,%r5412,%r5417 // 0.92724609375
s5044 L9529: fma.rn.f16x2 %r5418,%r5409,%r5414,%r5421 // 1.375
s5045 L9532: shl.b32 %r16192, %r5418, 5
s5046 L9533: and.b32 %r7323, %r16192, 2145419232 // 0x7FE07FE0
For each of phase 7's 96 f16x2 D registers, per half (an f16 value x):
m = clamp(f16(x * 0.017181396484375), -0.55615234375, +0.55615234375)
t = f16(m*(-m) + 0.92724609375)
u = f16(m*t + 1.375) = f16(1.375 + m*(0.92724609375 - m^2))
E = bits(u)[9:5] // floor(32*frac(u)) -- top five mantissa bits
F = bits(u)[4:0] // low five mantissa bits
expval = f16 from bits (E << 10) | (F << 5) // = 2^(E-15)*(1 + F/32)
Exactly the enc0 E5 scheme (((u_bits<<5) & 0x7FE07FE0) drops u's sign and
exponent so the mantissa lands in the exponent field): expval ≈ 2^(32·frac(u) − 15) = 2^(32u − 47) with u ∈ [1.03125, 1.71875] because the
clamp ±0.55615… = √(0.92724609375/3) is the turning point of
0.92724609375·m − m³. This is not exp: it is the cubic
32·(0.375 + 0.92724609375·m − m³) truncated to a 6-bit significand.
(b),(c) row sums, s7137–s7344. 80 add.f16x2 reduce the 96 expval
registers in-lane; then eight times (one per row g, g+8 of the four m-tiles):
s7223 L16329: shfl.sync.bfly.b32 %r7567,%r7563,%r3037,%r7570,%r3048 // delta 1
s7224 L16333: add.f16x2 %r7572,%r7563,%r7567
s7229 L16344: shfl.sync.bfly.b32 %r7576,%r7572,%r3046,%r7579,%r3048 // delta 2
s7230 L16348: add.f16x2 %r7581,%r7572,%r7576
s7232 L16353: add.f16 %rs2309,%rs2310,%rs2311
s7233 L16356: mov.b32 %r7778, {%rs2309, %rs2309}
Two butterfly steps over lane bits 0 (delta 1, %r3037) and 1 (delta 2,
%r3046), then add.f16 of the two halves and a broadcast into both halves.
Result: 8 row sums %r7778 %r7782 %r7826 %r7830 %r7874 %r7878 %r7922 %r7926, one per row of the 64-row window.
There is no row maximum anywhere in this epilogue. Enumerated: every one of
the 96 max.f16x2 (s5028…s7121) is immediately followed by a min.f16x2
(s5033…s7125) and preceded by the mul.f16x2 with %r5402 (the 0.017181 scale)
— one clamp triple per element group. The 96 neg.f16x2 (s5034…s7126) feed the
first fma, and the 16 shfl.sync.bfly are the row sums above. Nothing else
touches a D register. Contrast E38 (§3/E38), which does compute a real row
max.
(d) reciprocals, s7345–s8117. 192 rcp.approx.ftz.f32 = 96 f16x2
reciprocals, each cvt.f32.f16 → rcp.approx.ftz.f32 → cvt.rn.f16.f32 on a row
sum (s7350 L16652: mov.b32 {hl, hu}, %r7778 / s7353 L16655: rcp.approx.ftz.f32 fl, fl), duplicated four times per row sum (once per n-tile).
(e) probabilities, s8118–s8597.
s8118 L17897: mov.b32 {%rs2334, %rs2337}, %r7323 // expval
s8119 L17898: mov.b32 {%rs2335, %rs2338}, %r7735 // 1/rowsum
s8120 L17900: mul.f16 %rs2336,%rs2337,%rs2338
s8121 L17904: mul.f16 %rs2333,%rs2334,%rs2335
s8122 L17907: mov.b32 %r8071, {%rs2333, %rs2336}
p = expval · (1/rowsum); the row sums cancel the constant 2⁻⁴⁷ of (a), so
p_ij ∝ 2^(32·u_ij). 96 f16x2 results.
(f) V transpose, s8598–s8645. 48 movmatrix.sync.trans.aligned.m8n8.b16, one
per register of phase 6's D (%r4504, %r4505, %r4514, %r4515, %r4544, %r4545, … %r4734, %r4735 — exactly the 24 C/D pairs listed at s4776–s4799, each
once):
s8598 L18954: movmatrix.sync.trans.aligned.m8n8.b16 %r7927, %r4504
s8645 L19095: movmatrix.sync.trans.aligned.m8n8.b16 %r8021, %r4735
48 × 64 = 3072 values = the whole 96×32 tile of phase 6's D.
(g) packs, s8646–s8861. 144 cvt / 72 mov.b32 in two groups:
- s8646–s8717 — from the movmatrix results → phase 8's 12 B pairs
(
%r8533,%r8534,%r8543,%r8544, …%r8633,%r8644), i.e. the transposed V:s8646 L19098: cvt.rn.satfinite.e4m3x2.f16x2 %rs2910, %r7935/s8648 L19103: mov.b32 %r8533, {%rs2909, %rs2910}. - s8718–s8861 — from the probabilities → phase 8's 12 A quads
(
%r8239…%r8242,%r8259…,%r8279…,%r8359…,%r8379…,%r8399…,%r8479…,%r8499…,%r8519…,%r8599…,%r8619…,%r8639…%r8642):s8720 L19271: mov.b32 %r8239, {%rs2957, %rs2958}.
Inputs: phase 7's 96 D f16x2 (%r4921…%r5392), phase 6's 48 D
(%r4504…%r4735), %r3037/%r3046/%r3048, the five constants of §1.2.
Outputs: the 5 constants + phase 8's 12 A quads and 12 B pairs.
Memory: none.
E8 — s8910–s9078: phase-8 D → shared, phase 9 A, table add → phase 9 C
169 statements, four moves.
(a) stage phase 8's D into shared, s8910–s8968.
s8910 L19937: bar.sync 0
s8911 L19938: shl.b32 %r16320, %r10, 11 // %tid.z * 2048
s8912 L19939: add.s32 %r16321, %r3216, %r16320 // %r3216 = smem symbol
s8915 L19944: add.s32 %r16323, %r16321, %r16322 // + 16*laneid
s8928 L19973: st.shared.v4.u32 [%r16323], {%r16327, %r16326, %r16325, %r16324}
s8941 L20002: st.shared.v4.u32 [%r16323+512], {…}
s8954 L20031: st.shared.v4.u32 [%r16323+1024], {…}
s8967 L20060: st.shared.v4.u32 [%r16323+1536], {…}
s8968 L20061: bar.sync 0
Four v4 stores (16 bytes/lane) at smem + 2048*%tid.z + 16*laneid + {0,512,1024, 1536}. Each v4 word is an e4m3 pack of two distinct phase-8 D registers
(e.g. s8916 cvt %rs3054, %r8267, s8917 cvt %rs3053, %r8207, s8924
mov.b32 %r16324, {%rs3059, %rs3060}); all 32 cvt in the block name distinct
registers, so 32 of phase 8's 48 D registers are staged here, 16 words per
lane.
(b) phase 9's A, s8969–s8974.
s8969 L20062: shl.b32 %r16340, %r10, 9 // %tid.z * 512
s8970 L20063: add.s32 %r16341, %r3216, %r16340
s8973 L20068: add.s32 %r16343, %r16341, %r16342 // + 16*laneid
s8974 L20069: ld.shared.v4.u32 {%r16344, %r16345, %r16346, %r16347}, [%r16343]
One A quad (%r16344…%r16347), i.e. the e4m3-packed phase-8 D, re-read through
shared. Phase 9's mma (s9079) uses exactly this quad.
(c) phase 9's B, s8975–s8989. 6 ld.weak.global.ca.v4.u32 at
W + 16*laneid + 18624 + 512*i, i = 0…5 (s8978 add.s64 %rd98, %rd283, 18624) → %r8682…%r8705, the 12 B pairs of phase 9. Note the base is %rd283 = %rd2 + 16*laneid (s8977) — no %tid.z term, unlike E1–E6.
(d) per-column residual table → phase 9's C, s8990–s9078.
s8995 L20106: ld.global.v2.u16 {%rs4364, %rs4365}, [%rd286+86224]
… 86240, 86256, 86272, 86288, 86304, 86320, 86336, 86352, 86368, 86384, 86208
s9007 L20119: add.f16 %rs3088,%rs4387,%rs1103
s9008 L20123: add.f16 %rs3085,%rs4386,%rs1100
s9009 L20126: mov.b32 %r8715, {%rs3085, %rs3088}
12 ld.global.v2.u16 at W + ((laneid<<2)&12) + {86208, 86224, …, 86384} = 192
bytes = 96 f16, a per-column vector over the 96-wide N axis. 48 add.f16 add
them (pair-swapped) to the 48 halves of 24 prologue registers and 24
mov.b32 build the 24 registers %r8715…%r8826 = phase 9's C operand.
The 48 added operands are %rs1100 … %rs1361, and every one of them is defined
by a prologue mov.b32 from the quantised input patch — %rs1100/%rs1103 ←
%r2798 (s3366, source line 4684), %rs1106/%rs1109 ← %r2802, … — where
%r2798 is itself cvt.rn.f16x2.e4m3x2 %r2798, %rs128 (s3320), i.e. the first
element of the input image read at s176. So
C9 = column_table[86208] + (the block's quantised input patch, re-read as f16)
— an input skip connection, not phase 8's D. (This is a surprising but
direct reading: the 48 add.f16 sources are the halves of %r2798…%r2845, the
prologue's cvt.rn.f16x2.e4m3x2 results, and no statement anywhere redefines
them; see §6 U9.)
Inputs: phase 8's 48 D (staged in (a)), the prologue's 24 quantised input
registers %r2798…, table 86208, %tid.z, %r3216, W.
Outputs (live 49): %r16344…%r16347 (via shared), the 24 C seeds, the 12 B
pairs. Memory: 6 weight loads + 12 table reads + 2 barriers + 4 shared
stores + 1 shared load.
E9 — s9091–s9109: phase 10 A from shared + phase 10 B
19 statements.
s9091 L20419: mov.u32 %r8827, %laneid
s9092 L20421: shl.b32 %r16349, %r8827, 4
s9094 L20423: ld.shared.v4.u32 {%r16351, %r16352, %r16353, %r16354}, [%r16350+2048]
s9099 L20431: ld.weak.global.ca.v4.u32 { %r8829,%r8830,%r8831,%r8832},[%rd104]
… 5 more at 512 stride …
s9109 L20451: ld.weak.global.ca.v4.u32 { %r8849,%r8850,%r8851,%r8852},[%rd109]
One A quad read from smem + 16*laneid + 2048 (i.e. the next staged slot of
(a) in E8), and 6 weight fragments at W + 16*laneid + 40128 + 512*i = phase
10's B (%r8829…%r8852). The shared slot is 2048 bytes above the slot phase 9
read, so the four staged v4 words serve phases 9, 10, 11, 12 at
base + {0, 2048, 4096, 6144}.
E10 — s9122–s9140: phase 11 A from shared + phase 11 B
Same shape; s9125 L20542: ld.shared.v4.u32 {%r16357…%r16360}, [%r16356+4096] and
6 weight fragments at W + 16*laneid + 61632 + 512*i (s9130 …) → %r8975…%r8998.
E11 — s9153–s9171: phase 12 A from shared + phase 12 B
s9156 L20661: ld.shared.v4.u32 {%r16363…%r16366}, [%r16362+6144] and 6 weight
fragments at W + 16*laneid + 83136 + 512*i (s9161 …) → %r9121…%r9144.
Note phases 9–12 also chain their C: phase 12's C is
{%r8999,%r9000} = phase 11's D (s9141 / s9172), and likewise 10←9, 11←10.
E12 — s9184–s9819: the RMS norm before the MLP band
636 statements. Structure (counts from the op histogram of s9184–s9819: 182
mov.b32, 144 mul.f16, 50 add.f16, 48 each cvt.f32.f16 /
rsqrt.approx.ftz.f32 / cvt.rn.f16.f32, 26 add.f16x2, 24
cvt.rn.satfinite.e4m3x2.f16x2, 12 ld.global.v2.u16, 6 weight loads, 4
shfl.sync.bfly):
| range | stmts | op |
|---|---|---|
| s9184–s9281 | 98 | 48 mul.f16 — the squares of phase 12's D |
| s9282–s9301 | 20 | 21 add.f16x2 — in-lane reduction tree |
| s9302–s9344 | 43 | 4 shfl.sync.bfly + add.f16x2 + add.f16 + + eps, broadcast into 24 b32 |
| s9345–s9405 | 61 | 48 add.f16 more (24 duplicated eps-bearing sums) |
| s9406–s9598 | 193 | 48 rsqrt.approx.ftz.f32 (24 inverses) |
| s9599–s9615 | 17 | 12 gain loads +86400 … +86576 |
| s9616–s9687 | 72 | 48 mul.f16 — inv · gain |
| s9688–s9759 | 72 | 48 mul.f16 — x · (inv·gain) |
| s9760–s9774 | 15 | 6 weight loads +86632 … +89192 (phase 13's B) |
| s9775–s9783 | 9 | 4 ld.global.u32 +89664 … +89712 (phase 13's C seeds) |
| s9784–s9819 | 36 | 24 packs → phase 13's A |
Evidence:
s9185 L20776: add.s32 %r182, %r2, %r181 // %r2 + 2*%tid.z
s9186 L20777: mov.b32 {%rs3838, %rs3841}, %r9145 // unpack a D register
s9187 L20779: mul.f16 %rs3232,%rs3841,%rs3841 // square (high half)
s9188 L20783: mul.f16 %rs3229,%rs3838,%rs3838 // square (low half)
s9282 L21018: add.f16x2 %r9265,%r9266,%r9267
s9303 L21101: add.f16x2 %r9326,%r9313,%r9319
s9308 L21112: shfl.sync.bfly.b32 %r9330,%r9326,%r3037,%r9333,%r3048
s9317 L21136: add.f16 %rs3373,%rs3374,%rs3375
s9333 L21179: cvt.rn.f16.f64 %rs3379, %fd1 // eps = 2^-13
s9334 L21183: add.f16 %rs3383,%rs3373,%rs3379
s9410 L21407: rsqrt.approx.ftz.f32 fl, fl
s9604 L21720: ld.global.v2.u16 {%rs4388, %rs4389}, [%rd295+86416]
s9616 L21732: mov.b32 {%rs3525, %rs3528}, %r9369
s9688 L21949: mul.f16 %rs3671,%rs3841,%rs3527 // x * (inv*gain)
s9690 L21956: mov.b32 %r15561, {%rs3668, %rs3671} // the normed register
s9784 L22205: cvt.rn.satfinite.e4m3x2.f16x2 %rs3813, %r15562
s9786 L22210: mov.b32 %r9540, {%rs3812, %rs3813}
s9780 L22200: ld.global.u32 %r9477, [%rd300+89664]
What it computes. Phase 12's D is 24 registers = 48 f16/lane; each register
pairs (row g, cols 2t,2t+1) with (row g+8, cols 2t,2t+1), and two registers
(%r9145,%r9146) are one n-tile. For each of the two rows a lane holds:
sumsq[row] = Σ_{c=0..95} x[row,c]^2 (f16, one shfl.bfly pair)
inv = rsqrt.approx.ftz.f32(f32(f16(sumsq + 2^-13))) // rounded to f16
out[row,c] = x[row,c] * inv * gain[c]
- There is no mean subtraction and no
1/N:%rs3232/%rs3229are raw squares (mul.f16 %rs3232,%rs3841,%rs3841), the tree only adds, and the only constant added is%fd1 = 2⁻¹³(s9333/s9334). The quantity is the raw sum of 96 squares plus2⁻¹³; the mean is never formed. This is an RMS norm, not a mean-subtracting LayerNorm. gain[c]is read fromW + ((laneid<<2)&12) + {86400, 86416, …, 86576}(12v2.u16at 16-byte stride = 192 bytes = 96 f16), i.e. a per-column vector over the 96 columns, constant across rows.- the two butterfly steps (
%r3037 = 1,%r3046 = 2, segmask 31, membermask −1 at s9307/s9313) reduce over the four lanes with equallaneid>>2, which between them hold all 96 columns of the row;add.f16 %rs3373,%rs3374,%rs3375folds the two column halves. The inverse is broadcast into both halves (mov.b32 %r9370, {%rs3380, %rs3383}) so one row value serves all n-tiles. - the norm's bias is not added here. It is the 4
ld.global.u32at the end (s9780–s9783), which become phase 13's C seeds as{%r9477, %r9477}etc. — a per-column bias replicated over the 16 rows.
The prologue's normalise:96@s3812 is the same function. Its op block
(s3250–s4497) has the same shape and the same missing pieces: 48
cvt.rn.f16x2.e4m3x2 quantise the 24 ld.global.v2.u16 input loads (s176,
s328, … s3316, all through %rd12 = %rd4 + 4*index, %rd4 = param_0+8, i.e.
the input image); 96 mul.f16 square them in place
(s3431 L4846: mul.f16 %rs321,%rs1199,%rs1199); 52 add.f16x2 + 8
shfl.sync.bfly + add.f16 reduce; s3662 L5488: mov.f64 %fd1, 0d3F20000000000000 / s3664 L5494: add.f16 %rs526,%rs510,%rs522 add the
same 2⁻¹³; 96 rsqrt.approx.ftz.f32 (48 pairs, s3812 L5934: rsqrt.approx.ftz.f32 fl, fl) invert; the 96 products are multiplied by a
per-column gain read as 12 ld.global.v2.u16 at W + ((laneid<<2)&12) + {16, 32, …, 176, 0} (s4198–s4209, 192 bytes = 96 f16); 96 more mul.f16
(s4211 L6573: mul.f16 %rs811,%rs812,%rs1459) apply the gain; 48
cvt.rn.satfinite.e4m3x2.f16x2 (s4498–s4521) requantise and six
st.shared.v4.u32 (s4540, s4553, s4566, s4576, s4581, s4586) stage the 18 A
quads. No mean, no 1/N, no bias add — also an RMS norm, with its bias
deferred to a later GEMM's C exactly as in E12.
The normed tile is the A operand of every odd phase 13…35. E12's output
registers %r15561…%r15584 (24 b32) are re-packed not only here (s9784–s9819,
→ %r9540,%r9541,%r9542,%r9543 … %r9580…%r9583, i.e. three 16×32 A slices)
but again in every even-numbered epilogue E14, E16, …, E34:
%r15561 is read by exactly the s9785 (E12), s10126 (E14), s10374 (E16),
s10622 (E18), s10870 (E20), s11118 (E22), s11366 (E24), s11614 (E26), s11862
(E28), s12110 (E30), s12358 (E32), s12606 (E34) — twelve repacks, one per band
phase. So all twelve odd phases 13, 15, …, 35 share one input tile.
Inputs: phase 12's 24 D, %fd1, the gain table, W. Outputs (54 live):
phase 13's 3 A quads (%r9540…%r9543, %r9560…%r9563, %r9580…%r9583), phase
13's 4 C seeds (%r9477, %r9487, %r9537, %r9547) and 12 B pairs (%r9419…%r9442,
6 loads at +86592…+89152), the 24 normed registers %r15561…%r15584 (re-read by
E14, E16, …, E34). Memory: 12 table + 6 weight loads.
E13 — s9832–s10088: clamped cubic + residual/table, packs
257 statements. This is the epilogue that materialises %f861…%f864.
(a) the activation, s9832–s10076. 8 groups (one per chain-final D register of
phase 13: %r9508 %r9518 %r9509 %r9519 %r9568 %r9578 %r9569 %r9579), 17
statements each:
s9833 L22375: mov.f32 %f861, 0f3ED306EB
s9836 L22382: mov.f32 %f862, 0f3DA60DD6
s9839 L22389: mov.f32 %f863, 0f3F000000
s9842 L22396: mov.f32 %f864, 0f40000000
s9845 L22404: neg.f16x2 %r9593,%r9592 // -2.0
s9846 L22408: max.f16x2 %r9595,%r9508,%r9593 // max(x, -2)
s9847 L22412: min.f16x2 %r9598,%r9595,%r9592 // min(·, +2)
s9848 L22416: abs.f16x2 %r9601,%r9598 // |y|
s9849 L22420: mul.f16x2 %r9603,%r9590,%r9601 // 0.0810546875*|y|
s9850 L22424: sub.f16x2 %r9606,%r9589,%r9603 // 0.412109375 - that
s9851 L22428: mul.f16x2 %r9609,%r9598,%r9606 // y * that
s9852 L22432: add.f16x2 %r9612,%r9591,%r9609 // 0.5 + that
s9853 L22436: mul.f16x2 %r9615,%r9508,%r9612 // x * g
Per element, y = clamp(x, −2, +2) and
g = 0.5 + y * (0.412109375 − 0.0810546875 * |y|) // f16 throughout
out = x * g
g is a cubic ramp: g(+2) = 1 so out = x for x ≥ 2; g(−2) = 0 so
out = 0 for x ≤ −2. The constants and the op order are byte-identical to
enc0's E13 (0f3ED306EB, 0f3DA60DD6, 0.5, 2.0, neg/max/min/abs/mul/ sub/mul/add/mul): this is the same clamped cubic, not a GELU, not a sigmoid and
not a polynomial in x. Reproduce the formula, not x·sigmoid(x).
(b) the residual + per-column table → phase 14's C, s9990–s10076.
s9993 L22895: ld.global.v2.u16 {%rs4412, %rs4413}, [%rd305+92816]
… 92832, 92848, 92864, 92880, 92896, 92912, 92928, 92944, 92960, 92976, 92800
s10005 L22908: add.f16 %rs3839,%rs4435,%rs3841
s10006 L22912: add.f16 %rs3836,%rs4434,%rs3838
s10007 L22915: mov.b32 %r9863, {%rs3836, %rs3839}
12 ld.global.v2.u16 at W + ((laneid<<2)&12) + {92800…92976} (192 bytes = 96
f16, one per column), then 48 add.f16 and 24 mov.b32 producing
%r9863…%r9974, the 24 registers phase 14 uses as its C operand. %rs3838/ %rs3841 are the two halves of %r9145 — phase 12's D, not phase 13's, so
C14 = D12 + column_table[92800] (a residual, added to all 16 rows)
(c) phase 14's B, s9977–s9989. 6 ld.weak.global.ca.v4.u32 at
W + 16*laneid + 89728 + 512*i → %r9822…%r9845.
(d) packs, s10077–s10088. 8 cvt + 4 mov.b32 → %r9967, %r9969, %r9968, %r9970 = phase 14's A quad, from the activation outputs (%r9615, %r9644,
%r9673, %r9731, %r9760, %r9789, %r9818, %r9702):
s10077 L23124: cvt.rn.satfinite.e4m3x2.f16x2 %rs3981, %r9731
s10078 L23127: cvt.rn.satfinite.e4m3x2.f16x2 %rs3980, %r9615
s10079 L23129: mov.b32 %r9967, {%rs3980, %rs3981}
Inputs: phase 13's 8 chain-final D, phase 12's 24 D, table 92800, W.
Outputs (24 live): %f861…%f864, %r9967…%r9970, %r9863…%r9974,
%r9822…%r9845. Memory: 6 weight + 12 table loads.
E14 — s10101–s10160: phase 15 B/C + packs of the normed tile
60 statements: 6 weight loads at +92992…+95552 → %r9976…%r9999 (phase 15's
B); 4 ld.global.u32 at +96064, +96080, +96096, +96112 → %r10034, %r10044, %r10094, %r10104 (phase 15's C seeds, each used as {%rX, %rX}); and 24 packs
of E12's normed registers into phase 15's 3 A quads:
s10126 L23279: cvt.rn.satfinite.e4m3x2.f16x2 %rs3988, %r15561
s10127 L23281: mov.b32 %r10097, {%rs3988, %rs3989}
… %r10098, %r10099, %r10100 (quad 1) ; %r10117…%r10120; %r10137…%r10140
Note the pack order is %r10097←(%r15561,%r15562), %r10099←(%r15563,%r15564),
%r10098←(%r15565,%r15566), %r10100←(%r15567,%r15568) (s10125–s10136), i.e.
the whole 16×96 normed tile is re-quantised into 12 b32 = three 16×32 A slices.
E15 — s10173–s10336: clamped cubic on phase 15's D
164 statements = 8 activation groups + 6 weight loads + 4 packed b32. Identical
to E13(a) but on phase 15's chain-final D (%r10065 %r10075 %r10066 %r10076 %r10125 %r10135 %r10126 %r10136) and with no constants of its own and no
ld.global — the four cvt.rn.f16.f32 read %f861…%f864 defined in E13:
s10174 L23448: cvt.rn.f16.f32 low, %f861
s10183 L23475: max.f16x2 %r10152,%r10065,%r10150
s10190 L23503: mul.f16x2 %r10172,%r10065,%r10169
s10314 L23933: ld.weak.global.ca.v4.u32 { %r10379,…},[%rd140] // +96128
s10325 L23956: cvt.rn.satfinite.e4m3x2.f16x2 %rs4013, %r10288
s10327 L23961: mov.b32 %r10523, {%rs4012, %rs4013}
Outputs phase 16's A quad (%r10523…%r10526) and phase 16's B (6 loads at
+96128…+98688). No column-bias add.
E16 — s10349–s10408: phase 17 B/C + packs of the normed tile
Identical in shape to E14: B at +99200 … +101760, C words at
+102272, +102288, +102304, +102320 (s10369 L24103: ld.global.u32 %r10590, [%rd317+102272]), and 24 packs of %r15561…%r15584 (s10373 ff.) → phase 17's
three A quads %r10653…%r10656, %r10673…%r10676, %r10693…%r10696.
E17 — s10421–s10584: clamped cubic on phase 17's D
164 statements. 8 activation groups (17 statements each, the E13(a) shape) on
phase 17's chain-final D registers %r10621 %r10631 %r10622 %r10632 %r10681 %r10691 %r10682 %r10692; no constants of its own (cvt.rn.f16.f32 low, %f861
opens the first group at s10422); 6 weight loads at W + 16*laneid + 102336 + 512*i = phase 18's B; 8 cvt + 4 mov.b32 → phase 18's A quad
%r11079…%r11082.
E18 — s10597–s10656: phase 19 B/C loads + packs of the normed tile
60 statements: 6 weight loads at +105408 … +107968 (phase 19's B), 4
ld.global.u32 at +108480, +108496, +108512, +108528 (phase 19's C seeds,
each used as {%rX, %rX}), and 24 cvt / 12 mov.b32 of %r15561…%r15584 →
phase 19's three A quads %r11209…%r11212, %r11229…%r11232,
%r11249…%r11252. Evidence:
s10601 L24906: ld.weak.global.ca.v4.u32 { %r11088,…},[%rd158];
s10617 L24935: ld.global.u32 %r11146, [%rd324+108480].
E19 — s10669–s10832: clamped cubic on phase 19's D
164 statements, identical to E17. Chain-final D %r11177 %r11187 %r11178 %r11188 %r11237 %r11247 %r11238 %r11248; phase 20's B at +108544 … +111104;
phase 20's A quad %r11635…%r11638.
E20 — s10845–s10904: phase 21 B/C loads + packs of the normed tile
60 statements. B +111616 … +114176; C +114688, +114704, +114720, +114736;
A quads %r11765…%r11768, %r11785…%r11788, %r11805…%r11808. Evidence:
s10849 L25738: ld.weak.global.ca.v4.u32 { %r11644,…},[%rd170];
s10865 L25767: ld.global.u32 %r11702, [%rd331+114688].
E21 — s10917–s11080: clamped cubic on phase 21's D
164 statements. Chain-final D %r11733 %r11743 %r11734 %r11744 %r11793 %r11803 %r11794 %r11804; phase 22's B at +114752 … +117312; phase 22's A
quad %r12191…%r12194.
E22 — s11093–s11152: phase 23 B/C loads + packs of the normed tile
60 statements. B +117824 … +120384; C +120896, +120912, +120928, +120944;
A quads %r12321…%r12324, %r12341…%r12344, %r12361…%r12364. Evidence:
s11097 L26570: ld.weak.global.ca.v4.u32 { %r12200,…},[%rd182];
s11113 L26599: ld.global.u32 %r12258, [%rd338+120896].
E23 — s11165–s11328: clamped cubic on phase 23's D
164 statements. Chain-final D %r12289 %r12299 %r12290 %r12300 %r12349 %r12359 %r12350 %r12360; phase 24's B at +120960 … +123520; phase 24's A
quad %r12747…%r12750.
E24 — s11341–s11400: phase 25 B/C loads + packs of the normed tile
60 statements. B +124032 … +126592; C +127104, +127120, +127136, +127152;
A quads %r12877…%r12880, %r12897…%r12900, %r12917…%r12920. Evidence:
s11345 L27402: ld.weak.global.ca.v4.u32 { %r12756,…},[%rd194];
s11361 L27431: ld.global.u32 %r12814, [%rd345+127104].
E25 — s11413–s11576: clamped cubic on phase 25's D
164 statements. Chain-final D %r12845 %r12855 %r12846 %r12856 %r12905 %r12915 %r12906 %r12916; phase 26's B at +127168 … +129728; phase 26's A
quad %r13303…%r13306.
E26 — s11589–s11648: phase 27 B/C loads + packs of the normed tile
60 statements. B +130240 … +132800; C +133312, +133328, +133344, +133360;
A quads %r13433…%r13436, %r13453…%r13456, %r13473…%r13476. Evidence:
s11593 L28234: ld.weak.global.ca.v4.u32 { %r13312,…},[%rd206];
s11609 L28263: ld.global.u32 %r13370, [%rd352+133312].
E27 — s11661–s11824: clamped cubic on phase 27's D
164 statements. Chain-final D %r13401 %r13411 %r13402 %r13412 %r13461 %r13471 %r13462 %r13472; phase 28's B at +133376 … +135936; phase 28's A
quad %r13859…%r13862.
E28 — s11837–s11896: phase 29 B/C loads + packs of the normed tile
60 statements. B +136448 … +139008; C +139520, +139536, +139552, +139568;
A quads %r13989…%r13992, %r14009…%r14012, %r14029…%r14032. Evidence:
s11841 L29066: ld.weak.global.ca.v4.u32 { %r13868,…},[%rd218];
s11857 L29095: ld.global.u32 %r13926, [%rd359+139520].
E29 — s11909–s12072: clamped cubic on phase 29's D
164 statements. Chain-final D %r13957 %r13967 %r13958 %r13968 %r14017 %r14027 %r14018 %r14028; phase 30's B at +139584 … +142144; phase 30's A
quad %r14415…%r14418.
E30 — s12085–s12144: phase 31 B/C loads + packs of the normed tile
60 statements. B +142656 … +145216; C +145728, +145744, +145760, +145776;
A quads %r14545…%r14548, %r14565…%r14568, %r14585…%r14588. Evidence:
s12089 L29898: ld.weak.global.ca.v4.u32 { %r14424,…},[%rd230];
s12105 L29927: ld.global.u32 %r14482, [%rd366+145728].
E31 — s12157–s12320: clamped cubic on phase 31's D
164 statements. Chain-final D %r14513 %r14523 %r14514 %r14524 %r14573 %r14583 %r14574 %r14584; phase 32's B at +145792 … +148352; phase 32's A
quad %r14971…%r14974.
E32 — s12333–s12392: phase 33 B/C loads + packs of the normed tile
60 statements. B +148864 … +151424; C +151936, +151952, +151968, +151984;
A quads %r15101…%r15104, %r15121…%r15124, %r15141…%r15144. Evidence:
s12337 L30730: ld.weak.global.ca.v4.u32 { %r14980,…},[%rd242];
s12353 L30759: ld.global.u32 %r15038, [%rd373+151936].
E33 — s12405–s12568: clamped cubic on phase 33's D
164 statements. Chain-final D %r15069 %r15079 %r15070 %r15080 %r15129 %r15139 %r15130 %r15140; phase 34's B at +152000 … +154560; phase 34's A
quad %r15527…%r15530.
E34 — s12581–s12640: phase 35 B/C loads + packs of the normed tile
60 statements. B +155072 … +157632; C +158144, +158160, +158176, +158192;
A quads %r15657…%r15660, %r15677…%r15680, %r15697…%r15700. Evidence:
s12585 L31562: ld.weak.global.ca.v4.u32 { %r15536,…},[%rd254];
s12601 L31591: ld.global.u32 %r15594, [%rd380+158144].
E35 — s12653–s12816: clamped cubic on phase 35's D
164 statements. Chain-final D %r15625 %r15635 %r15626 %r15636 %r15685 %r15695 %r15686 %r15696; phase 36's B at +158208 … +160768
(s12794 L32253: ld.weak.global.ca.v4.u32 { %r15939,…},[%rd260]); phase 36's A
quad %r16083…%r16086. This is the last of the twelve band activations; its
output feeds phase 36, whose output E36 packs (and this is where the A-quad
chain from the normed tile ends).
Shape of the whole MLP band (E12–E36)
E12 normed tile N (24 b32, %r15561…%r15584) — RMS norm of the attention output
├─ pack → P13 A (s9784) P13 : A = N, B = W+86592, C = bias@89664 → D13
│ E13: clamped cubic(D13) pack → P14 A; D12 + table@92800 → P14 C; P14 B = W+89728
├─ pack → P15 A (s10126) P15 : A = N, B = W+92992, C = bias@96064 → D15
│ E15: clamped cubic(D15) pack → P16 A; P16 B = W+96128, C = D14
├─ pack → P17 A (s10373) P17 : A = N, B = W+99240, C = bias@102272 → D17
│ E17: clamped cubic(D17) pack → P18 A; P18 B = W+102336, C = D16
⋮ (twenty-four phases, twelve repetitions of the pair)
├─ pack → P35 A (s12606) P35 : A = N, B = W+155072, C = bias@158144 → D35
│ E35: clamped cubic(D35) pack → P36 A
└───────────────────────── P36 : A = act(D35), B = W+158208, C = D34 → D36
So the band is twelve branches off one shared normed tile, each branch being
the pair (odd phase: 48×96×32 banded GEMM with clamped cubic and a per-column
bias; even phase: 16×96×32 GEMM whose C is the running residual D12 + table
and whose output is fed to the next odd phase as B), and the residual chain
runs D12 → D14 → D16 → … → D36. D36 (12 registers, 16×96) is the band's
output and is the input to E36.
E36 — s12829–s13698: arena scatter, shared staging, phase 37 A/B/C
870 statements. Four sequential moves.
(a) requantise, s12829–s12864. 24 cvt.rn.satfinite.e4m3x2.f16x2 + 12
mov.b32 → %r183…%r194 (12 b32), the requantised 16×96 output of phase 36.
Each pair of e4m3 registers is built from (reg0, reg2) / (reg1, reg3) of a
phase-36 D brace group:
s12829 L32388: cvt.rn.satfinite.e4m3x2.f16x2 %rs4341, %r15991
s12830 L32391: cvt.rn.satfinite.e4m3x2.f16x2 %rs4340, %r15971
s12831 L32393: mov.b32 %r183, {%rs4340, %rs4341}
(b) plane-arena scatter to param+48, s12865–s13009.
s12865 L32471: ld.param.u64 %rd383, [%rd1+-32] // %rd1 = param_0+80 → param_0+48
s12866 L32472: cvta.to.global.u64 %rd5, %rd383
s12868 L32476: shr.u32 %r195, %r16115, 2 // g = laneid>>2
s12869 L32477: and.b32 %r196, %r16115, 3 // t = laneid&3
s12870 L32478: shr.u32 %r16389, %r16115, 5
s12872 L32480: add.s32 %r16391, %r182, %r16389 // y = %r182 + (laneid>>5)
s12878 L32486: add.s32 %r16392, %r1, %r16390 // x = %r1 + (g&7)
s12884 L32492: mad.lo.s32 %r197, %r5, %r16391, %r16393 // %r5 = 8*ex
s12885 L32493: add.s32 %r16394, %r196, %r197 // + t (+ guard bit)
s12886 L32494: selp.b32 %r198, -1, %r16394, %p1
s12887 L32495: setp.lt.s32 %p197, %r198, 0
s12888 L32496: @%p197 bra $L__BB1_196
s12889 L32498: mul.wide.s32 %rd384, %r198, 4
s12890 L32499: add.s64 %rd385, %rd5, %rd384
s12891 L32500: st.global.u32 [%rd385], %r183
Twelve st.global.u32, all with idx = 8·ex·y + 8·x + d in units of 4 bytes:
| # | stmt | value | index base | guard | d |
|---|---|---|---|---|---|
| 1 | s12891 | %r183 |
%r197 = 8*ex*(%r182 + (laneid>>5)) + 8*(%r1 + (g&7)) |
p1 | t |
| 2 | s12900 | %r184 |
%r197 |
p1 | t|4 |
| 3 | s12933 | %r185 |
%r201 = 8*ex*(%r182 + ((g+8)>>3)) + 8*(%r1 + ((g+8)&7)) |
p2 | t |
| 4 | s12941 | %r186 |
%r201 |
p2 | t|4 |
| 5 | s12950 | %r187 |
%r204 = %r197 + %r6 |
p1 | t |
| 6 | s12958 | %r188 |
%r204 |
p1 | t|4 |
| 7 | s12967 | %r189 |
%r207 = %r201 + %r6 |
p2 | t |
| 8 | s12975 | %r190 |
%r207 |
p2 | t|4 |
| 9 | s12984 | %r191 |
%r210 = %r204 + %r6 |
p1 | t |
| 10 | s12992 | %r192 |
%r210 |
p1 | t|4 |
| 11 | s13001 | %r193 |
%r213 = %r207 + %r6 |
p2 | t |
| 12 | s13009 | %r194 |
%r213 |
p2 | t|4 |
with %r5 = 8*ex (s15), %r6 = ey·8·ex (s16, one image plane), %r182 = %r2 + 2·%tid.z (s9185), g = laneid>>2, t = laneid&3. Each store block is
guarded by p1/p2 = (y<0)|(y≥ey)|(laneid>63)|(x<0)|(x≥ex) (s12874–s12882,
s12916–s12924) and a guard failure stores -1 as the index, which the following
setp.lt.s32 %p, %idx, 0 + bra turns into "skip the store" (the pattern of
s12886–s12888). Note the index also has the guard bit added
(add.s32 %r16394, %r196, %r197 at s12885 uses %r196 — a predicate register
read as 0/1), which the selp/bra pair makes harmless.
(c) shared staging, s13013–s13131.
s13013 L32645: bar.sync 0
s13014 L32647: mov.u32 %r16412, %laneid
s13018 L32652: add.s32 %r16414, %r17741, %r16413 // %r17741 = 2*%tid.z, %r16413 = laneid>>5
s13022 L32656: shl.b32 %r16415, %r16414, 5
s13023 L32657: and.b32 %r16416, %r16412, 28
s13025 L32659: or.b32 %r218, %r217, %r16417
s13031 L32666: st.shared.u32 [%r16420], %r183
s13039 L32676: st.shared.u32 [%r16424], %r184 // index + 256
s13059 L32698: st.shared.u32 [%r16434], %r185 // group 2, base from (laneid>>2)+8
s13067 L32708: st.shared.u32 [%r16438], %r186
… +512, +768, +1024, +1280 variants for %r187…%r194 …
s13131 L32788: st.shared.u32 [%r16470], %r194
Twelve st.shared.u32, word index for the first six
(2·%tid.z)·32 + (laneid & 28) and +256, and for the last six
((2·%tid.z)+((laneid>>2)+8)>>3)·32 + (((laneid>>2)+8)<<2 & 28) and +256
(s13043–s13058, s13061–s13074). See §6 U3 for the address collision this
produces. A second bar.sync 0 follows at s13136.
(d) phase 37's A operand from shared, s13138–s13625.
s13138 L32798: ld.param.v2.u32 {%r16472, %r16473}, [%rd489+-80] // param_0+0 = (ex, ey)
s13143 L32804: shr.u32 %r16475, %r16471, 2
s13146 L32807: and.b32 %r16478, %r16477, -64
s13148 L32809: and.b32 %r16480, %r16479, 24
s13150 L32811: or.b32 %r16482, %r16476, %r16481
s13151 L32812: shl.b32 %r16483, %r16482, 2
s13152 L32813: add.s32 %r16485, %r3216, %r16483
s13153 L32814: ld.shared.u32 %r235, [%r16485]
s13165 L32826: ld.shared.u32 %r236, [%r16490]
… 48 loads total, %r235…%r282 …
s13625 L33286: ld.shared.u32 %r282, [%r16812+144]
48 ld.shared.u32 = 12 A quads (one {a0,a2,a1,a3}-ordered quad each). The
first word's index is
word = ((laneid<<2) & ~63) | ((laneid<<1) & 24) | ((laneid>>2)&3)
= 64·(laneid>>4) + 8·((laneid>>2)&1) + ((laneid>>2)&3)
byte = smem + 4·word
and the remaining 47 use the same base plus +4, +8, +12, +16, +20 and a
divisor-by-24 decomposition of (laneid&3)|4 / |8 / |12 … (s13156 mul.wide.u16 %r16486, %rs4437, -21845 / s13157 shr.u32 %r16487, %r16486, 20
computes ⌊x/24⌋, s13159–s13160 the remainder, s13161–s13163 select the
byte plane), with the whole block guarded by setp.gt.u32 %p233, %r17795, 127
(s13140) and by bounds checks against (ex, ey) at s13287–s13295.
(e) phase 37's B, s13634–s13687.
s13634 L33296: mul.lo.s32 %r17412, %r17795, 384 // %r17795 = %tid.z<<5
s13635 L33297: cvt.u64.u32 %rd433, %r17412
s13636 L33298: add.s64 %rd434, %rd2, %rd433
s13638 L33302: mul.wide.u32 %rd435, %r16825, 16
s13639 L33303: add.s64 %rd436, %rd434, %rd435
s13640 L33304: add.s64 %rd409, %rd436, 161280
s13641 L33306: ld.weak.global.ca.v4.u32 { %r16826,%r16827,%r16828,%r16829},[%rd409]
… 24 loads at 512 stride, last at +173056 …
s13687 L33398: ld.weak.global.ca.v4.u32 { %r16918,%r16919,%r16920,%r16921},[%rd432]
24 weight fragments at W + 12288·%tid.z + 16·laneid + 161280 + 512·i, i = 0…23 = 12288 bytes = 48 B pairs = phase 37's B. The whole block is inside the
loop $L__BB1_244 (s13633) which advances %r17795 by 128 and branches back at
s13847–s13850 while %r17795 < 128; with %r17795 = %tid.z<<5 this is a single
iteration for %tid.z ≤ 3.
(f) phase 37's C, s13688–s13698. 4 ld.global.u32 at
W + ((laneid*4)&12) + {210432, 210448, 210464, 210480} → %r16932, %r16942, %r17172, %r17182, each used as {%rX, %rX} — a per-column bias replicated over
the 16 rows, seeding the four chains of phase 37.
Inputs: phase 36's 24 D, %r3216, W, (ex,ey). Outputs: the 12 arena
stores (side effect), %r183…%r194, phase 37's 12 A quads, 48 B pairs, 4 C
seeds. Memory: 12 global stores, 12 shared stores, 48 shared loads, 24
weight loads, 4 table loads, 2 barriers.
E37 — s13747–s13870: packs + 4 stores to param+56
124 statements.
(a) packs, s13747–s13756. 8 cvt + 3 mov.b32 from phase 37's chain-final
D registers → %r287 (2 b32), %rs4534/%rs4535, %rs4540/%rs4541:
s13747 L33750: cvt.rn.satfinite.e4m3x2.f16x2 %rs4534, %r17143
s13748 L33753: cvt.rn.satfinite.e4m3x2.f16x2 %rs4535, %r17383
s13749 L33756: cvt.rn.satfinite.e4m3x2.f16x2 %rs4537, %r17393
s13750 L33759: cvt.rn.satfinite.e4m3x2.f16x2 %rs4536, %r17153
s13751 L33761: mov.b32 %r287, {%rs4536, %rs4537}
(b) the four stores to param+56 (O), s13757–s13845.
s13763 L33783: shr.u32 %r17418, %r16473, 1 // ey>>1
s13764 L33784: shr.u32 %r17419, %r16472, 1 // ex>>1
s13765 L33785: shl.b32 %r17420, %r17419, 3 // 8*(ex>>1)
s13766 L33786: mul.lo.s32 %r17421, %r17418, %r17420
s13767 L33787: shr.u32 %r17422, %r17795, 5 // %tid.z (loop-carried)
s13768 L33788: mul.lo.s32 %r290, %r17421, %r17422
s13769 L33789: add.s32 %r17423, %r283, %r17416 // y = (%r2>>1) + (laneid>>4)
s13776 L33796: mad.lo.s32 %r17424, %r17420, %r17423, %r290
s13777 L33797: add.s32 %r17425, %r284, %r17417 // x = (%r1>>1) + ((laneid>>2)&3)
s13783 L33803: add.s32 %r291, %r17426, %r17424
s13784 L33804: or.b32 %r17427, %r17415, %r291 // + t
s13791 L33812: st.global.v2.u16 [%rd445], {%rs4534, %rs4535}
s13802 L33825: st.global.u32 [%rd448], %r287
s13836 L33861: st.global.u32 [%rd451], %r288
s13845 L33872: st.global.v2.u16 [%rd454], {%rs4540, %rs4541}
%r283 = %r2>>1 (s13628), %r284 = %r1>>1 (s13631), %r17416 = laneid>>4,
%r17417 = (laneid>>2)&3, t = laneid&3; the second pair recomputes
y' = (%r2>>1) + (((laneid>>2)+8)>>2), x' = (%r1>>1) + (((laneid>>2)+8)&3)
(s13811–s13822) at the same base %r290. Extents are halved (ex>>1,
ey>>1) and the destination is param+56, not the plane arena. Guards are
p6 = (y<0)|(y≥(ey>>1))|(laneid>63)|(y-constrained tid.z>127)|(x<0)|(x≥(ex>>1))
(s13762–s13781), with the same selp −1 + bra skip.
(c) loop close and phase 38's operands, s13846–s13870.
s13847 L33875: add.s32 %r299, %r17795, 128
s13848 L33876: setp.gt.u32 %p256, %r17795, -129
s13849 L33877: mov.u32 %r17795, %r299
s13850 L33878: @%p256 bra $L__BB1_244
s13852 L33881: mov.u32 %r17758, _ZZ…E4smem
s13858 L33889: add.s64 %rd455, %rd459, 210688
s13859 L33891: ld.weak.global.ca.v4.u32 { %r17443,%r17444,%r17445,%r17446},[%rd455]
s13861 L33895: ld.weak.global.ca.v4.u32 { %r17447,…},[%rd456] // +211200
s13863 L33899: ld.weak.global.ca.v4.u32 { %r17451,…},[%rd457] // +211712
s13869 L33908: ld.global.u32 %r17465, [%rd463+212224]
s13870 L33909: ld.global.u32 %r17475, [%rd463+212240]
Three weight fragments at W + 16*laneid + {210688, 211200, 211712} = phase
38's 6 B pairs, and 2 ld.global.u32 at W + ((laneid*4)&12) + {212224, 212240}
= phase 38's C seeds (each used as {%rX, %rX}).
E38 — s13877–s14199: the final epilogue
323 statements: 4 st.shared.u32 (staging phase 38's D), 5 ld.shared.u32 +
1 ld.shared.v2.u16 (the softmax window), 1 real row max over 5 values, 5
ex2.approx.ftz.f32 + 5 div.approx.ftz.f32… no — 9 tex.base.2d.v4.f16.s32,
3 sust.b.2d.v4.b16.zero, 4 shfl.sync.down.b32, 3 ld.param.u64, 1 ret.
(a) stage phase 38's D for the softmax, s13877–s13907.
s13877 L33952: bar.sync 0
s13878 L33954: mov.u32 %r17516, %laneid
s13882 L33959: add.s32 %r17521, %r304, %r17746 // g + 16*tid.z
s13883 L33960: and.b32 %r17522, %r17521, 1073741816 // & 0x40000008
s13884 L33961: or.b32 %r17523, %r17522, %r17518 // | (laneid>>2)&7
s13885 L33962: mad.lo.s32 %r17524, %r17523, 6, %r305 // *6 + t
s13887 L33964: add.s32 %r306, %r17758, %r17525 // *4 → byte address
s13888 L33965: st.shared.u32 [%r306], %r17496
s13892 L33970: st.shared.u32 [%r306+16], %r17506
s13905 L33984: st.shared.u32 [%r308], %r17497
s13907 L33987: st.shared.u32 [%r308+16], %r17507
Four words staged: the phase-38 chain-final pairs {%r17496,%r17497} and
{%r17506,%r17507}, at a 6-word row pitch (*6) plus t, in two banks 16 bytes
apart for the two rows g and g+8.
(b) bounds gate, s13909–s13939. %r311 = %r17766 + (tid.x&7) with
%r17766 = 8*ctaid.x - rs126 (the x origin) and %r312 = %r17762 + (tid.x>>3); p265 = (tid.z>1)|((%r312|%r311)<0)|(%r311≥ex)|(%r312≥ey) (s13931–s13939)
skips the whole softmax/gather. Note (tid.x + 32*tid.z) is first reduced mod 8
into %r309 = ... & -8 and %r310 = ... >>3 (s13922–s13928).
(c) the softmax window, s13940–s14038.
s13946 L34028: ld.shared.u32 %r17554, [%r17703]
s13948 L34030: ld.shared.u32 %r17555, [%r17703+4]
s13950 L34032: ld.shared.u32 %r17558, [%r17703+8]
s13951 L34033: ld.shared.u32 %r17561, [%r17703+12]
s13952 L34034: ld.shared.v2.u16 {%rs4674, %rs4675}, [%r17703+20]
s13954 L34036: ld.shared.u32 %r17575, [%r17703+16]
s13957 L34040: cvt.f32.f16 %f865, %rs4542
s13958 L34043: mul.ftz.f32 %f893, %f865, 0fBFB8AA3B // -log2 e
s13959 L34044: ex2.approx.ftz.f32 %f894, %f893
s13960 L34045: add.ftz.f32 %f895, %f894, 0f3F800000
s13962 L34047: div.approx.ftz.f32 %f866, %f896, %f895 // %f896 = 1.0
s13964 L34053: max.f16x2 %r17553,%r17554,%r17555
s13965 L34057: max.f16x2 %r17556,%r17553,%r17558
s13966 L34061: max.f16x2 %r17559,%r17556,%r17561
s13968 L34066: max.f16 %rs4544,%rs4545,%rs4546
s13969 L34070: max.f16 %rs4547,%rs4544,%rs4549
s13970 L34073: mov.b32 %r17576, {%rs4547, %rs4547}
s13971 L34075: sub.f16x2 %r17562,%r17554,%r17576
s13974 L34083: mul.ftz.f32 %f897, %f867, 0f3FB8AA3B // +log2 e
s13975 L34084: ex2.approx.ftz.f32 %f869, %f897
This epilogue does have a real row maximum. max.f16x2 twice and max.f16
twice (s13964–s13969) reduce the low halves of the five words
%r17554, %r17555, %r17558, %r17561, %r17575 (the last through %rs4549) into
%r17576, which is broadcast to both halves and subtracted
(s13971: sub.f16x2 %r17562,%r17554,%r17576). Then a textbook f16 softmax:
2^((x−max)·log2e) per element through ex2.approx.ftz.f32 (s13974–s14029),
the row sum accumulated in f32 (s13979, s13983, s13996, s14009, s14022, s14030),
inverted with div.approx.ftz.f32 1.0/… (s14031
div.approx.ftz.f32 %f885, %f896, %f914), and the five f16x2 registers scaled
(s14034–s14038 mul.f16x2 %r17577…%r17589,%…,%r17591).
(d) the 3×3 texture gather, s14039–s14121. The nine
tex.base.2d.v4.f16.s32 (s14058, s14060, s14077, s14079, s14087, s14089, s14102,
s14104, s14117) sample the surface param+88 (%rd480, s14056) at the nine
combinations of (x−1, x, x+1) × (y−1, y, y+1):
s14044 L34234: setp.lt.s32 %p267, %r311, 1
s14045 L34235: selp.b32 %r17640, 1, %r17706, %p267 // xm
s14051 L34241: selp.b32 %r17609, 1, %r17709, %p269 // ym
s14055 L34245: selp.b32 %r17642, 1, %r17710, %p271 // xp
s14058 L34249: tex.base.2d.v4.f16.s32 {%rs4569,%rs4570,%rs4571,%rs4572}, [%rd464, {%r17640,%r17609}]
%rd464…%rd480 are all mov.u64 copies of %rd480 (s14057, s14059, …). The
sampled values are combined with the five softmax weights by fma.rn.f16x2
(s14062, s14064, s14066, s14081, s14083, s14085, s14091, s14093, s14095, s14106,
s14109, s14112) and the 2×2 accumulator halves folded with add.f16 (s14114,
s14115, s14116), then one more shared value %rs4610 folded with fma.rn.f16
(s14119–s14121).
(e) sigmoid, difference, scale, cross-lane sum, s14122–s14179.
s14122 L34385: cvt.f32.f16 %f886, %rs4674
s14123 L34388: mul.ftz.f32 %f915, %f886, 0fBFB8AA3B
s14125 L34390: add.ftz.f32 %f917, %f916, 0f3F800000
s14126 L34391: div.approx.ftz.f32 %f887, %f896, %f917 // sigmoid
s14134 L34409: mul.f16 %rs4630,%rs4543,%rs4614
s14137 L34421: sub.f16 %rs4645,%rs4622,%rs4636 // acc - sigmoid*acc
s14138 L34424: mov.f32 %f892, 0f3E800000 // 0.25
s14145 L34444: mul.f16x2 %r17658,%r17659,%r17660 // * 0.25
s14152 L34463: shfl.sync.down.b32 %r17662,%r17658,%r17683,%r17665,%r17693 // delta 1
s14153 L34467: add.f16x2 %r17667,%r17658,%r17662
s14158 L34478: shfl.sync.down.b32 %r17671,%r17667,%r17691,%r17674,%r17693 // delta 8
s14159 L34482: add.f16x2 %r17676,%r17667,%r17671
s14168 L34506: add.f16 %rs4657,%rs4651,%rs4656
s14177 L34530: add.f16 %rs4663,%rs4657,%rs4662
sigmoid(v) = 1/(1 + 2^(−log2e·v)) on two more shared values
(%rs4674/%rs4675, the v2.u16 at +20), then three acc − sigmoid·acc
differences scaled by 0.25 (f16), then a two-step partial warp reduction:
shfl.sync.down delta 1 (%r17683, s13956) then delta 8 (%r17691, s13949),
each followed by an add.f16x2; the scalar path repeats the same two deltas with
add.f16 (s14168, s14177). Segmask %r17665/%r17674 is (WARP_SZ<<8)−8192|31
= 31 in both, membermask %r17693 = −1: a partial (not five-step) reduction.
(f) the three surface writes, s14178–s14199.
s14178 L34533: ld.param.u64 %rd484, [%rd491+120]
s14179 L34534: ld.param.u64 %rd487, [%rd491+128]
s14180 L34535: shl.b32 %r17697, %r311, 3
s14181 L34537: sust.b.2d.v4.b16.zero [%rd482, {%r17697,%r312}], {%rs4639,%rs4642,%rs4645,%rs4676}
s14182 L34540: sust.b.2d.v4.b16.zero [%rd484, {%r17697,%r312}], {%rs4630,%rs4633,%rs4636,%rs4677}
s14183 L34542: and.b32 %r17724, %r313, 1
s14185 L34544: mov.pred %p279, 0
s14186 L34545: xor.pred %p280, %p278, %p279
s14187 L34546: @%p280 bra $L__BB1_260
s14191 L34551: shl.b32 %r17729, %r17728, 2
s14192 L34552: and.b32 %r17725, %r17729, -8
s14197 L34558: sust.b.2d.v4.b16.zero [%rd487, {%r17725,%r17726}], {%rs4678,%rs4679,%rs4663,%rs4681}
s14199 L34562: ret
Three four-channel b16 surface writes to param+112, param+120 and
param+128; the third is taken only when (x|y) is odd
(and.b32 %r17724, %r313, 1 at s14183, @%p280 bra) and stores
mov.u16 %rs4681, 0 (s14196) in its fourth channel. %r17725 = (x<<2) & −8,
%r17726 = y>>1 for that one, versus the plain (x<<3, y) of the first two.
Inputs: the four staged phase-38 D words, %rd480 (texture), (ex,ey),
%r1/%r2/%tid.z/%tid.x, constants 0fBFB8AA3B, 0f3FB8AA3B, 0f3F800000,
0f3E800000. Outputs: none (the kernel returns).
4. The plane arena (param_0+48) and the other ld.param buffers
The four ld.param that matter, quoted verbatim:
s4 L1032: add.s64 %rd1, %rd8, 80
s5 L1033: ld.param.v2.u16 {%rs126, %rs127}, [cuda_dldn_engine_swin_enc2_kernel_param_0+80]
s8 L1036: ld.param.u64 %rd2, [cuda_dldn_engine_swin_enc2_kernel_param_0+40]
s14 L1042: ld.param.v2.u32 {%r322, %r323}, [cuda_dldn_engine_swin_enc2_kernel_param_0]
s17 L1045: ld.param.u64 %rd10, [cuda_dldn_engine_swin_enc2_kernel_param_0+8]
s12865 L32471: ld.param.u64 %rd383, [%rd1+-32] // = param_0 + 48
s13632 L33293: ld.param.u64 %rd6, [%rd492+56] // = param_0 + 56
s13941 L34023: mov.b64 %rd491, cuda_dldn_engine_swin_enc2_kernel_param_0
s13953 L34035: ld.param.u64 %rd482, [%rd491+112]
s14056 L34246: ld.param.u64 %rd480, [%rd491+88]
s14178 L34533: ld.param.u64 %rd484, [%rd491+120]
s14179 L34534: ld.param.u64 %rd487, [%rd491+128]
| offset | role | read by | written by |
|---|---|---|---|
| 0 | (ex, ey) v2.u32 |
s14 (%r322/%r323), s13138 (%r16472/%r16473) |
— |
| 8 | input image I |
prologue: 24 × ld.global.v2.u16 [%rd12] (s176, s328, … s3316) via %rd4 |
— |
| 40 | weight image W |
every weight load, §1.1 | — |
| 48 | plane arena S |
never | 12 × st.global.u32 (s12891…s13009) |
| 56 | phase-37 output buffer O |
never | 4 stores (s13791, s13802, s13836, s13845) |
| 80 | patch origins v2.u16 | s5 (%rs126/%rs127), reused at s13910–s13915 |
— |
| 88 | input texture | 9 × tex.base.2d.v4.f16.s32 (s14058…s14117) |
— |
| 112, 120, 128 | output surfaces | — | 3 × sust.b.2d.v4.b16.zero (s14181, s14182, s14197) |
Is anything generated into the plane arena? Yes, exactly one thing:
E36(b) writes phase 36's requantised 16×96 output — 12 st.global.u32, 4 bytes ×
32 lanes × 12 = 1536 bytes per block — into S at
4·(8·ex·y + 8·x + d). Nothing in the entry ever reads param_0+48:
$ grep -n '%rd5\b\|%rd383' 0015-PREPASS_ENTRYPOINT_NAME.ptx
32471:ld.param.u64 %rd383, [%rd1+-32];
32472:cvta.to.global.u64 %rd5, %rd383;
32499:add.s64 %rd385, %rd5, %rd384;
… 11 more add.s64 %rd5, all feeding st.global.u32 …
%rd5 appears only in the twelve st.global.u32 (there is no ld.* with
%rd5 as base) and %rd383 only at s12865. So the arena write is a pure
side effect for the next launch; the same 12 registers are also consumed
inside this kernel via the shared staging of E36(c).
The one surface-write-style marker the brief attributes to a "phase 30
epilogue" (surface-write:32@s25247) has no counterpart here: s25247 does not
exist (the entry ends at s14199) and E30 is a 60-statement load/pack epilogue
with no store of any kind (§3/E30; verified by enumerating every st.* in the
entry — the only st.global are s12891–s13009 and s13791–s13845, and the only
st.shared are the prologue's six, E8's four, E36's twelve and E38's four).
That marker belongs to a different linearisation — see §6 U6.
5. Cross-lane reductions worth calling out
Introduction: the entry contains exactly 32 shfl.sync.bfly.b32 and 4
shfl.sync.down.b32; 8 of the former are in the prologue, 16 in E7, 4 in E12 and
4 in E38. Each butterfly uses the shared delta operands %r3037 = 1 (s3318),
%r3046 = 2 (s3611) and membermask %r3048 = −1 (s3604), with a segmask built as
((WARP_SZ<<8) − 8192) | 31 = 31.
- The prologue's RMS reduction (s3605, s3612, s3621, s3627, s3637, s3643,
s3652, s3658): four butterfly pairs over lane bits 0 (
%r3037) and 1 (%r3046), each followed byadd.f16x2and a finaladd.f16(s3646 L5446: add.f16 %rs516,%rs517,%rs518) — four row sums from the prologue's 96 squared input values. - E7's row sums (s7223–s7344): 16
shfl.sync.bfly.b32= eight such pairs, each followed byadd.f16x2and a finaladd.f16of the two halves — 8 row sums over 96 columns of the score window. - E12's RMS reduction (s9308–s9330): the same two butterfly steps
(
delta 1at s9308,delta 2at s9314) over the four lanes with equallaneid>>2, folding the 96 squares of one row; thenadd.f16of the two column halves (s9317, s9332). This is the whole cross-lane traffic of the norm. - E36's and E38's
bar.syncpairs (s8910/s8968, s13013/s13136, s13877/s13920): block-wide, guarding the shared tiles. The seventhbar.syncis the prologue's, s4588. - E38's partial lane sums (s14152–s14177): four
shfl.sync.down.b32, two on the f16x2 path (delta 1=%r17683,delta 8=%r17691) and two on the scalar path, segmask 31, membermask −1.
There is no red.* or atom.* anywhere in the entry, and movmatrix occurs only
in E7 (the 48 of §3/E7(f)).
6. Uncharacterised
U1. The (n,k) indexing of the 512-byte B tiles of phases 13…35 and 37.
The geometry of these phases is not in doubt: the chain fixes m·k = |A|,
n·k = |B|, m·n = chains, k = chain length, so phase 13 (and 15…35) is
m=1, n=4, k=3 → M=16, N=32, K=96 and phase 37 is m=1, n=4, k=12 → M=16,
N=32, K=384 (§2.2); every row then satisfies mma = (M/16)(N/8)(K/32). What I
cannot derive is which (n-tile, k-step) each 512-byte tile holds. For phase 13
the 12 B pairs sit in six tiles at W + 86592 + 512*i (E12's six loads), and the
emitted chains pair them as chain 0 = tiles {0,2,4} at byte +0, chain 1 = tiles
{0,2,4} at byte +8, chain 2 = tiles {3,4,5} at +0, chain 3 = tiles {3,4,5} at
+8 (mma s9820/s9822/s9824, s9821/s9823/s9825, s9826/s9828/s9830,
s9827/s9829/s9831). If a chain is one n-tile's three k-slices, its three tiles
would have to be the same tile — they are not; so either the weight image's tile
stride is not the (n,k)-major layout rr_layer_spec.py assumes
(:189–192, :491–498), or m·n counts something other than the chains. The
register/byte evidence above is exact and sufficient to reproduce the operands;
only their (n,k) naming is unresolved. Missing: the _NAME_prep permutation
that built the weight image, or a run of the kernel with a weight-image dump.
U2. The identity of the 12288-byte score bias. Phase 7 adds the f16 table
at W + 6336 … W + 18624 as its mma C operand, one value per element of the
64×96 score, tile-major at 512 bytes per output tile (§3/E6(a)). Whether it is a
relative-position bias, a learned score bias, or something else cannot be decided
from the PTX: it is an opaque blob in a permuted weight image. Missing: the
weight image's layout description (the _prep permutation) or the parameter
names from the model file.
U3. E36(c)'s shared staging produces 4-way write collisions. The word index
is (2·%tid.z)·32 + (laneid & 28) for the first six stores and
((2·%tid.z) + ((laneid>>2)+8)>>3)·32 + (((laneid>>2)+8)<<2 & 28) for the last
six, plus +256 for the alternating pair. laneid & 28 takes only the eight
values 0,4,…,28, so lanes 0…3, 4…7, … all write the same word, with different
data (%r183 vs %r184 are the two halves of the same D pair, and %r185 is
the other half of %r183's source). Either the four colliding lanes are
guaranteed to hold equal data in this kernel's inputs (unlikely — they are
distinct e4m3 packs of distinct D registers) or the staging is racy and the
issued values come from whichever lane wins. I could not decide which from the
PTX. Missing: a run with a shared-memory dump, or the high-level source of the
phase-36→37 hand-off. (The corresponding read side, E36(d), is 48
ld.shared.u32 with the swizzle of §3/E36(d) and is complete.)
U4. E8's shared slot layout. E8 stages four words at
smem + 2048*%tid.z + 16*laneid + {0,512,1024,1536} (s8911–s8967), but phases 9,
10, 11 read single quads at smem + 16*laneid + {2048, 4096, 6144} (s8974,
s9094, s9125) and phase 12 at smem + %tid.z*512 + 16*laneid (s8969–s8974 uses
%tid.z*512 for phase 9's other read). For %tid.z = 0 the written slots are
{0,512,1024,1536} while phase 9 reads 2048, phase 10 4096, phase 11 6144 —
disjoint. The union over %tid.z ∈ {0,1,2,3} of the writes is the 16 slots
{0…15}, which does contain 4, 8 and 12 (i.e. the reads 2048/4096/6144 are
slots 4/8/12). This is consistent only if blockDim.z = 4 and the four z-planes
write the block's shared tile cooperatively; the module has no .maxntid
declaration so I cannot confirm the launch shape. Missing: the harness's
launch configuration (gridDim/blockDim), or whatever .maxntid/.reqntid the
unstripped cubin carries.
U5. The %tid.z weight-slab strides are mutually inconsistent.
Phases 1–7 index the weight image through W + 21504*%tid.z + 16*laneid
(s4589–s4590, s4613–s4615); phases 9–36 index it through W + 16*laneid with no
%tid.z term at all (s8976–s8978); phase 37 through W + 12288*%tid.z + 16*laneid (s13634–s13639); phase 38's bias again without a %tid.z term
(s13688–s13694). Since phase 9's very first weight offset (18624) is smaller
than the 21504-byte stride of phases 1–7, the two regions overlap for %tid.z ≥ 1
whatever the value of %tid.z. The addresses are as quoted and are sufficient
to reproduce each load; the reason they differ is not derivable from the PTX.
Missing: the launcher's gridDim.z and the weight-image builder. For
%tid.z = 0 the touched byte range of the image is [192, 212244) (see below).
Weight-image extent. Relative to the pointer named in U5 (i.e. excluding any
%tid.z term and the 16*laneid lane stride):
- the highest weight-fragment offset is
173056 + 16(phase 37's lastld.weak.global.ca.v4.u32, s13687,add.s64 %rd432, %rd436, 173056); - the highest scalar-table offset is
212240 + 4(phase 38's second C seed,ld.global.u32 [%rd463+212240], s13870), and%rd463 = %rd3 + ((laneid*4)&12)so the true last byte is212240 + 12 + 4 = 212256.
The weight image therefore spans at least 212256 bytes, and per block the
touched range is [16*laneid + 192, 16*laneid + 212256). Per-%tid.z slabs add
21504*%tid.z (phases 1–7) and 12288*%tid.z (phase 37) on top (U5).
U6. The brief's stale landmarks. Three statement indices given with this
ticket do not exist in this linearisation: s25247 (the entry has 14199
statements), and the range markers "phase 30's epilogue has
surface-write:32@s25247". s12891 (surface-write:12) and s13791
(surface-write:4) do match, and s13959 / s14058 match the reported
softmax:12 and texture-read:9. So only the s25247 landmark is foreign; it
is most likely from an enc0-era numbering. Missing: the numbering that
produced s25247, if it is meant to be in this layer.
U7. The semantic names of the per-column tables. W + 0 … 192 (the
prologue's norm gain, 12 loads at +0,16,…,176: s4198–s4209), W + 86208 … 86400
(E8's residual add), W + 86400 … 86592 (E12's norm gain), W + 92800 … 92992
(E13's residual add), and the four-word biases at 89664, 96064, 102272, …,
158144, 210432, 212224 are all read with the same
W + ((laneid<<2)&12) + off pattern and are 96 f16 or 32 f16 wide. Their roles
are computable from the arithmetic (gain in the two RMS norms, additive per-column
bias elsewhere) but their names are not. Missing: the weight-image layout or
the model's parameter names.
U8. Phase 37's K = 384 against a 96-wide A. The chain gives phase 37
m=1, n=4, k=12 → M=16, N=32, K=384, and 48 mma (verified: 4 chains of 12, each
step a distinct A quad and a distinct B pair). But the A operand is 12 quads =
48 registers read from a shared tile whose source (E36(c)) stages only 12 words
per lane, and E36(a) packs only 12 of phase 36's 24 D registers — i.e. one of the
twelve 16×8 n-tiles of phase 36's output is staged per staged word. The
mechanical relation between the 12 staged words, the 48 A registers, and a
384-deep k axis is not determined without the swizzle's intent. Missing: the
high-level structure of the phase-36→37 merge, or a shared-memory dump.
U9. The residual operand added into phase 9's C. E8(d) adds a per-column
table (at W+86208) to 48 f16 values that are the halves of the prologue's
quantised-input registers %r2798…%r2845. The mechanics are unambiguous (the
defining mov.b32 {%rs1100, %rs1103}, %r2798 at s3366 has no successor
anywhere in the entry, and %r2798 is cvt.rn.f16x2.e4m3x2 %r2798, %rs128 of
the input element loaded at s176). What is not derivable from the PTX is
whether this really is a semantic skip connection from the block input (which
would require the input patch's per-lane layout to coincide with an mma C
operand), or whether the input patch registers happen to alias a value the
compiler could have recomputed from phase 8's D. Missing: the high-level
source, or a numeric run showing whether phase 9's output equals
pack(D8)·W9 + table + input.
7. Quick acceptance index
| requirement | where |
|---|---|
| phase table for all 38 phases, M/16·N/8·K/32 == mma checked, mismatches named | §2.2: the identity holds on all 38 once the tool's M = 16·|A|, N = 8·|B| are divided by k = K/32; the flagged rows 8, 13…35, 37, 38 are re-derived there (the tool's K column is right, its M,N and full/band label wrong) |
| weight image extent | §6 U5 (212256 bytes, per-block) |
| phases that read no weight operand | §2.2 and §3/E6(b), E7(f): phases 7 and 8 — phase 7's A and B operands both come from E6's packs of phase 5's D, with A frag 0 bit-identical to B frag 0 (s4853 vs s4925) and the later ones the same data with the two halves swapped (s4865 vs s4937); phase 8's A is E7's probability packs (s8720 ff.) and its B is E7's movmatrix transposes of phase 6's D (s8598 ff.) |
| every epilogue named | §2.1 (38 rows), §3 (38 ### sections) |
| the norms: mean-subtracting LN or RMS? | §3/E12: raw sum of squares, only 2⁻¹³ added, no mean and no 1/N; prologue (s3812 ff.) identical structure → both are RMS norms, with a per-column gain and the bias deferred to the next GEMM's C |
| attention: does the score have a row max? | §3/E7(b),(c): no — all 96 max.f16x2 are the clamp lower bound −0.55615234375, all 96 min.f16x2 the upper; the only warp shuffles are the 16 shfl.sync.bfly row sums |
| attention: the score's bias operand | §3/E6(a): the mma C operand, 48 distinct pairs from the 12288-byte f16 table at W+6336, tile-major 512*(4*(n>>1)+m), one value per score element; identity → §6 U2 |
| activations | §3/E13(a), E15, E17…E33: `out = x·(0.5 + y·(0.412109375 − 0.0810546875· |
| patch merge / surface writes | §3/E36(b) (12 stores to param+48), E37(b) (4 stores to param+56), E38(f) (3 sust to param+112/+120/+128) |
| final texture-reading epilogue | §3/E38: 4 staged words, real 5-value row max, softmax, 9 tex.2d, 3×3 fma gather, sigmoid, 0.25 scale, two shfl.sync.down steps |
| per-launch table into the plane arena | §4: exactly one — E36's 1536 bytes per block; param+48 is written and never read |
| cross-lane reductions | §5 |
| uncharacterised | §6 U1–U9 |