Skip to content

feat(KERNEL-QUANT-CIQ-GEMM-ROCM): keep-quant GEMM providers on kROCM - #1

Open
ghazni101 wants to merge 3 commits into
mainfrom
row/KERNEL-QUANT-CIQ-GEMM-ROCM
Open

feat(KERNEL-QUANT-CIQ-GEMM-ROCM): keep-quant GEMM providers on kROCM#1
ghazni101 wants to merge 3 commits into
mainfrom
row/KERNEL-QUANT-CIQ-GEMM-ROCM

Conversation

@ghazni101

Copy link
Copy Markdown
Owner

What changed

W1 of KERNEL-QUANT-CIQ-GEMM-ROCM (mudler#1587): the kROCM providers for
OpId::kMatmulBTQuant and OpId::kMatmulBTQuantGrouped, in the new
src/vt/rocm/rocm_quant_dot.hip, plus the committed spec
(.agents/specs/kernel-quant-ciq-gemm-rocm.md) and a red-first gate
(tests/vt/test_rocm_quant_dot.cpp).

Registering the providers flips GgufQuantComputeAvailable() true on
ROCm, so every GGUF k-quant model now computes on the AMD GPU through
the existing seams (loader policy -> vt::MatmulBT[Quant] ->
GetOp(kROCM)) with zero model-path edits. Towers stage once through
the existing ResidentWeight path.

Coverage mirrors the CUDA sibling exactly: ten Q8_K-family encodings
(Q2_K, Q3_K, Q4_K, Q5_K, Q6_K, IQ2_XXS, IQ3_XXS, IQ2_S, IQ1_S,
IQ1_XXXS) plus a native Q8_0 arm. The IQ codebooks reuse
cuda_quant_iq_tables.cuh as the single source — pure __device__
syntax, no CUDA — so no transcription drift is possible.

Why

The ROCm backend had no quantized-weight GEMM provider: every GGUF
k-quant weight on an AMD card computed off device (bf16 expansion at
load). AGENTS.md makes quantized arms a standing requirement. Upstream
pins an RDNA3 W4A16 family in csrc/rocm (gptq_gemm_rdna3 gated
VLLM_ROCM_GFX1100) but nothing in this tree can reach it yet — no
AWQ/GPTQ consumer exists — so that family stays under ## Owed in the
spec (W2), per the reachability rule.

HIP delta that shaped the implementation (measured on gfx1100 /

ROCm 7.14.0)

gfx1100 exposes NO signed byte dot: __dp4a does not exist;
__builtin_amdgcn_sdot4 needs target feature dot1-insts, which clang
rejects for this target ("not a recognized feature"); the one hardware
dot that assembles (v_dot4_i32_iu8) is UNSIGNEDxUNSIGNED and computes
wrong over signed Q8 activations (verified by device experiment:
byte 0xFD contributed +253x, not -3x). The integer dots therefore ship
as portable scalar loops in the CPU reference's exact accumulation
order — which is what makes the bit-exactness gate achievable. The ISA
dots are recorded as W2 levers with their own exactness arguments.
__shfl_down_sync also requires a 64-bit mask on this target.

Unsupported dtypes (Q4_0/MXFP4, Q8_0-activation) THROW naming the dtype
instead of falling back to a host kernel that cannot follow device
pointers on a discrete card; VT_GGUF_KEEP_QUANT=0 restores load-time
bf16 expansion.

How to verify

On gfx1100 / ROCm 7.14.0 (container rocm-dev:7.14.0):

cmake -B build-hip -S . -GNinja -DCMAKE_BUILD_TYPE=Release \
  -DVLLM_CPP_HIP=ON \
  -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath,/opt/rocm/lib -Wl,-rpath-link,/opt/rocm/lib" \
  -DCMAKE_SHARED_LINKER_FLAGS="-Wl,-rpath,/opt/rocm/lib -Wl,-rpath-link,/opt/rocm/lib"
cmake --build build-hip -j 8
ctest --test-dir build-hip -R 'test_rocm_quant_dot|test_gguf_keep_quant|test_ops_quant_dot'
VT_GGUF_KEEP_QUANT=1 ./build-hip/examples/vllm-cli \
  --model <qwen3.5 gguf> --device auto --temperature 0 \
  --max-tokens 64 --prompt "The capital of France is"

Measured on this tree:

What remains unverified or out of scope

  • No fresh independent review yet: this was implemented in-session under
    a recorded deviation (subagent runtime had no model configured). A
    fresh review pass against the spec is still owed before merge.
  • Performance vs the CPU tier is parity-at-these-shapes, not a claimed
    win; the unsigned-dot/dot2 ISA levers are W2 with their own exactness
    arguments.
  • W2 upstream RDNA3 family port + AWQ/GPTQ loader consumer stays owed in
    the spec's ## Owed.
  • docs/ENVIRONMENT.md keep-quant row should gain the ROCm throw caveat;
    riding the follow-up rather than widening this PR.

Closes mudler#1587

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true

AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]

