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).
51 KiB
cuda_dldn_engine_swin_dec1_kernel — the epilogues of phases 1…26
Companion to kernels/rr/rr_layer_spec.py dec1. Everything below was read out of
~/.cache/d4r-rr-corpus/0022-PREPASS_ENTRYPOINT_NAME.ptx, entry
cuda_dldn_engine_swin_dec1_kernel (16304 statements, 44528 physical lines).
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.rr_layer_spec.py dec1prints the ranges; this file names what each one computes. Because phase 1 is the first GEMM, the prologue (s1–s402) doubles as phase 1's prelude and is described here too. Wdenotes the prepared weight image:ld.param.u64 %rd2, [cuda_dldn_engine_swin_dec1_kernel_param_0+40](s8,L1036) andcvta.to.global.u64 %rd3, %rd2(s9,L1037). Everyld.weak.global.ca.v4.u32readsW + 16*laneid + imm.- Byte offsets are relative to
Wand (where noted) carry atid.zterm. Two epilogues' load chains add a per-tid.zstride that the rest of the entry does not, so the offsets in the phase table are not all on one base — §1.1 and §7/U6. - The C/D register layout and the weight-addressing formula are fixed (see the
settled notes):
C/D reg0 = (row g, cols 2t,2t+1),reg1 = (row g+8, same cols),g = laneid>>2,t = laneid&3. - Anything I could not pin down from the PTX is in §7 under uncharacterised.
1. Shared vocabulary
1.1 Addresses and parameters an epilogue touches
| symbol | how it is obtained | statements |
|---|---|---|
param_0 |
mov.b64 %rd8, …_param_0 |
s1, L1018 |
W (weight image) |
ld.param.u64 %rd2, [param_0+40] → cvta… %rd3, %rd2 |
s8/s9, L1036–L1037 |
extents (ex, ey) |
ld.param.v2.u32 {%r2195, %r2196}, [param_0+0] |
s14, L1042 |
%r7 = 8·ex, %r8 = ey·8·ex |
shl.b32 %r7, %r2195, 3 / mul.lo.s32 %r8, %r2196, %r7 |
s15/s16, L1043–L1044 |
block origins %r1, %r2 |
sub.s32 %r1, %r2190, %r2191 / sub.s32 %r2, %r2194, %r2192 |
s7/s13, L1035/L1041 |
%rd1 = param_0+80 |
add.s64 %rd1, %rd8, 80 |
s4, L1032 |
| input plane arena | ld.param.u64 %rd34, [param_0+8] → cvta… %rd35, %rd34 |
s29/s30, L1057–L1058 |
| texture / skip input | ld.param.u64 %rd149, [%rd1+-56] = param_0+24 → cvta… %rd4, %rd149 |
s2215/s2216, L4641–L4642 |
| output plane arena | ld.param.u64 %rd388, [%rd8+48] → cvta… %rd5, %rd388 |
s15754/s15755, L38150/L38151 |
| block-origin table | ld.param.v2.u16 {%rs296, %rs297}, [param_0+80] |
s5, L1033 |
window index tid.z |
mov.u32 %r9, %tid.z |
s325, L1355 |
| shared arena | .shared .align 4 .b8 _ZZ33…dec1_kernel…E4smem[6400] |
L1027 (declaration; not a statement) |
| per-lane weight address | mul.wide.u32 %rd, %laneid, 16 + add.s64 %rd, W, %rd |
every load block, e.g. s7214–s7215, L12685–L12686 |
| per-lane table address | shl.b32 %r, %laneid, 2 → cvt.u64.u32 → and.b64 %rd, %r, 12 → add.s64 %rd, %rd3, %rd = W + ((laneid·4)&12) |
e.g. s6684–s6686, L10864–L10866 |
The last row matters: the ld.global.v2.u16 / ld.global.u32 table loads in an
epilogue read W + ((laneid<<2)&12) + off, i.e. the same 16-byte slot is read
by the four lanes with equal laneid&3, and consecutive loads pick consecutive
4-byte words inside one 64-byte record — so those tables are indexed by
column, not by row.
Two per-tid.z strides exist and are used by different groups:
| group | base | statements |
|---|---|---|
| phase 1's B | W + 12288·tid.z + 16·laneid |
s326–s331, L1356–L1363 |
| phase 1's C bias | W + 256·tid.z + ((laneid·4)&12) + 24576…24816 |
s379–s402, L1459–L1484 |
| phases 2…5 B + phase 6's bias | W + 18432·tid.z + 25216 (%rd307, s7073) |
s7068–s7073, L11935–L11940 |
%rd307 is written exactly once (s7073) and consumed only by s7092–s7095
(phase 2's B) and by the four load blocks of E2…E5 (s7122, s7153, s7184, s7215).
Every other phase's loads go straight off W with absolute offsets.
1.2 Constants that survive between epilogues
| register | first definition | f32/f64 literal | value used (after the cvt to f16) |
|---|---|---|---|
%f476 |
s7433 mov.f32 %f476, 0f3C8CCB50 |
0.017186790704727173 | f16 0x2466 = 0.017181396484375 |
%fd383 |
s7437 mov.f64 %fd383, 0dBFE1CC0000000000 |
−0.55615234375 | f16 0xB873 |
%fd385 |
s7442 mov.f64 %fd385, 0d3FE1CC0000000000 |
+0.55615234375 | f16 0x3873 |
%f478 |
s7448 mov.f32 %f478, 0f3F6D6000 |
0.92724609375 | f16 0x3B6B |
%f480 |
s7453 mov.f32 %f480, 0f3FB00000 |
1.375 | f16 0x3D80 |
%fd1 |
s6143 mov.f64 %fd1, 0d3F20000000000000 |
2⁻¹³ | f16 0x0800 = 0.0001220703125 |
%f989 |
s12414 mov.f32 %f989, 0f3ED306EB |
0.4121621549129486 | f16 0x3698 = 0.412109375 |
%f990 |
s12417 mov.f32 %f990, 0f3DA60DD6 |
0.0810810774564743 | f16 0x2D30 = 0.0810546875 |
%f991 |
s12420 mov.f32 %f991, 0f3F000000 |
0.5 | f16 0x3800 |
%f992 |
s12423 mov.f32 %f992, 0f40000000 |
2.0 | f16 0x4000 |
%f476/%fd383/%fd385/%f478/%f480 are defined inside E6 and reused by the
second softmax — there is no second softmax here, so they are consumed by E6
alone. %f989…%f992 are defined inside E10 (s12414–s12425) and re-read by
E12, E14, E16, E18, E20, E22, E24 (e.g. s12642 cvt.rn.f16.f32 low, %f989);
E12 is the only one that materialises them; the others only cvt them.
Also live across the region (defined in the prologue, read by every warp
reduction): %r5735 = 1 (s5579, L8209), %r5744 = 2 (s6061, L9582),
%r5746 = -1 (s6054, L9566) — the three operands of every
shfl.sync.bfly.b32 in the epilogues. (%r5746 is also reused as the
membermask of the E6 shuffles.)
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— its inverse, the unpack, used only when a shared or register tile that was stored e4m3-packed is read back (E1: 144 of them, s5590–s5676, L8241–L9556).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
| epilogue | range | stmts | named operation |
|---|---|---|---|
| E1 | s547–s7095 | 6549 | patch expand into shared + per-column RMS norm + gain + restage |
| E2 | s7120–s7126 | 7 | next-B weight loads (P3) |
| E3 | s7151–s7157 | 7 | next-B weight loads (P4) |
| E4 | s7182–s7188 | 7 | next-B weight loads (P5) |
| E5 | s7213–s7383 | 171 | score-bias C-seed loads (24×512 B) + 80 packs of P2/P3's D |
| E6 | s7432–s11274 | 3843 | cubic-exponent softmax, V transpose, pack |
| E7 | s11323–s11505 | 183 | pack P7's D → shared, restage A, next-B, per-column table + residual → P8's C |
| E8 | s11522–s11537 | 16 | 2 shared A fragments + 4 next-B loads (P9) |
| E9 | s11554–s12396 | 843 | RMS norm (squares, butterfly, +2⁻¹³, rsqrt, gain), packs, next B/C |
| E10 | s12413–s12833 | 421 | clamped-cubic activation + per-column table + residual, packs, next B |
| E11 | s12850–s12917 | 68 | 32 packs → P12's A, next B/C |
| E12 | s12934–s13241 | 308 | clamped-cubic activation, packs, next B |
| E13 | s13258–s13325 | 68 | 32 packs → P14's A, next B/C |
| E14 | s13342–s13649 | 308 | clamped-cubic activation, packs, next B |
| E15 | s13666–s13733 | 68 | 32 packs → P16's A, next B/C |
| E16 | s13750–s14057 | 308 | clamped-cubic activation, packs, next B |
| E17 | s14074–s14141 | 68 | 32 packs → P18's A, next B/C |
| E18 | s14158–s14465 | 308 | clamped-cubic activation, packs, next B |
| E19 | s14482–s14549 | 68 | 32 packs → P20's A, next B/C |
| E20 | s14566–s14873 | 308 | clamped-cubic activation, packs, next B |
| E21 | s14890–s14957 | 68 | 32 packs → P22's A, next B/C |
| E22 | s14974–s15281 | 308 | clamped-cubic activation, packs, next B |
| E23 | s15298–s15365 | 68 | 32 packs → P24's A, next B/C |
| E24 | s15382–s15689 | 308 | clamped-cubic activation, packs, next B |
| E25 | s15706–s15980 | 275 | pack → 16 guarded plane-arena scatter → B/C for P26 |
| E26 | s15989–s16304 | 316 | 6 D stores → shared, row max, softmax, 9+2 texture reads, surface store |
3. The epilogues
E1 — s547–s7095: patch expand, RMS norm, gain, restage
6549 statements; op histogram (top): 1102 add.s32, 523 shl.b32, 460
mov.b32, 438 $label, 414 sub.s32, 368 min.u32, 341 bra.uni, 288
mul.f16, 198 add.f16, 144 cvt.rn.satfinite.e4m3x2.f16x2, 96
cvt.rn.f16x2.e4m3x2, 96 rsqrt.approx.ftz.f32, 96 cvt.f32.f16, 96
cvt.rn.f16.f32, 54 add.f16x2, 54 st.shared.* (46 u32 + 2 v2.u16 + 6
v4.u32), 36 ld.shared.* (24 v2.u16 + 12 v4.u32), 32
ld.global.v2.u16, 12 shfl.sync.bfly.b32, 1 ld.param, 2 ld.weak….
Sub-sections (bounded by the first/last statement of each op):
| range | what |
|---|---|
| s547–s688 | 144 packs of phase 1's D → the 48 b32 %r10…%r55 |
| s689–s1367 | per-lane index arithmetic + the 48 guarded patch stores (46 st.shared.u32 s732–s1355 + 2 st.shared.v2.u16 s718/s1367); 1st bar.sync at s1369 |
| s1413–s2214 | 24 ld.shared.v2.u16 read back of the packed patch |
| s2215–s2216 | param_0+24 → %rd4: the patch-gather buffer |
| s2371–s5577 | 24 clamped ld.global.v2.u16 gathered from that buffer |
| s5578–s5674 | 96 cvt.rn.f16x2.e4m3x2 unpacks of the read-back patch |
| s5677–s6287 | the sum-of-squares trees (198 add.f16, 54 add.f16x2) |
| s6143–s6144 | the 2⁻¹³ round (s6143 mov.f64 %fd1, s6144 cvt.rn.f16.f64 %rs1082, %fd1) |
| s6293–s6670 | 96 rsqrt.approx.ftz.f32, each cvt.f32.f16 → rsqrt → cvt.rn.f16.f32 |
| s6668–s6686 | 12 statements: gain loads W + ((laneid·4)&12) + {25088…25200} (8 ld.global.v2.u16) |
| s5868–s6973 | the 288 mul.f16 (two groups: inv · gain then x · (inv·gain)) |
| s6990–s7047 | 24 packs → the 6 st.shared.v4.u32 at s7008/7021/7034/7047/7061/7066 |
| s7058–s7089 | bar.sync (s7072) + 12 ld.shared.v4.u32 (s7077–s7089) → phase 2's A |
| s7068–s7095 | %rd307 = W + 18432·tid.z + 25216 (s7073) and 2 ld.weak → phase 2's B |
Evidence for the patch scatter (48 stores):
s718 L2859: st.shared.v2.u16 [%r2393], {%rs200, %rs201}
s732 L2875: st.shared.u32 [%r2400+1600], %r10
s771 L2916: st.shared.u32 [%r2418+1600], %r12
s783 L2932: st.shared.u32 [%r2425+3200], %r13
s797 L2948: st.shared.u32 [%r2432+4800], %r14
s7066 L11932: st.shared.v4.u32 [%r5949+4608], {%r5957, %r5956, %r5955, %r5954}
The 48 patch stores are 12 blocks of 4: base offsets {0, 1600, 3200, 4800}
(12 stores each) plus two v2.u16 stores. Each block is guarded by
s708 L2848: setp.gt.u32 %p5, %r64, 9
s709 L2849: setp.gt.u32 %p6, %r62, 9
s711 L2851: @%p7 bra $L__BB1_2
and the index is (row_clamped·40 + col_clamped·8 + t)·4 bytes
(s714 mad.lo.s32 %r2389, %r64, 40, %r2388, s715 or.b32 %r2390, %r57, %r2389,
s716 shl.b32 %r2391, %r2390, 2), i.e. a 10×10 grid of 4-word cells × 4
sub-arrays = 6400 bytes = the whole shared allocation. (§7/U4: the guards
bound row/col at 9; I did not reduce the 438-label duplication to a closed form.)
The gather from param_0+24 (s2215–s2371) uses
s2245 L4674: mad.lo.s32 %r3019, %r3009, %r7, %r3018 // clamp_row·(8·ex) + clamp_col·8
s2250 L4679: add.s32 %r19034, %r3023, %r3019 // + clamp(t, - , 7)
s2369 L4806: mul.wide.u32 %rd150, %r19034, 4
s2371 L4808: ld.global.v2.u16 {%rs346, %rs347}, [%rd151]
so the element stride of the +24 buffer is 4 bytes (one f16x2 = two channels),
indexed row·(8·ex) + col·8 + clamp(t,0,7); the same buffer is read 24 times
per lane, once per (row,col) of the stencil.
The norm itself:
s6143 L9797: mov.f64 %fd1, 0d3F20000000000000 // 2^-13
s6144 L9799: cvt.rn.f16.f64 %rs1082, %fd1
s6186 L9927: add.f16 %rs1161,%rs1067,%rs1082 // ...+ eps
s6293 L10243: rsqrt.approx.ftz.f32 fl, fl
s6688 L10878: mul.f16 %rs1371,%rs1372,%rs2011 // inv * gain
s6832 L11313: mul.f16 %rs1659,%rs488,%rs1371 // value * (inv*gain)
There is no mean subtraction and no 1/N. The accumulated quantity is the
raw sum of squares of the patch's 3 channels plus the constant 2⁻¹³, and the
norm is applied per column (the gain is read at W + ((laneid<<2)&12) + off
so it is constant across rows). The 12 shfl.sync.bfly.b32 sum across the four
lanes of each laneid&3 group.
Inputs: phase 1's D (%r692…%r2083), W, param_0+8, param_0+24,
%r1/%r2/%r7/%r8/%r2195/%r2196, %r9 (tid.z).
Outputs (live past s7095): the 6 patch-expand st.shared regions, the 12
ld.shared.v4.u32 A fragments %r18458…%r18505, phase 2's B
%r5960…%r5967, %rd307, the 5 constants of §1.2.
E2/E3/E4 — s7120–s7126, s7151–s7157, s7182–s7188: next-B loads
Three 7-statement blocks, each identical in shape:
s7120 L12140: mov.u32 %r6208, %laneid
s7121 L12142: mul.wide.u32 %rd309, %r6208, 16
s7122 L12143: add.s64 %rd310, %rd307, %rd309
s7123 L12144: add.s64 %rd203, %rd310, 2048
s7124 L12146: ld.weak.global.ca.v4.u32 { %r6209,…%r6212},[%rd203] // +2560 as well
| epilogue | offsets added to %rd307 (W + 18432·tid.z + 25216) |
destination phase |
|---|---|---|
| E2 | +2048, +2560 |
P3's B |
| E3 | +1024, +1536 |
P4's B |
| E4 | +3072, +3584 |
P5's B |
Nothing else. The offsets are deliberately out of phase order: the weight sub-image is laid out P2, P4, P3, P5 (offset blocks 0, 1, 2, 3 of 1024 bytes).
E5 — s7213–s7383: score-bias C-seed loads + pack of P2/P3's D
171 statements; the only epilogue whose weight loads carry the tid.z stride.
- 24 loads
W + 18432·tid.z + 16·laneid + 25216 + 4096 + 512·i,i = 0…23, at s7217…s7263 (L12689…L12781), into%r6956…%r7051. Byte block[18432·tid.z + 29312, +41100)= 12288 bytes = 24 tiles × 512 — phase 6's C score-bias seed. - 80 packs / 40 b32, s7264–s7383, whose sources are registers written by
phase 2's and phase 3's mma (
%r6466/6467/6486/6487/…from s7096–s7126 and s7127–s7136). Two interleaved destinations —%r7498 %r7508 …and%r7244 %r7246 …— which become phase 6's B and A respectively (verified:%r7244 ← mov.b32 {%rs2061,%rs2062}at s7338 whose sources arecvtof%r6466/%r6486;%r7498 ← mov.b32 {%rs2013,%rs2014}at s7266 whose source iscvtof%r6466at s7265).
The C-seed layout. The four C registers of one mma are the four u32 of one
16-byte lane slot, and the tile index runs 4·(n>>1) + m — the same scheme
rrswin_enc0_epilogues.md §3/E9 established. Checked against phase 6's own
operands: s7384 C=%r6956,%r6957, s7385 C=%r6958,%r6959,
s7390 C=%r7004,%r7005, s7391 C=%r7006,%r7007 — consecutive pairs from the
load block, i.e. mma (m, n) takes the slot (n>>1) of the load block.
E6 — s7432–s11274: cubic-exponent softmax, V transpose, pack
Identical in kind to cuda_dldn_engine_swin_enc0_kernel's E5 (see
rrswin_enc0_epilogues.md §3/E5) and byte-for-byte the same op histogram:
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,
48 movmatrix, 16 shfl.sync.bfly.b32, 96 each of max/min/neg/abs.f16x2.
Sub-sections:
| range | stmts | what |
|---|---|---|
| s7433–s7458 | 26 | the 5 constants (§1.2): s7433 mov.f32 %f476, 0f3C8CCB50, s7437/s7442 the two f64 clamps, s7448/s7453 the cubic pair |
| s7459–s9549 | 2091 | 96 per-element groups (scale, clamp, cubic, and.b32 …,2145419232) |
| s9550–s9635 | 86 | in-lane partial sums (add.f16x2) |
| s9636–s9765 | 130 | 16 shfl.sync.bfly → 8 row sums |
| s9766–s10587 | 822 | 192 rcp.approx.ftz.f32 → 96 reciprocals |
| s10588–s11010 | 423 | 192 mul.f16 → 96 probabilities |
| s11011–s11058 | 48 | movmatrix.sync.trans.aligned.m8n8.b16 of phase 5's D |
| s11059–s11273 | 215 | 144 cvt.rn.satfinite.e4m3x2.f16x2 packs |
Evidence:
s7459 L13474: and.b32 %r9534, %r18506, 2145419232 // 0x7FE07FE0
s9636 L20270: shfl.sync.bfly.b32 %r9778,%r9774,%r5735,%r9781,%r5746
s9766 L20596: rcp.approx.ftz.f32 fl, fl
s11011 L22895: movmatrix.sync.trans.aligned.m8n8.b16 %r10138, %r6715
s11059 L23039: cvt.rn.satfinite.e4m3x2.f16x2 %rs3462, %r10146
The transposed registers %r6715…%r6946 are exactly phase 5's D (s7189–s7212).
%r10138/%r10146/… are the movmatrix results; the pack at s11059 takes
%r10146, i.e. the transposed V is what becomes phase 7's B (checked:
%r10744 ← mov.b32 {%rs3461,%rs3462} at s11061, and %r10744 is phase 7's
first B pair; the probability packs, e.g. %r10450 ← mov.b32 {%rs3509,%rs3510} at s11133, feed phase 7's A). %r6715 is phase 5's first D
and %r6946 its last, so all 48 movmatrix inputs are phase 5's D.
For the record, the per-element function (identical to enc0's E5(a), reproduced here because it is the only arithmetic in this file that is not obviously a named primitive):
m = clamp(f16(x * 0.017181396484375), -0.55615234375, +0.55615234375)
u = f16(1.375 + m*(0.92724609375 - m*m))
expval = f16 from bits ((u_bits << 5) & 0x7FE07FE0)
There is no row maximum in E6 (the only max.f16x2/min.f16x2 are the 96
clamp pairs of (a)), and no bias add — the bias is phase 6's GEMM C operand.
Inputs: phase 6's 96 D f16x2, phase 5's 48 D f16x2, %r5735/%r5744/%r5746.
Outputs: the 5 constants, phase 7's 12 A fragments + 12 B fragments.
Memory: none (0 ld.*, 0 st.* in 3843 statements).
E7 — s11323–s11505: pack P7's D → shared, restage, next-B, table → P8's C
183 statements: 64 add.f16, 48 mov.b32, 32 packs, 8 ld.global.v2.u16,
4 st.shared.v4.u32, 4 ld.shared.v4.u32, 4 ld.weak, 6 add.s64.
s11323 L23878: bar.sync 0
s11339 L23912: st.shared.v4.u32 [%r18635], {%r18639, %r18638, %r18637, %r18636}
s11352 L23941: st.shared.v4.u32 [%r18635+512], {…}
s11365 L23970: st.shared.v4.u32 [%r18635+1024], {…}
s11378 L23999: st.shared.v4.u32 [%r18635+1536], {…}
s11379 L24000: bar.sync 0
s11384 L24007: ld.shared.v4.u32 {%r18655,…%r18658}, [%r18654]
s11385 L24008: ld.shared.v4.u32 {%r18659,…%r18662}, [%r18654+512]
s11390 L24016: ld.weak.global.ca.v4.u32 { %r10893,…%r10896},[%rd233] // W+41600
s11402 L24037: ld.global.v2.u16 {%rs5218, %rs5219}, [%rd322+62096]
s11410 L24046: add.f16 %rs3640,%rs5233,%rs491
- 32 packs of phase 7's D (
%r10378…%r10428, the 192×96 banded P·V output) → 16 b32 → fourst.shared.v4.u32at[%r18635 + {0,512,1024,1536}]; thebar.syncpair makes them visible, then twold.shared.v4.u32give phase 8's A (%r18655…%r18662). - 4
ld.weakatW + 16·laneid + {41600, 42112, 42624, 43136}→ phase 8's B. - 8
ld.global.v2.u16atW + ((laneid·4)&12) + {62080…62192}— a per-column vector, 8 words per lane = 32 columns. - 64
add.f16adding that vector to the f16 halves of phase 7's D which were unpacked at s6186–s6288 (%rs488/%rs491/%rs494…), producing%r10918 %r10919 %r10928 %r10929 …— phase 8's C operand (confirmed:s11506 C=%r10918,%r10919,s11507 C=%r10928,%r10929). So phase 8 computesD8 = A8·B8 + (D7 + column_table).
E8 — s11522–s11537: next-A and next-B for P9
s11525 L24450: ld.shared.v4.u32 {%r18666,…%r18669}, [%r18665+2048]
s11526 L24451: ld.shared.v4.u32 {%r18670,…%r18673}, [%r18665+2560]
s11531 L24459: ld.weak.global.ca.v4.u32 { %r11072,…%r11075},[%rd237] // W+60032
s11537 L24471: ld.weak.global.ca.v4.u32 { %r11084,…%r11087},[%rd240] // W+61568
Two shared A fragments (the upper half of the same staging region, offsets 2048
and 2560 relative to %r18652) and four weight fragments at
W + {60032, 60544, 61056, 61568} → phase 9's A and B.
E9 — s11554–s12396: RMS norm before the second attention window
843 statements, no stores, 16 memory ops. Structure:
| range | stmts | op |
|---|---|---|
| s11554–s11699 | 146 | 32 × x·x squares of phase 9's D |
| s11700–s11757 | 58 | in-lane pairwise add.f16x2 |
| s11714–s11757 | 44 | 8 shfl.sync.bfly (2 per row) → row sums |
| s11764–s11779 | 16 | add.f16 of the two halves, then + 2⁻¹³ (§1.2 %fd1) |
| s11779–s11880 | 102 | broadcast each row sum into 4 b32 |
| s11883–s12128 | 246 | 64 rsqrt.approx.ftz.f32 |
| s12129–s12136 | 8 | gain loads W + ((laneid·4)&12) + {62208…62320} |
| s12137–s12232 | 96 | mul.f16 — inv · gain |
| s12233–s12328 | 96 | mul.f16 — x · (inv·gain) → 32 b32 %r17567…%r17598 |
| s12332–s12339 | 8 | next-B loads W + {62336, 62848, 63360, 63872} → phase 10's B |
| s12341–s12348 | 8 | next-C loads W + ((laneid·4)&12) + {64384,64400,64416,64432} → phase 10's C |
| s12349–s12396 | 48 | 32 packs → phase 10's A (%r11574…%r11577, %r11594…, …) |
Evidence:
s11557 L24589: mul.f16 %rs3832,%rs4647,%rs4647 // square of a D half
s11714 L25018: shfl.sync.bfly.b32 %r11325,%r11321,%r5735,%r11328,%r5746
s11776 L25176: cvt.rn.f16.f64 %rs4033, %fd1 // eps = 2^-13
s11883 L25588: rsqrt.approx.ftz.f32 fl, fl
s12129 L25883: ld.global.v2.u16 {%rs5234, %rs5235}, [%rd327+62224]
s12233 L26180: mul.f16 %rs4421,%rs4647,%rs4229 // D_half * (inv*gain)
s12332 L26472: add.s64 %rd241, %rd329, 62336
s12349 L26500: cvt.rn.satfinite.e4m3x2.f16x2 %rs4611, %r17568
What it computes. For each of the 8 rows a lane holds:
sumsq[row] = Σ_c x[row,c]² (f16, 4-lane butterfly)
inv = rsqrt.approx.ftz.f32(f32(f16(sumsq + 2⁻¹³))) (rounded to f16)
out[row,c] = x[row,c] · inv · gain[c]
- no mean subtraction and no
1/N— the quantity is the raw sum of squares plus the constant 2⁻¹³. gain[c]is per column (theW + ((laneid<<2)&12)pattern) and constant across rows.- the
invregister is duplicated into both halves of a b32 before use, so one row-sum serves every n-tile of that row (64rsqrtfor 16 row sums). - the norm's bias is not added here; it is phase 10's C, loaded at the end of this epilogue (s12345–s12348).
Outputs: %r11473 %r11477 (P10 B), %r11611 %r11621 %r11651 %r11661
(P10 C), 16 packed A registers, the 32 f16x2 normed values %r17567…%r17598
(re-packed by E11).
Shape of the attention region (E5–E9)
P2 A = patch(shared), B = W+25216+2048, C = 0 → D2 (96×32)
P3 A = patch(shared), B = W+25216+ 0, C = 0 → D3 (96×32)
P4 A = patch(shared), B = W+25216+1024, C = D2 → D4
P5 A = patch(shared), B = W+25216+3072, C = D3 → D5
E5 packs D2 ∪ D3 → P6 A and B; E5 loads P6 C = bias@29312
P6 A = pack(D2∪D3), B = pack(D2∪D3), C = bias → scores 64×96
E6: softmax over the 96 columns (no row max), 48 movmatrix(D5), pack
P7 A = pack(prob), B = pack(transpose(D5)), C = 0 → D7 (192×96)
E7: pack D7 → shared; P8 C = D7 + column table@62080; P8 B = W+41600
P8 A = shared, B = W+41600, C = D7+table → D8
P9 A = shared+2048, B = W+60032, C = 0 → D9
E9: RMS norm of D9 + gain, packs → P10 A
E10 — s12413–s12833: clamped-cubic activation, per-column table, residual
421 statements; this is the only epilogue that materialises %f989…%f992.
s12414 L26726: mov.f32 %f989, 0f3ED306EB
s12420 L26740: mov.f32 %f991, 0f3F000000 // 0.5
s12426 L26755: neg.f16x2 %r11687,%r11686
s12427 L26759: max.f16x2 %r11689,%r11542,%r11687
s12428 L26763: min.f16x2 %r11692,%r11689,%r11686
s12429 L26767: abs.f16x2 %r11695,%r11692
s12432 L26779: mul.f16x2 %r11703,%r11692,%r11700
s12433 L26783: add.f16x2 %r11706,%r11685,%r11703
s12434 L26787: mul.f16x2 %r11709,%r11542,%r11706
Per element, over each of the 16 f16x2 D registers of phase 10:
y = clamp(x, -2, +2)
g = 0.5 + y * (0.412109375 - 0.0810546875 * |y|) // f16 throughout
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.
Then:
| range | op |
|---|---|
| s12413–s12689 | 16 activation groups |
| s12693–s12700 | next-B loads W + {64448,64960,65472,65984} → phase 11's B |
| s12701–s12713 | ld.global.v2.u16 at W + ((laneid·4)&12) + {66496…66608} |
| s12714–s12809 | 64 add.f16 — residual add, written over the activation inputs (%rs4644/%rs4647, unpacked at s11556/… from phase 9's D) |
| s12810–s12833 | 16 packs of the activation outputs → phase 12's A |
Evidence for the residual add:
s12706 L27718: ld.global.v2.u16 {%rs5250, %rs5251}, [%rd337+66512]
s12714 L27727: add.f16 %rs4645,%rs5265,%rs4647
s12716 L27734: mov.b32 %r12189, {%rs4642, %rs4645}
s12810 L28015: cvt.rn.satfinite.e4m3x2.f16x2 %rs4835, %r11825
The residual is phase 9's D, not phase 10's: %rs4644/%rs4647 are the halves
of %r11088, unpacked at s11556 (mov.b32 {%rs4644, %rs4647}, %r11088) at the
very start of E9, and %r11088 is written by phase 9's mma (s11538–s11553).
The 32 such registers %r12189 %r12190 … %r12329 %r12330 are phase 11's C;
%r12148 %r12152 … are phase 11's B.
Note the two pack streams: the activation outputs (%r11709, %r11825, …) go
to phase 12's A, while the residual+table registers go to phase 11's C.
E11 — s12850–s12917: packs of the normed tile, next B/C
68 statements: 32 packs, 4 ld.weak, 4 ld.global.u32.
s12854 L28189: ld.weak.global.ca.v4.u32 { %r12342,…%r12345},[%rd249]
s12866 L28210: ld.global.u32 %r12480, [%rd342+68672]
s12870 L28216: cvt.rn.satfinite.e4m3x2.f16x2 %rs…, %r17567
- four
v4.u32fragments atW + {66624, 67136, 67648, 68160}→ phase 12's B; - four
ld.global.u32atW + ((laneid·4)&12) + {68672, 68688, 68704, 68720}→ phase 12's C seed, each used in both halves of the C operand; - 32 packs of
%r17567…%r17598— the normalised tile E9 produced — so phase 12's A operand is bit-identical to phase 10's A. Phases 10 and 12 are two parallel GEMMs off the same normed tile.
E12 — s12934–s13241: clamped-cubic activation, no residual
308 statements; op mix 72 mov.b32, 64 cvt.rn.f16.f32, 48 mul.f16x2, 16
each neg/max/min/abs/sub/add.f16x2, 4 ld.weak, 16 packs. No constants of
its own (it re-cvts %f989 etc. at s12642-equivalent sites) and no
ld.global.
Same function as E10; 16 packs of the activation outputs
(%r13046 at s13191, …) → phase 13's A, plus 4 weight loads at
W + {68736, 69248, 69760, 70272} → phase 13's B. No table add and no
residual, so phase 13's C is zero.
E13 — s13258–s13325: packs of the normed tile again, next B/C
68 statements, shape-identical to E11:
s13262 L29597: ld.weak.global.ca.v4.u32 { %r13210,…},[%rd257]
s13274 L29618: ld.global.u32 %r13348, [%rd349+72832]
B at W + {70784, 71296, 71808, 72320} → phase 14's B; 32 packs of
%r17567…%r17598 — the normed tile for the second time; C at
W + ((laneid·4)&12) + {72832, 72848, 72864, 72880} → phase 14's C.
E14/E16/E18/E20/E22/E24 — s13342–s13649, s13750–s14057, s14158–s14465, s14566–s14873, s14974–s15281, s15382–s15689: activation + packs + next B
Six shape-identical 308-statement epilogues (shape hash 4234ec4de215). Each
applies the E10 activation to the D of its own phase, then 16 packs of the
activation outputs → the next odd phase's A, and 4 weight loads → the next odd
phase's B. Evidence (E14):
s13376 L30936: cvt.rn.f16.f32 low, %f989
s13393 L30985: mul.f16x2 %r13727,%r13634,%r13724
s13619 L30816: ld.weak.global.ca.v4.u32 { %r13885,…},[%rd261] // W+70784
s13633 L30986: cvt.rn.satfinite.e4m3x2.f16x2 %rs…, %r13727
Offsets: E12→{68736,69248,69760,70272}, E14→{70784,71296,71808,72320}, E16→{72896,73408,73920,74432}, E18→{74944,75456,75968,76480}, E20→{77056,77568,78080,78592}, E21's targets … (from the phase table's weight column: P13=68736, P15=72896, P17=77056, P19=81216, P21=85376, P23=89536, P25=93696).
No ld.global, no table add, no residual: these six produce only A and B.
E15/E17/E19/E21/E23 — s13666–s13733, s14074–s14141, s14482–s14549, s14890–s14957, s15298–s15365: packs + next B/C
Five shape-identical 68-statement epilogues (shape hash 819f37d58cd2), the
twin of E11/E13. Example (E15):
s13670 L31005: ld.weak.global.ca.v4.u32 { %r14078,…},[%rd265]
s13682 L31026: ld.global.u32 %r14216, [%rd356+76992]
s13686 L31031: cvt.rn.satfinite.e4m3x2.f16x2 %rs…, %r17567
B at W + {72896+1024…} etc. per the phase table; C at
W + ((laneid·4)&12) + {76992, 77008, 77024, 77040} (E15 → phase 16's C);
32 packs of %r17567…%r17598, the normed tile — third, fourth, fifth, sixth
and seventh time (E13, E15, E17, E19, E21, E23 all pack the same 32 f16x2).
Shape of the whole MLP region (E9–E25)
E9 normed tile N (32 f16x2, %r17567…%r17598, s12349 packs)
├─ pack → P10 A (s12349) P10: A=N, B=W10, C=bias@64384 → act (E10)
│ pack of act → P12 A (s12870)
│ E10: P11 C = D9 + table@66496
├─ pack → P12 A (s12870) P12: A=N, B=W12, C=bias@68672 → act (E12)
│ pack of act → P14 A (s13394)
├─ pack → P14 A (s13394) P14: A=N, B=W14, C=bias@72832 → act (E14)
├─ pack → P16 A (s13806) P16: A=N, B=W16, C=bias@76992 → act (E16)
├─ pack → P18 A (s14218) P18: A=N, B=W18, C=bias@81152 → act (E18)
├─ pack → P20 A (s14630) P20: A=N, B=W20, C=bias@85312 → act (E20)
├─ pack → P22 A (s15042) P22: A=N, B=W22, C=bias@89472 → act (E22)
└─ pack → P24 A (s15454) P24: A=N, B=W24, C=bias@93632 → act (E24)
pack of act → P25 A (s15666)
The odd phases P11, P13, …, P25 take C = 0 and produce the residual pair that
the following even phase's table adds.
E25 — s15706–s15980: pack → 16 guarded plane-arena stores → B/C for P26
275 statements; the only st.global in the entry (16 of them, all here).
(a) requantise, s15706–s15753. 24 groups of 2 cvt + 1 mov.b32 → 24 b32;
the 16 consumed are %r489…%r504.
(b) plane-arena scatter, s15754–s15960.
s15754 L38150: ld.param.u64 %rd388, [%rd8+48] // = param_0+48
s15755 L38151: cvta.to.global.u64 %rd5, %rd388
s15757 L38155: shr.u32 %r505, %r18449, 2 // g = laneid>>2
s15758 L38156: and.b32 %r506, %r18449, 3 // t = laneid&3
s15761 L38159: add.s32 %r18707, %r488, %r18705 // y = %r488 + (laneid>>5)
s15767 L38165: add.s32 %r18713, %r1, %r18706 // x = %r1 + (g&7)
s15772 L38170: shl.b32 %r18714, %r18713, 3 // x<<3
s15773 L38171: mad.lo.s32 %r507, %r7, %r18707, %r18714
s15775 L38173: selp.b32 %r508, -1, %r18715, %p1
s15780 L38179: st.global.u32 [%rd390], %r489
Stride register %r7 = 8·ex (s15). Every store is
idx = 8·ex·y + 8·x + d, with d = t or t|4; each of the four base slices is
guarded by its own predicate of the form
(y<0)|(y≥ey)|(laneid>127)|(x<0)|(x≥ex) (e.g. s15762–s15771,
L38160–L38169); a guard failure stores -1 as the index, which the following
setp.lt.s32 %p, %idx, 0 + bra turns into "skip the store"
(s15774–s15777).
Store values, bases and column offsets (g = laneid>>2, t = laneid&3,
%r488 = %r2 + 4·tid.z at s11555/L24586, %r8 = ey·8·ex):
| store | stmt | value | index base | d |
|---|---|---|---|---|
| 1 | s15780 | %r489 |
idx1 |
t |
| 2 | s15789 | %r490 |
idx1 |
t|4 |
| 3 | s15815 | %r491 |
idx2 |
t |
| 4 | s15823 | %r492 |
idx2 |
t|4 |
| 5 | s15832 | %r493 |
idx1 + %r8 |
t |
| 6 | s15840 | %r494 |
idx1 + %r8 |
t|4 |
| 7 | s15849 | %r495 |
idx2 + %r8 |
t |
| 8 | s15857 | %r496 |
idx2 + %r8 |
t|4 |
| 9 | s15883 | %r497 |
idx3 |
t |
| 10 | s15891 | %r498 |
idx3 |
t|4 |
| 11 | s15917 | %r499 |
idx4 |
t |
| 12 | s15925 | %r500 |
idx4 |
t|4 |
| 13 | s15934 | %r501 |
idx3 + %r8 |
t |
| 14 | s15942 | %r502 |
idx3 + %r8 |
t|4 |
| 15 | s15951 | %r503 |
idx4 + %r8 |
t |
| 16 | s15959 | %r504 |
idx4 + %r8 |
t|4 |
with the four bases built as mad.lo.s32 %rBASE, 8·ex, y, 8·x:
| base | stmt | y | x |
|---|---|---|---|
idx1 = %r507 |
s15773, L38171 | %r488 + (laneid>>5) |
%r1 + (g&7) |
idx2 = %r511 |
s15808, L38210 | %r488 + ((g+8)>>3) |
%r1 + ((g+8)&7) |
idx3 = %r520 |
s15876, L38290 | %r488 + ((g+16)>>3) |
%r1 + ((g+16)&7) |
idx4 = %r523 |
s15910, L38328 | %r488 + ((g+24)>>3) |
%r1 + ((g+24)&7) |
The g+8j slices come from add.s32 %r18717, %r18986, 8 (s15793, L38195),
add.s32 %r18734, %r18988, 16 (s15861, L38275) and
add.s32 %r18747, %r18990, 24 (s15895, L38313); each base carries its own
guard predicate (p1…p4) of the form
(y<0)|(y≥ey)|(slice>31)|(x<0)|(x≥ex).
(c) next B/C, s15961–s15980.
s15962 L38392: ld.param.u64 %rd456, [param_0+40]
s15969 L38401: add.s64 %rd424, %rd456, %rd423 // + 16*laneid
s15971 L38404: ld.weak.global.ca.v4.u32 { %r18765,…%r18768},[%rd421] // +95744
s15973 L38408: ld.weak.global.ca.v4.u32 { %r18769,…%r18772},[%rd422] // +96256
s15979 L38417: ld.global.u32 %r18823, [%rd428+96768]
s15980 L38418: ld.global.u32 %r18833, [%rd428+96784]
Note %rd424 is built from %rd456 (the uncvtad pointer) while %rd455 is
cvtad; on this corpus both are the same address, and the offsets still read
W + ….
Outputs: %r489…%r504 (re-read as P26's A, see s15981), %r18765 %r18769
(P26 B), %r18823 %r18833 (P26 C), %rd388 (param_0+48).
E26 — s15989–s16304: the final epilogue
316 statements; it consumes the six D accumulators of phase 26
(%r18794 %r18795 %r18804 %r18805 %r18834 %r18835 %r18844 %r18845 — the
s15996…s15988 mma results) and writes the surface.
(a) staging, s15989–s16050. 12 guarded st.shared.u32 of the phase-26 D
pairs into a shared scratch, indexed from %r540 = laneid>>2, %r541 = laneid&3
and %tid.z:
s16000 L38488: st.shared.u32 [%r542], %r18794
s16004 L38493: st.shared.u32 [%r542+16], %r18804
s16017 L38507: st.shared.u32 [%r544], %r18795
s16019 L38510: st.shared.u32 [%r544+16], %r18805
(b) coordinates and bounds check, s16051–s16084.
s16059 L38555: mov.u32 %r18897, %tid.x
s16060 L38556: add.s32 %r18898, %r19003, %r18897 // %r19003 = tid.z*32
s16065 L38561: sub.s32 %r547, %r18898, %r18902
s16067 L38563: add.s32 %r549, %r1, %r547 // x
s16068 L38564: add.s32 %r550, %r19004, %r548 // y, %r19004 = ctaid.y*8 - origin
s16073 L38569: ld.param.v2.u32 {%r18912, %r18913}, [%rd458]
s16078 L38574: @%p480 bra $L__BB1_534
Out-of-range lanes jump straight to $L__BB1_534 (s16303) and skip (c)…(g).
(c) row max over 5 shared values, s16085–s16100.
s16085 L38582: ld.shared.u32 %r18915, [%r18964]
s16087 L38584: ld.shared.u32 %r18916, [%r18964+4]
s16091 L38588: ld.shared.u32 %r18936, [%r18964+16]
s16093 L38591: max.f16x2 %r18914,%r18915,%r18916
s16097 L38604: max.f16 %rs5266,%rs5267,%rs5268
s16099 L38611: mov.b32 %r18937, {%rs5269, %rs5269}
s16100 L38613: sub.f16x2 %r18923,%r18915,%r18937
Five u32 (= 10 f16) are read per row and reduced in registers — no warp
shuffle — giving the row max, which is subtracted.
(d) softmax, s16101–s16168. Textbook f32 softmax over the row's 5 values:
s16103 L38621: mul.ftz.f32 %f1033, %f993, 0f3FB8AA3B // * log2(e)
s16104 L38622: ex2.approx.ftz.f32 %f995, %f1033
s16160 L38741: mov.f32 %f1032, 0f3F800000
s16161 L38742: div.approx.ftz.f32 %f1011, %f1032, %f1050
s16164 L38749: mul.f16x2 %r18938,%r18939,%r18952
y = 2^((x−max)·log2e), p = y · (1/Σy) in f16, five f16x2 registers
(%r18938 %r18941 %r18944 %r18947 %r18950).
(e) texture gather, s16169–s16266. A 3×3 stencil of a colour texture:
s16184 L38789: cvt.rn.f32.s32 %f1051, %r18968
s16185 L38790: ld.param.v2.f32 {%f1052, %f1053}, [%rd459+136]
s16187 L38792: fma.rn.ftz.f32 %f1012, %f1052, %f1051, %f1056
s16191 L38796: ld.param.u64 %rd446, [%rd459+88]
s16193 L38799: tex.base.2d.v4.f16.f32 {%rs5294,%rs5295,%rs5296,%rs5297}, [%rd430, {%f1012,%f1013}]
s16195 L38803: fma.rn.f16 %rs5298,%rs5294,%rs5308,%rs5291
%rd459 = param_0 (s16079), so the texture handle is param_0+88, the
scale/offset are param_0+136 (two f32: f1052·(k−0.5·?)) and the
accumulator weights come from the softmax registers. Nine
tex.base.2d.v4.f16.f32 are issued (s16193, 16205, 16217, 16229, 16234, 16240,
16252, 16258, 16263) for the 3×3 neighbourhood (clamped coordinates at
s16172–s16183 compute (x−1, x, x+1) and (y−1, y, y+1)), accumulated with
fma.rn.f16 into 3 channels.
(f) two integer-coordinate textures, s16267–s16270.
s16267 L38971: ld.param.u64 %rd448, [%rd459+96]
s16268 L38973: tex.base.2d.v4.f16.s32 {%rs5438,%rs5439,%rs5440,%rs5441}, [%rd448, {%r549,%r550}]
s16269 L38975: ld.param.u64 %rd450, [%rd459+104]
s16270 L38977: tex.base.2d.v4.f16.s32 {%rs5442,%rs5443,%rs5444,%rs5445}, [%rd450, {%r549,%r550}]
These are the +96 / +104 texture reads the brief asks about — handles at
param_0+96 and param_0+104, sampled at the integer pixel coordinate
(%r549, %r550), returning two 4-channel f16 vectors.
(g) combine, sigmoid gate, surface store, s16271–s16304.
s16271 L38980: cvt.f32.f16 %f1030, %rs5446
s16272 L38983: mul.ftz.f32 %f1063, %f1030, 0fBFB8AA3B
s16273 L38984: ex2.approx.ftz.f32 %f1064, %f1063
s16274 L38985: add.ftz.f32 %f1065, %f1064, 0f3F800000
s16275 L38986: div.approx.ftz.f32 %f1031, %f1032, %f1065 // 1/(1+2^(-log2e·v))
s16278 L38996: sub.f16 %rs5449,%rs5448,%rs5447 // 1 - sigmoid
s16279 L39000: mul.f16 %rs5452,%rs5426,%rs5447 // acc * sigmoid
s16280 L39004: fma.rn.f16 %rs5495,%rs5438,%rs5449,%rs5452 // + tex96 * (1-sig)
s16285 L39024: set.nan.f16.f16 %rs5473,%rs5442,%rs5442 // + tex104 unless NaN
s16302 L39065: sust.b.2d.v4.b16.zero [%rd453, {%r18983,%r550}], {%rs5495,%rs5496,%rs5497,%rs5494}
%rd453 = param_0+112 (s16090). The store address column is %r549·8 (s16300
shl.b32 %r18983, %r549, 3), the row %r550; the fourth channel is the zero
literal 0x0000 (s16301 mov.u16 %rs5494, 0), so this is a 3-channel write
into a 4-channel surface. The set.nan/bra triad (s16285–s16298) adds the
+104 read only when it is not NaN.
Inputs: the 6 returned D registers, param_0+{48,88,96,104,112,136}, the
shared scratch, constants 0f3FB8AA3B, 0fBFB8AA3B, 0f3F800000.
Outputs: none (the kernel returns, s16304).
4. Texture reads, surface writes, and the +24 input
| what | where |
|---|---|
param_0+8 — input feature plane arena, read by phase 1's A loads |
prologue s67…s324 (52 ld.global.u32) |
param_0+24 — the patch-gather source E1 reads |
s2215 (ld.param.u64 %rd149, [%rd1+-56], %rd1 = param_0+80) and the 24 ld.global.v2.u16 at s2371…s5577 |
param_0+48 — the plane arena E25 scatters phase 25's output to |
s15754 / s15755, 16 st.global.u32 at s15780…s15959 |
param_0+88 — the colour texture handle |
s16191, 9 tex.base.2d.v4.f16.f32 at s16193…s16263 |
param_0+96 — texture handle #2 (integer coords) |
s16267/s16268 |
param_0+104 — texture handle #3 (integer coords) |
s16269/s16270 |
param_0+112 — the output surface |
s16090, sust.b.2d.v4.b16.zero s16302 |
param_0+136 — two f32 texture coordinate scales |
s16185 |
The skip input at +24 supplies the patch-gather source. E1 reads it once
per lane per stencil position and never writes it; the buffer is addressed in
elements of 4 bytes (one f16x2 = 2 channels) with index
clamp(row)·8·ex + clamp(col)·8 + clamp(t,0,7) (s2245/s2250, L4674/L4679). It
is the input the patch expand tile is built from — the same operand the
window-1 patch is expanded out of.
The surface writes are param_0+112 (E26, s16302), a 4-channel b16 surface
written with 3 live channels. The plane-arena writes are E25's 16
st.global.u32 to param_0+48 (s15780…s15959).
Nothing reads param_0+48 back in this entry: param_0+48 is the source of
exactly one ld.param (s15754) and never the base of a load. Likewise the
texture handles at +88/+96/+104 are only ever tex.* operands.
5. The phase table, M/N/K checked
rr_layer_spec.py dec1's phase table with the arithmetic M/16 · N/8 · K/32 == mma evaluated on every row (the brief's check):
| # | stmts | M | N | K | mma | M/16·N/8·K/32 | holds? | weight bytes (rel. W) |
bias bytes |
|---|---|---|---|---|---|---|---|---|---|
| 1 | s403–s546 | 144 | 384 | 96 | 144 | 9·48·3 = 1296 | no | 0…11788 (+12288·tid.z) |
24576…24816 (+256·tid.z) |
| 2 | s7096–s7119 | 96 | 32 | 32 | 24 | 6·4·1 = 24 | yes | 25216…25740 (+18432·tid.z) |
– |
| 3 | s7127–s7150 | 96 | 32 | 32 | 24 | 24 | yes | 27264…27788 (+18432·tid.z) |
– |
| 4 | s7158–s7181 | 96 | 32 | 32 | 24 | 24 | yes | 26240…26764 (+18432·tid.z) |
– |
| 5 | s7189–s7212 | 96 | 32 | 32 | 24 | 24 | yes | 28288…28812 (+18432·tid.z) |
– |
| 6 | s7384–s7431 | 64 | 96 | 32 | 48 | 4·12·1 = 48 | yes | none | 29312…41100 (+18432·tid.z) |
| 7 | s11275–s11322 | 192 | 96 | 96 | 48 | 12·12·3 = 432 | no | none | – |
| 8 | s11506–s11521 | 32 | 64 | 32 | 16 | 2·8·1 = 16 | yes | 41600…43148 | – |
| 9 | s11538–s11553 | 32 | 64 | 32 | 16 | 16 | yes | 60032…61580 | – |
| 10 | s12397–s12412 | 64 | 64 | 64 | 16 | 4·8·2 = 64 | no | 62336…63884 | 64384…64432 |
| 11 | s12834–s12849 | 32 | 64 | 32 | 16 | 16 | yes | 64448…65996 | – |
| 12 | s12918–s12933 | 64 | 64 | 64 | 16 | 64 | no | 66624…68172 | 68672…68720 |
| 13 | s13242–s13257 | 32 | 64 | 32 | 16 | 16 | yes | 68736…70284 | – |
| 14 | s13326–s13341 | 64 | 64 | 64 | 16 | 64 | no | 70784…72332 | 72832…72880 |
| 15 | s13650–s13665 | 32 | 64 | 32 | 16 | 16 | yes | 72896…74444 | – |
| 16 | s13734–s13749 | 64 | 64 | 64 | 16 | 64 | no | 74944…76492 | 76992…77040 |
| 17 | s14058–s14073 | 32 | 64 | 32 | 16 | 16 | yes | 77056…78604 | – |
| 18 | s14142–s14157 | 64 | 64 | 64 | 16 | 64 | no | 79104…80652 | 81152…81200 |
| 19 | s14466–s14481 | 32 | 64 | 32 | 16 | 16 | yes | 81216…82764 | – |
| 20 | s14550–s14565 | 64 | 64 | 64 | 16 | 64 | no | 83264…84812 | 85312…85360 |
| 21 | s14874–s14889 | 32 | 64 | 32 | 16 | 16 | yes | 85376…86924 | – |
| 22 | s14958–s14973 | 64 | 64 | 64 | 16 | 64 | no | 87424…88972 | 89472…89520 |
| 23 | s15282–s15297 | 32 | 64 | 32 | 16 | 16 | yes | 89536…91084 | – |
| 24 | s15366–s15381 | 64 | 64 | 64 | 16 | 64 | no | 91584…93132 | 93632…93680 |
| 25 | s15690–s15705 | 32 | 64 | 32 | 16 | 16 | yes | 93696…95244 | – |
| 26 | s15981–s15988 | 64 | 32 | 64 | 8 | 4·4·2 = 32 | no | 95744…96268 | 96768…96784 |
Every phase whose M/16·N/8·K/32 does not equal mma is one the tool also
marks grid = band, and every phase that holds is marked full. The
mismatching rows are not off by a constant factor (P1 is ×9, P7 ×9, P10/12/…
×4, P26 ×4), so M, N and K cannot all be taken at face value for those
rows: nA counts distinct A register quads, which can be either distinct
M-tiles or distinct K-slices of the same tile, and the tool cannot tell them
apart. See §7/U1.
Which phases read no weight operand: P6 and P7. Both take B from
registers that the preceding epilogue packed (E5 packs P2/P3's D for P6's A and
B; E6 packs the probabilities and the transposed P5 for P7's A and B). Their
bo is None for every mma.
Weight image extent as the tool measures it: the largest resolved offset is
96784 (s15980, 4 bytes wide) ⇒ 96788 bytes (634 distinct slots). This
excludes the two tid.z-strided groups in §1.1: phase 1's B and bias are read
at W + 12288·tid.z / W + 256·tid.z, and phases 2…5's B and phase 6's bias at
W + 18432·tid.z + 25216. With tid.z ∈ {0,1} (E26 rejects tid.z > 1 at
s16069 setp.gt.u32 %p474, %r19008, 1) the true image must reach at least
18432 + 41100 = 59532, which is inside 96788, so the two tid.z slices do not
extend the image beyond what the tool already sees. (INFERENCE — the tool
drops the tid.z term, so the per-window base cannot be read off it directly.)
6. The plane arena (param_0 + 48) and the per-launch tables
param_0+8is read 52 times in the prologue (s67…s324) as the input feature plane that builds phase 1's 9 A fragments. The index arithmetic is(clamp_r·ey + clamp_c)·4style (shl.b32 … 3,mad.lo.s32,mul.wide.u32 …, 4,add.s64 …, %rd35): the arena is a{ex, ey}-wide f16/byte plane read with 4-byte elements.param_0+24is the patch-gather source (see §4); it is read, never written.param_0+48is the output plane arena: written by E25's 16st.global.u32(s15780…s15959), never read. There is no secondst.*group in the entry (the entry has exactly 16st.globaland 0 other global stores before L38179).- The only other
ld.paramsites are the 4 in the prologue (§1.1) and the 5 in E26 ([%rd459+88/96/104/112/136], s16191/16267/16269/16090/16185) plus[%rd8+48](s15754) and the twoparam_0+40re-reads (s15961/s15962).
There is no per-launch table generated into the plane arena by this kernel:
the plane arena is only written (E25) and the tables that look like per-launch
tables (the W + ((laneid<<2)&12) + off blocks at 25088, 62080, 66496) all live
in the weight image, not the arena. See §7/U5.
7. Uncharacterised
U1. The true M/N/K of the nine banded phases (P1, P7, P10, P12, …, P24,
P26). For those rows M/16·N/8·K/32 ≠ mma (§5). The reason is structural
and checkable: the accumulator chains of a banded phase mix distinct B
operands. For P10 the four chains are
s12397 D=%r11522 A=%r11574 B=%r11473 C=bias (bo 62336)
s12399 D=%r11542 A=%r11594 B=%r11477 C=%r11522 (bo 62848)
s12401 D=%r11562 A=%r11574 B=%r11481 C=bias (bo 63360)
s12403 D=%r11582 A=%r11594 B=%r11485 C=%r11562 (bo 63872)
— the same D accumulates products with B fragments from four different
512-byte blocks and two different A quads. Under the settled weight formula
(n = 16·tt + 8·(b>>3) + (L>>2), verified on enc0) those four blocks span
n = 0…63, so N = 64 is supported; but then the two-mma chains must be
summing over M or K, and neither M nor K is separately readable from the
fragment structure. For P1 the same argument gives N = 384 (48 B fragments =
24 blocks × 16 columns) while the arithmetic needs M/16 · K/32 = 3.
Missing: either a run with a weight-image dump, or the _prep permutation
that produces the image, or the high-level layer definition. Until then I will
not assign M/K to these rows.
U2. The identity of the 12288-byte score bias (P6's C). E5 loads it as 24
tiles × 512 bytes at W + 18432·tid.z + 29312. Whether it is a relative
position bias, a learned score bias, or a per-lane constant cannot be decided
from the PTX: it is an opaque blob in a permuted weight image. Missing: the
image's layout description (the _prep permutation) or the model's parameter
names.
U3. The identity of the per-column tables. W+25088…25200 (E1 gain),
W+62080…62192 (E7), W+62208…62320 (E9 gain), W+66496…66608 (E10) are all
read with the same W + ((laneid<<2)&12) + off pattern and are 8 words per lane.
Their role is computable (E1/E9: multiply the inverse RMS; E7/E10: added to a
residual before the next GEMM) but their names are not. The GEMM biases at
+24576, +64384, +68672, +72832, +76992, +81152, +85312, +89472,
+93632, +96768 are the same case.
U4. E1's patch-expand geometry. The 48 st.shared of §3/E1 are guarded by
row/col limits of 9 and index (row·40 + col·8 + t)·4 into four 1600-byte
sub-arrays, but they are emitted as ~400 basic blocks with 438 labels and I could
not reduce the lane→(row,col) map of the 10×10 patch to a closed form. What is
established: the four sub-arrays, the 40-word row pitch, the t and t|4
column split, the two v2.u16 cells, and the total 6400 bytes.
Missing: the source-level im2col/patchify expression, or a shared-memory dump.
U5. Whether param_0+8 is an input plane or a scratch table. The prologue
reads it 52 times with 4-byte elements and never writes it, and E1 does not read
it at all (E1 reads param_0+24). So +8 and +24 are two different input
buffers; which one holds the noisy colour plane and which holds the feature
planes cannot be decided from the PTX. Missing: the host-side launch
arguments.
U6. The tid.z strides. Two different per-tid.z strides (12288 for phase
1, 18432 for phases 2…5 + phase 6's bias, 256 for phase 1's bias, 192 for
phase 1's bias in the companion layout) appear, and no later phase carries one.
Whether this means the weight image is a concatenation of per-window sub-images
or that tid.z selects a batch element whose weights are interleaved cannot be
decided from the PTX alone. Consequence: the "96788 bytes" of §5 is a lower
bound for the image, not necessarily its size.
U7. The logical meaning of E26's final expression. The tail computes
out = acc·σ(v) + tex96·(1−σ(v)) + tex104 on the three channels gathered from
the 3×3 colour stencil and the two integer-coordinate textures. The PTX gives
the arithmetic exactly (§3/E26(g)) but not what model term it corresponds to.
Missing: the high-level source or a reference implementation.
U8. E12/E14/…/E24's %f989…%f992. These six epilogues re-cvt the four
constants but never re-mov.f32 them, so they read the registers E10 wrote
(s12414–s12425). That is only valid if the register allocator kept them live
across phases 11…23; the PTX shows no redefinition, so it is consistent, but
nothing in the file asserts the interference-freedom. Missing: an SSA check.
8. Quick acceptance index
| requirement | where |
|---|---|
| every epilogue named and ranged | §2, §3 (E1…E26) |
| phase-1 epilogue's patch expand + norm | §3/E1 |
the norm (E1, E9) |
§3/E1, §3/E9 — raw sum of squares + 2⁻¹³, no mean, no 1/N |
| the attention (score, softmax, P·V) | §3/E5, E6, E7; §3/E9 (region diagram) |
| the activations | §3/E10 (materialises %f989…%f992), E12, E14, E16, E18, E20, E22, E24 |
| the patch expand | §3/E1 (48 st.shared), §7/U4 |
| the final epilogue, textures at +96/+104, surface | §3/E26, §4 |
| the texture reads at +96/+104 | §3/E26(f), s16268 / s16270 |
| the skip input at +24 | §3/E1 (s2215), §4 |
| the plane-arena writes | §3/E25 (s15780…s15959), §4, §6 |
| the phase table with M/N/K checked | §5 |
| weight image extent | §5 (96788 bytes, 634 slots) |
| which phases read no weight | §5 (P6, P7) |
| uncharacterised | §7 |