Repository navigation
Turing runs m8n8k4 with F = 23 (not 24); kind::f8f6f4 reads E2M1's low padding bits as mantissa (RTX 2060 / RTX 5070 Ti measurements) #19
Description
Thanks for MMA-Sim. We have been checking it against our own GPUs with an independent harness, and found two places where hardware and MMA-Sim (commit 54294d4) disagree. Neither instruction has a hardware kernel in mmasim-kernels, which is probably why they slipped through. Everything else we measured matched bit for bit. Details and minimal cases below.
Setup. Our harness is a raw-bit mma.sync kernel written independently of mmasim-kernels: one independent tile per warp (per quad-pair for m8n8k4), with fragment layouts from the PTX ISA. Inputs:
- half of the tiles are uniform random words;
- half have operands within a few binades of 1, so that every fractional bit of the fused sum shows;
- the bits a format does not use (the low 13 bits of TF32, the padding around FP4/FP6) are random on purpose;
- a set of directed cases.
GPUs and software:
- RTX 2060 (sm_75), driver 610.62;
- RTX 5070 Ti (GB203, sm_120a), driver 616.64;
- A100-PCIE-40GB (sm_80), driver 580.178.04;
- CUDA 12.9.
1. Turing executes Volta's m8n8k4 with F = 23, not 24
MMA("Turing", "m8n8k4.*") applies Turing's parameters (F = 24; e_zero −21 / −132). On an RTX 2060 the native HMMA.884 (it is native: cuobjdump -sass shows HMMA.884.F32.F32.STEP0..3) behaves like Volta's sum width.
Statistics:
- Against MMA-Sim:
m8n8k4.f32.f16.f16.f32differs in 49,758 of 262,784 results, andm8n8k4.f32.f16.f16.f16in 47,429 of 262,720. All differences are ±1 ulp and spread evenly over quad-pairs, rows and columns. - With F = 23: a T-FDPA with L = 4, F = 23 and RZ gives 0 mismatches on all three
m8n8k4variants. No other F in 22..26, and no L = 2 chaining, does. - Turing's own instructions agree:
m16n8k8(f32 and f16 accumulators) matches MMA-Sim exactly, with 0 mismatches in about 1.05 M results.
Minimal case, the discrepancy example from your Table 6:
- inputs: a = (−2^13, −0.5, −0.25, −0.125) f16, b = (2^10, 1, 1, 1) f16, c = 2^23 f32,
m8n8k4.f32.f16.f16.f32; - MMA-Sim (Turing):
0xbf000000(−0.5); - RTX 2060:
0x0, Volta's answer.
Zero accumulator with C in f16. We probed single products a·b with C = +0 (f16) and product exponents from −14 to −28. The significand 2047·1025 ends in binary 1111, so any bits lost in alignment would show. No bits are lost at any exponent. So with F = 23, every e_zero ≤ −25 is consistent: a zero f16 C does not affect the alignment. MMA-Sim's −21 truncates tiny products. Example, m8n8k4.f32.f16.f16.f16, c = +0 (f16):
| a (f16) | b (f16) | MMA-Sim (Turing) | RTX 2060 |
|---|---|---|---|
0x07ff |
0x0401 |
0x32000fe0 |
0x32000ffc (exact) |
0x0bff |
0x0401 |
0x32800ff0 |
0x32800ffc (exact) |
Ampere, for comparison. On sm_80, ptxas lowers m8n8k4 to FFMA/FADD/FMUL with no HMMA at all. An A100 then computes (((p0+p1)+p2)+p3)+c in f32 with round-to-nearest-even: 0 mismatches on 233,452 finite f32-output results. So m8n8k4 is a tensor-core instruction only on Volta and Turing. That matches MMA-Sim not listing it for Ampere, but it may be worth a note. The eight Ampere mma.sync instructions MMA-Sim models match on the A100 with 0 mismatches.
2. kind::f8f6f4 reads the low padding bits of an E2M1 byte as mantissa bits
The PTX ISA says only that ".e2m1 resides in central 4 bits of the 8-bit container with padding in the upper 2 bits and lower 2 bits of the container". It does not say whether the padding is ignored. MMA-Sim's decode_uint8_to_fp6_fp4 drops bits 1:0. On an RTX 5070 Ti, the byte is read as E2M3 in bits 5:0: bits 1:0 act as two more mantissa bits, and bits 7:6 are ignored. With zero padding both readings agree, which is presumably the common case.
One-hot probe with m16n8k32.f32.e2m1.e4m3.f32 (mma.sync.aligned.m16n8k32.row.col.kind::f8f6f4): one non-zero byte in A, B = 1.0 (e4m3 0x38), C = 0. 512 tiles per row of the table, every (row, k) position:
| A byte | MMA-Sim | RTX 5070 Ti | reading as E2M3 |
|---|---|---|---|
0x08 |
1.0 | 1.0 (512/512 positions) | 1.0 |
0x02 |
0.0 | 0.25 | 0.25 |
0xC9 |
1.0 | 1.125 | 1.125 |
0xF2 |
−2.0 | −2.5 | −2.5 |
With E2M1 operands decoded as E2M3 from bits 5:0 and everything else as in your T-FDPA (F = 25, L = 32), we get 0 mismatches on all 18 kind::f8f6f4 instructions involving e2m1, f32 and f16 accumulators. That is 14.2 M results across two runs with different seeds, random padding included. On the 10 of these instructions that MMA-Sim models, its current decoding mismatches 58–96 % of the results of the first run.
3. A data point for the e3m2 / e2m3 TODO
For kind::f8f6f4 with e3m2 and e2m3 (in bits 5:0, bits 7:6 random), we used T-FDPA with F = 25 and L = 32, as for the other RTX Blackwell 8-bit formats. It gives 0 mismatches on all 24 combinations without e2m1 (e3m2/e2m3 with each other and with e4m3/e5m2, f32 and f16 accumulators), 18.9 M results over two runs. So adding them to rtx_blackwell_mma_shape_and_type with the existing model looks safe.
Happy to share the raw inputs and outputs (case files, GPU words and SHA-256) or to run specific probes on these GPUs if useful.
Activity
PabloSoage commentedon Oct 20, 2026
A follow-up on point 2: the same E2M1 reading applies to the block-scaled instruction
kind::mxf8f6f4.block_scaleon the same RTX 5070 Ti (driver 616.64, CUDA 12.9,sm_120a).Setup. As before, our harness is independent of
mmasim-kernels:scale-a-data/scale-b-dataword and the four selectors are passed raw, per tile;Results with E2M1 padding. With E2M1 decoded as E2M3 from bits 5:0 and everything else as in your ST-FDPA (F = 25, L = 32, RZ, e_zero = −133), all 5
mxf8f6f4instructions with an e2m1 operand give 0 mismatches. With MMA-Sim's current decoding (decode_uint8_to_fp6_fp4, bits 5:2), the same results mismatch:m16n8k32.block32…ue8m0)f32.e2m1.e2m1.f32f32.e2m1.e4m3.f32f32.e4m3.e2m1.f32f32.e2m1.e5m2.f32f32.e5m2.e2m1.f32What is not affected. Our integer re-implementation with your published parameters gives exactly MMA-Sim's bits on all 12 block-scale instructions you model, so these differences come from the E2M1 decoding alone. Against the GPU, MMA-Sim gives 0 mismatches on:
mxf8f6f4;m16n8k64instructions that compile (mxf42X,mxf4nvf42X ue8m0,mxf4nvf44X ue4m3). There,kind::mxf4/mxf4nvf4have no padding: E2M1 is a bare nibble.A few other data points, in case they are useful:
{byte-id, thread-id}and every block, with a distinct value in each lane and byte. SF_A[r][j] is read from lane4·(r mod 8) + 2·thread-id-a + ⌊r/8⌋, bytebyte-id-a + j; SF_B[j][n] from lane4n + thread-id-b, bytebyte-id-b + j. This matches yourLOAD_SFA_*/LOAD_SFB_*macros for selector 0.truncate_e4m3_to_ue4m3: scale0xB8acts as 1.0, and0xFFis NaN like0x7F.kind::mxf4equalskind::mxf4nvf4.scale_vec::2X: on identical inputs the GPU outputs are bit-identical, confirming the comment inmma_sm120a.cu.mxf8f6f4pairs with an FP6 operand match ST-FDPA with F = 25 (0 mismatches, 262,784 results each). This adds to the e3m2/e2m3 TODO data point above.ptxasrejections (CUDA 12.9):mxf4nvf4.block_scale.scale_vec::4X…ue8m0is rejected, although the PTX ISA's table of valid combinations lists it, so we could not measure it.scale_vec::2Xwithue4m3is rejected too, which matches that table. So two entries ofrtx_blackwell_mma_block_scale_shape_and_typecannot be built withmma.syncon this toolchain:m16n8k64.block16…ue8m0andm16n8k64.block32…ue4m3.peichenxie commentedon Oct 21, 2026
Thank you for your feedback! I have added support for these instructions in PR #20 .