tbrasser and others added 3 commits August 21, 2026 12:54
…tion call site (mudler#1273)

Closes the "Owed" item from mudler#1065: CheckKvCacheShape's production call site in GPUModelRunner::initialize_kv_cache now has a test. Building the runner with a non-multiple-of-16 block size (kBlockSize = 8) asserts the throw at construction, from the FLASH_ATTN backend's own get_kv_cache_shape — the executable statement of the contract the server's --block-size validation and the bench rounding exist to prevent at the entry points.

Validated on gfx1151 (Strix Halo, ROCm 7.2.3) as part of the mudler#41 M3 battery: test_runner 20/20 cases, 544 assertions, on the current-main build with mudler#1056 + mudler#1065 merged.

Issue: mudler#41

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: DeepSeekV4:Flash [Freebuff]
The ROCm backend registers no quantized-weight GEMM provider, so every
GGUF k-quant weight on an AMD card computes off device. The spec scopes
W1 as kMatmulBTQuant and kMatmulBTQuantGrouped providers mirroring the
CUDA sibling, which the GGUF loader reaches automatically once they are
registered, and owes the upstream csrc/rocm W4A16 family behind a loader
consumer that does not exist yet. The issue index gains the three
2026-08-21 campaign issues: mudler#1586 toolchain adoption and optimization,
mudler#1587 this row, mudler#1588 the Qwen3.5-0.8B numerics characterization.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
… kROCM

The GGUF loader routes a block-typed weight to MatmulBTQuant whenever the
running device has the provider, so registering these two ops lights up
keep-quant compute on every ROCm board with no model-path change: the
dense and grouped MoE towers stage once through ResidentWeight and
dispatch to the new device GEMM.

Coverage mirrors the CUDA sibling exactly — the ten Q8_K-family
encodings plus a native Q8_0 arm. The integer dots are the portable
scalar forms of the CPU reference bodies in the CPU accumulation order,
because gfx1100 exposes no signed byte dot (v_dot4_i32_iu8 is
unsigned-only; sdot4 needs a feature this target does not offer), and
the gate is bit-exactness against the CPU tier at NMSE 1e-6 with the f64
dequant band at 5e-4. Unsupported dtypes throw naming the dtype instead
of silently falling back to a host kernel that cannot follow device
pointers; VT_GGUF_KEEP_QUANT=0 restores load-time expansion.

Gates on gfx1100 / ROCm 7.14.0: test_rocm_quant_dot 132,094 assertions
green across all ten encodings (decode through prefill shapes, broadcast
and per-row grouped arms over a poisoned output buffer), focused
ctest 'rocm|cross_device|quant' 20/21 with only the pre-existing
MoeSiluMul bf16 exactness failure (mudler#1588) remaining, and an end-to-end
Qwen3.5-0.8B Q4_K_M decode that is deterministic on device.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port the upstream RDNA3 quantized-GEMM family from csrc/rocm

2 participants