[MLIR][XeGPU] Add MXFP fp8 WG GEMM tests and use shared input set (#225944)
Adds four workgroup-level tests, updates two and renames one, so that
seven tests covering bf16, mx-fp4 and mx-fp8 exercise the same
arithmetic on the same operand values and check against the same
expected result.
1. simple_mxfp_gemm -> simple_mxfp_gemm_F4 - renamed, for consistency
with the F4/F8 suffixes used by the quantize and dequantize tests
2. (new) simple_gemm_bf16 - non-quantized bf16 GEMM at the MXFP problem
size, as the unquantized comparison point, and where the shared input
set is documented
3. (new) simple_mxfp_gemm_F8 - f8E5M2 operands with f8E8M0FNU scales
4. (new) simple_mxfp_gemm_dequantizeB_F8
5. (new) simple_mxfp_gemm_quantizeA_F8 - fp8 counterparts of the
existing fp4 tests
6. simple_mxfp_gemm_dequantizeB_F4 and simple_mxfp_gemm_quantizeA_F4 are
updated in place.
What the update changes:
* Meaningful operand values. The tests previously used all-ones
operands, which cannot distinguish a correct kernel from one that swaps
the two fp4 values in a byte, misapplies a scale, or mixes up lanes or
tiles. Operands are now drawn from the 8 magnitudes e2m1 can represent -
0, 0.5, 1, 1.5, 2, 3, 4, 6 - indexed by (i + k) % 8 and (j + k) % 8, so
consecutive K values differ and the packing is exercised. Every value is
exact in f4E2M1FN, f8E5M2, bf16 and f32, which is what lets one input
set serve all three formats.
* Non-unit block scales. A carries a per-K-block divisor and B a
per-K-block scale, three of each, all powers of two, cycling per block
of 32. The quantized formats realise the same values through their own
scales, so a scale that is ignored or applied to the wrong block now
changes the result.
* One reference result. Each test computes its expected C with a host
f32 GEMM over f32 shadows of the operands, filled in the same loop that
writes the device operands so the two cannot drift. Every product is a
multiple of 0.25 and the largest result is well under 2^24, so the
accumulation is exact and the comparison is bit-for-bit rather than
approximate.
* A labelled result line. verifyMemRefF32's mismatch count is printed as
"mismatches: <n>" via printString/printI64/printNewline and matched with
{{^mismatches: 0$}}. A bare "0" check is a substring match over all of
stdout, so it passes on any 0 the runtime happens to print, including
one inside a larger number, and cannot distinguish a correct run from a
kernel that never ran.
* A fix for the fp4 A operand in simple_mxfp_gemm_F4.
gpu.alloc/memcpy/dealloc size a memref by rounding the element type up
to a whole byte - the size is lowered as gep(null, numElements) on i4,
which LLVM strides by 1 byte - so a packed fp4 memref is sized at twice
its real footprint. The test allocated A as memref<256x4096xf4E2M1FN>,
giving a 512 KB overread of a 524288-byte host buffer; because
mgpuMemcpy is asynchronous the fault surfaces later, at the stream sync
inside the first mgpuMemFree, so the test passed regardless. A now
crosses as memref<524288xi8> and is viewed as fp4 on the device, which
is what the B operand already does.
All seven follow the existing convention in this directory: the mlir-opt
compile is the active RUN line and the mlir-runner pipeline is kept
behind RUN-DISABLED with a MISMATCH check prefix, so no device is needed
and the compile-only run cannot pick up the result check.