Skip to content

feat(ck-tile): multi-ABD GEMM TE to dispatcher bridge - #9305

Merged
ozturkosu merged 12 commits into
developfrom
users/muozturk/ck/bridge_gemm_multi_abd
Jul 23, 2026
Merged

ozturkosu merged 12 commits into
developfrom
users/muozturk/ck/bridge_gemm_multi_abd

Conversation

@ozturkosu

@ozturkosu ozturkosu commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

JIRA ID : AICK-2086

ISSUE ID: #8997

Motivation

The CK Tile dispatcher could already generate and launch regular GEMM through the
TileEngine → Dispatcher bridge, but it had no path for the multi-tensor
gemm_multi_abd op. Multi-ABD is used when a GEMM needs to combine several A and
B operands and fuse several D operands in the epilogue
(E = cde_op(a_op(As) @ b_op(Bs), {Ds})), which is a real Old-TE capability with no
dispatcher equivalent. This PR closes that gap so Python callers can drive multi_abd
through the dispatcher at parity with the legacy Tile Engine version, without touching
C++.

It follows the divergent-ABI pattern established by the grouped bridge (#9000) because
multi_abd needs arrays of A/B/D device pointers, not the single-pointer regular
GEMM ABI. The capability set matches the Old-TE gemm_multi_abd_instance_builder.py
exactly: fp16, rcrr layout, configurable A/B/D tensor counts, and the element-wise
op set {PassThrough, AddScale, MultiDMultiply, MultiDAdd}.

Test Plan

  • Run the CPU-only unit tests (no GPU required):
    python3 -m pytest dispatcher/tests/test_multi_abd_bridge.py -v
  • On-GPU numeric verification through the bridge launch path (gfx942 / MI300X),
    512x512x512 fp16 rcrr, across the default 2/2/2 all-PassThrough config and
    non-PassThrough element-wise ops.
  • Confirm the CI and default config expansions yield the expected kernel counts.

Test Result

  • CPU-only unit tests pass (10 passed).
  • Numeric verification (bridge launch path), 512x512x512 fp16 rcrr:
    • default 2/2/2 all-PassThrough: max_rel = 2.9e-4
    • CDE = MultiDAdd: max_rel = 5.7e-4
    • A-op = MultiDAdd: max_rel = 4.1e-4
    • all far below the fp16 tolerance (2e-2); 0 failed measurements.
  • CI config expands to 16 arch-valid kernels; default_config.json → 8896.
  • standard variant expand_sweep regression clean.
  • clang-format-18 (18.1.8) clean on gemm_multi_abd_ctypes_lib.cpp.
  • Serialized A/B perf-parity vs Old-TE (MI300X / gfx942, fp16 rcrr, 16 stems × 5
    shapes = 80 rows, interleaved, fair 50/100/flush/rotating both sides): at
    parity
    — median gap -0.24%, mean -0.66%, 100% within ±15%, 87.5% within ±5%
    (range [-9.57%, +5.35%]). See the parity comment for details.

Related PRs / references (TileEngine → Dispatcher GEMM bridge series): #8997 (regular GEMM fp16/bf16 all-layout), #9000 (grouped GEMM), #9028 (stream-K), #8887 (fp8/bf8/int8). This PR is a sibling in the same bridge effort tracked across those PRs.

Bridge the multi-tensor gemm_multi_abd op (fp16, rcrr layout, arrays of
A/B/D tensors with per-group element-wise ops) so the Dispatcher generates
and launches it at parity with the legacy Tile Engine version. Mirrors the
gemm_universal (#8997) bridge, using the grouped/divergent-ABI pattern
(#9000) since multi_abd needs array-of-pointer arguments.

- unified_gemm_codegen.py: new GemmVariant.MULTI_ABD (tuple As/Bs/Ds types
  and layouts, three element-wise fns, GemmKernelMultiABD launch, CShuffle
  epilogue); tensor counts + all three ops folded into key_name()/kernel
  name; multi_abd_config search block; global tensor-count re-exports for
  the registry-bypass ctypes lib.
- gemm_multi_abd_ctypes_lib.cpp: new registry-bypass C ABI
  (dispatcher_run_multi_abd) that calls SelectedKernel::launch directly on
  the force-included kernel with GemmMultiABDHostArgs; owns GPU memory.
- gemm_utils.py: multi_abd fields on GemmKernelConfig (byte-exact name),
  4-char layout handling, multi_abd codegen json, variant-aware build
  (own ctypes source, no static lib), MultiABDDispatcherLib + GpuMultiABDRunner,
  expand_sweep variant support.
- gemm_full_benchmark.py / run_one_gemm_kernel.py: gemm_multi_abd variant
  wired into the 3-phase driver and routed to GpuMultiABDRunner by name.
- ctypes CMakeLists: dispatcher_gemm_multi_abd_lib target.

Validated on gfx942 (MI300X): fp16/rcrr default_config kernel codegen (0
failures), ctypes .so compiles + links, all C-ABI symbols exported;
standard/multi_d/multi_abd codegen regression clean; name parity between
GemmKernelConfig.name and the generated header confirmed byte-for-byte.
@ozturkosu ozturkosu changed the title [CK Tile] Bridge gemm_multi_abd to the Dispatcher feat(ck-tile): multi-ABD GEMM TE to dispatcher bridge Jul 11, 2026
@ozturkosu ozturkosu self-assigned this Jul 11, 2026
@therock-pr-bot

therock-pr-bot Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
🌿 Branch Name ✅ Pass —
📝 PR Title/Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

@therock-pr-bot

therock-pr-bot Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

🎉 All checks passed! This PR is ready for review.

…s, valid CI tiles

Round-2 fixes for the gemm_multi_abd TileEngine->Dispatcher bridge:

- B1: verify the BRIDGE launch path numerically. GpuMultiABDRunner now owns a
  numpy reference E = CDE( AB(As) @ BB(Bs), {Ds} ) that mirrors
  ck_tile::reference_gemm_multiple_abd byte-for-byte (A/B groups combined
  element-wise into one matrix, single GEMM, then the CDE op folds the D
  tensors) and reports max_rel on the result; run_one_gemm_kernel routes
  --verify through it. This also uncovered + fixed a real launch bug: the
  GemmMultiABDKernel does NOT derive strides from a 0 sentinel, so the runner
  now passes explicit leading strides for A/B/D/E (get_default_stride
  semantics) instead of 0, which had collapsed the whole output onto row 0.
- B2: drive the element-wise ops + tensor counts. expand_sweep reads a
  multi_abd_config block (lists) and iterates the op/count combos, and
  gemm_full_benchmark exposes --multi-abd-{a,b,cde}-op / --multi-abd-num-{a,b,d}
  CLI overrides, so non-PassThrough kernels are actually generated and verified.
- B3: default_ci_config.json warp tile 32x32x64 -> 32x32x16 (32x32x64 is not in
  the gfx942 fp16 XDL allow-list; CI now yields 16 kernels, was 0).
- N1: comment the fp16_fp16_fp16 vs fp16_fp16_fp32 arch-filter key mismatch in
  ctypes_utils as a latent bridge-wide parity narrowing.
- N2: derive layout + per-group ops from the config object (passed via the
  worker payload) instead of parsing the kernel name with a silent rcrr default.
- Ensure build/examples exists before hipcc -o so the self-contained multi_abd
  .so builds without a prior full CMake configure.

dtype x layout unchanged: exactly fp16 x rcrr (Old-TE parity set).

Validated on gfx942 (MI300X, GPU0): default 2/2/2 PassThrough max_rel=2.9e-4,
CDE=MultiDAdd max_rel=5.7e-4, A-op=MultiDAdd max_rel=4.1e-4 (all << fp16 tol);
CI config yields 16 kernels; standard-variant expand_sweep regression clean.
@ozturkosu

Copy link
Copy Markdown
Contributor Author

Posting the round-2 review fixes here to keep the description focused on what the PR does. Here is what I addressed from review:

  • B1 -- real bridge numeric verification. GpuMultiABDRunner now owns a numpy reference E = CDE( AB(As) @ BB(Bs), {Ds} ) that mirrors ck_tile::reference_gemm_multiple_abd byte-for-byte (A/B groups combined element-wise into one matrix, single GEMM, then the CDE op folds the D tensors) and reports max_rel; --verify now exercises the bridge launch path (dispatcher_run_multi_abd -> SelectedKernel::launch), not just the Old-TE profiler. This uncovered + fixed a real launch bug: the GemmMultiABDKernel does not derive strides from a 0 sentinel, so the runner now passes explicit leading strides for A/B/D/E (get_default_stride semantics); a 0 stride had collapsed the whole output onto row 0.
  • B2 -- element-wise ops + tensor counts are now driven. expand_sweep reads a multi_abd_config block (lists) and iterates op/count combos; the driver adds CLI overrides so non-PassThrough kernels are actually generated and verified.
  • B3 -- CI config yields kernels. default_ci_config.json warp tile 32x32x64 -> 32x32x16 (32x32x64 is not in the gfx942 fp16 XDL allow-list). CI now yields 16 kernels (was 0).
  • N1 comment added flagging the fp16_fp16_fp16 vs fp16_fp16_fp32 arch-filter key mismatch as a latent bridge-wide parity narrowing.
  • N2 layout + per-group ops now derived from the config object (via the worker payload), not parsed from the kernel name with a silent default.

@ozturkosu
ozturkosu marked this pull request as ready for review July 12, 2026 22:34
@ozturkosu
ozturkosu requested a review from a team as a code owner July 12, 2026 22:34
@ozturkosu

Copy link
Copy Markdown
Contributor Author

A/B perf-parity sweep vs Old-TE (MI300X / gfx942)

Ran the full serialized bridge-vs-Old-TE performance parity sweep for multi_abd on a single MI300X (gfx942), apples-to-apples:

  • Both sides identical kernels: the bridge codegens the same ck_tile kernel the Old-TE gemm_multi_abd benchmark builds; same CI kernel set (fp16 / rcrr, 16 stems from the fixed CI config), same TE -mllvm codegen flags on both.
  • Fair timing: warmup=50 / repeat=100 / flush_cache=true / rotating_count=1000 on both sides (the bridge ctypes stream_config was set to match the Old-TE profiler for the measurement).
  • Interleaved A/B: per (stem, shape) the two sides are measured back-to-back, 3 reps alternating order, median taken — so no thermal/clock drift between sides.
  • Shapes: 512³, 1024³, 2048³, 1024×512×256, 4096³.

Result (80 rows = 16 stems × 5 shapes)

metric value
median gap -0.24%
mean gap -0.66%
within ±5% 87.5%
within ±15% 100%
range [-9.57%, +5.35%]

gap% = (bridge_tflops − oldte_tflops) / oldte_tflops × 100. The bridge is at parity with Old-TE — the residual spread is ordinary measurement/thermal noise (both sides run the byte-identical kernel), with no systematic regression in either direction. The largest outliers are on the small 1024×512×256 shape where absolute times are tiny.

Full per-(stem, shape) CSV is attached to the PR working set (parity_multi_abd_MI300X_gfx942_fp16_rcrr_2026-07-12.csv).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the CK Tile “TileEngine → Dispatcher bridge” to support the multi-tensor gemm_multi_abd variant (arrays of A/B/D tensors + fused epilogue), aiming for parity with the legacy Tile Engine (Old-TE) capability without requiring C++ changes from Python callers.

Changes:

  • Adds a new registry-bypass, divergent C ABI (dispatcher_run_multi_abd) and a Python runner (GpuMultiABDRunner) to launch compiled multi-ABD kernels via ctypes.
  • Extends unified GEMM codegen and TE benchmark plumbing to generate/build/run multi-ABD kernels, including kernel-name contract enforcement.
  • Adds CPU-only unit tests to lock in the multi-ABD kernel naming and config JSON projection invariants.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
projects/composablekernel/dispatcher/codegen/unified_gemm_codegen.py Adds multi_abd variant support to codegen (naming, types, direct-launch generator).
projects/composablekernel/dispatcher/python/gemm_utils.py Extends config model/naming/JSON projection; adds multi-ABD ctypes wrapper + GPU runner; integrates variant-aware build.
projects/composablekernel/dispatcher/python/ctypes_utils.py Adds notes around arch-filter validation interactions (no functional bridge change here).
projects/composablekernel/dispatcher/bindings/ctypes/gemm_multi_abd_ctypes_lib.cpp New ctypes C ABI shim implementing dispatcher_run_multi_abd (device alloc/copy/launch/copyback).
projects/composablekernel/dispatcher/bindings/ctypes/CMakeLists.txt Adds a CMake target intended to build the multi-ABD ctypes library in CI/CMake flows.
projects/composablekernel/dispatcher/tests/test_multi_abd_bridge.py New CPU-only tests for multi-ABD naming + codegen JSON + shipped config validity.
projects/composablekernel/tile_engine/ops/gemm/gemm_full_benchmark.py Wires benchmark driver to select multi-ABD variant, propagate layout/ops/counts to worker, adds CLI overrides.
projects/composablekernel/tile_engine/ops/gemm/run_one_gemm_kernel.py Routes multi-ABD kernels to GpuMultiABDRunner and uses runner-driven verification.
projects/composablekernel/tile_engine/ops/gemm/gemm_multi_abd/configs/default_ci_config.json Adjusts CI warp tile K to an arch-valid value for gfx942 fp16 XDL.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@ozturkosu

Copy link
Copy Markdown
Contributor Author

A/B perf-parity sweep vs Old-TE (MI350X / gfx950)

MI350X (CDNA4 / gfx950) re-run of the multi-ABD GEMM TE-to-dispatcher bridge perf
parity, mirroring the MI300X methodology. ROCm 7.2.0, single dedicated GPU.

  • Identical kernels: both sides emit the byte-identical ck_tile kernel via the
    identical-kernel trick. The Old-TE gemm_multi_abd benchmark bakes
    num_a=2, num_b=2, num_d=2 with A=AddScale, B=AddScale, CDE=MultiDAdd
    (from tile_engine/ops/gemm/gemm_multi_abd/CMakeLists.txt cache defaults). Old-TE
    was built with the PR's fixed default_ci_config.json
    (-DGEMM_MULTI_ABD_CONFIG_FILE=default_ci_config.json,
    -DSUPPORTED_GPU_TARGETS=gfx950) → 16 stems, tile 2x2x1_32x32x16. The bridge is
    codegen'd with the same ops/counts (expand_sweep(..., num_a=2, num_b=2, num_d=2, a=AddScale, b=AddScale, cde=MultiDAdd)), NOT the bridge default all-PassThrough, so
    the bridge stem
    gemm_fp16_rcrr_<trait>_<tile>_multiabd_a2_b2_d2_AddScale_AddScale_MultiDAdd maps to
    the Old-TE binary benchmark_gemm_multi_abd_fp16_rcrr_<trait>_2x2x1_32x32x16 — same
    instance-builder codegen path → same device kernel. Bridge and Old-TE stem <core>
    sets are identical (diff empty), 16 vs 16.
  • Fair timing (both sides): warmup=50, repeat=100, flush_cache=true, rotating_count=1000. The bridge multi_abd ctypes stream_config was patched to
    {nullptr, true, 0, 50, 100, true, true, 1000} and the .so rebuilt; Old-TE bins run
    -warmup=50 -repeat=100 -flush_cache=true -rotating_count=1000 -metric=1 -verify=0 -json_output=true.
  • Same build flags: Old-TE built with the PR's own CMake
    (-DSUPPORTED_GPU_TARGETS=gfx950, TE -mllvm codegen flags on); the bridge codegen
    applies the identical _TILE_ENGINE_CODEGEN_FLAGS. No noalias=0, no hand-rolled
    hipcc.
  • Interleaved 3-rep median: for each (stem, shape) bridge and Old-TE are measured
    back-to-back, alternating order across reps, median per side (no thermal drift).
    Correctness-gated: every bridge stem is fp32-verified once before timing
    (max_rel ≈ 6.6e-4, tol 1e-2).
  • Full-precision timing (fairness fix): Old-TE's JSON latency(ms) field is printed
    rounded to 2 decimals, which quantizes sub-0.1 ms (small-shape) timings into a
    shape-fixed bucket and fabricates huge phantom gaps (every 1024×512×256 kernel
    collapsed to an identical 26.95 TFLOPS → +15..+59% phantom "bridge faster"; every
    512³ kernel → 26.90 → −21% phantom "bridge slower"). Old-TE's own tflops(TFlops)
    field is derived from the full-precision device latency with Old-TE's native flop count
    (2·M·N·K + num_d·M·N, num_d=2 — identical to the formula the bridge is normalized
    with), so both sides are compared from that same full-precision value. This removes the
    rounding artifact and leaves a pure timing comparison; all 512³ and 1024×512×256 rows
    collapsed to parity (e.g. +59% → −0.1%, −21% → −0.2%).
  • Shapes: 512³, 1024³, 2048³, 1024×512×256, 4096³.
  • Scope: fp16, layout rcrr only (the Old-TE gemm_multi_abd benchmark bakes
    rcrr), num_a=2 / num_b=2 / num_d=2, A=AddScale / B=AddScale / CDE=MultiDAdd, 16 trait
    combos → 80 measured rows.

Result (num_a=2 / num_b=2 / num_d=2 — byte-identical to Old-TE)

metric value
rows 80
median gap -0.66%
mean gap -1.38%
within +/-5% 95.0%
within +/-15% 100.0%
range [-15.0%, +1.5%]

gap_pct = (bridge_tflops - oldte_tflops) / oldte_tflops * 100 (+ = bridge faster).

Verdict: at parity. On the byte-identical num_a/b/d=2 comparison the bridge is
centered on zero (median −0.66%, mean −1.38%, 95.0% within ±5%) and 100% of rows are
within ±15% — zero surviving |gap|>15% outliers
(all small-shape phantom gaps were
traced to the Old-TE latency(ms) 2-decimal rounding, not codegen, and collapse to
parity once both sides are timed from the full-precision tflops field). Every
pipeline (compv3 / compv4 / mem), epilogue (cshuffle / default), scheduler
(intrawave / interwave) and shape is 100% within ±15%. The two widest rows (−14.4% /
−15.0%) are a single matched compv4/default/intrawave pair at 2048³ (a mild real
pipeline characteristic, consistent between the persistent-False/True variants, not an
artifact); no row exceeds ±15% and none requires rocprof follow-up.

CSV: parity_multi_abd_MI350X_gfx950_fp16_rcrr_2026-07-13.csv

@ozturkosu ozturkosu added the gfx950 run CI on gfx950 label Jul 13, 2026
ozturkosu and others added 3 commits July 15, 2026 16:49
Resolve conflicts in unified_gemm_codegen.py and gemm_utils.py with the
grouped-GEMM bridge that landed on develop. All conflicts were the multi_abd
(this branch) and grouped (develop) variants touching the same extension
points; kept both:
- GemmVariant enum, KernelNaming suffix, includes, _get_configs_for_variant,
  variant->operator map, and --variants CLI choices: keep MULTI_ABD and GROUPED.
- gemm_utils: fold multi_abd into develop's _ctypes_source_name helper; merge
  the setup-batch preflight so it checks per-config ctypes sources (develop)
  while preserving the multi_abd registry-bypass skip of the static lib
  (branch); keep the multi_abd expand_sweep kwargs + config construction.

Also fix two pre-existing parse errors on the branch (the file did not compile
before this merge): the multi_abd launch f-string was missing its closing
`return ave_time; }}"""`, and the multi_abd_config default dict was missing its
closing brace.

Address Copilot review feedback:
- gemm_multi_abd_ctypes_lib.cpp: strides are forwarded verbatim (the kernel does
  NOT derive defaults from 0), so validate stride_as/bs/ds and stride_e are
  non-null and strictly positive, reject otherwise (-1), and correct the ABI
  doc comment that wrongly claimed <=0 derived a default.
- gemm_utils.py: reject non-fp16 multi_abd in expand_sweep (before codegen/build)
  and fail fast at the top of GpuMultiABDRunner.run, so bf16 no longer fails only
  after kernels are built.
- ctypes/CMakeLists.txt: skip the dispatcher_gemm_multi_abd_lib target when no
  kernel header exists instead of building a "without kernel" lib that cannot
  compile (it unconditionally references force-included kernel symbols).
…bridge_gemm_multi_abd

# Conflicts:
#	projects/composablekernel/dispatcher/python/gemm_utils.py
@ozturkosu

ozturkosu commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Arch handling fix — do not default to gfx942

Addresses the review feedback "don't default to gfx942. Use get_arch and throw an error for unsupported arch types."

What changed (bindings/ctypes/gemm_multi_abd_ctypes_lib.cpp):

  • Replaced the silent #define GFX_ARCH "gfx942" fallback with a hard #error so the translation unit now fails to compile unless -DGFX_ARCH=<arch> is passed explicitly:
    #ifndef GFX_ARCH
    #error "GFX_ARCH must be defined at compile time (pass -DGFX_ARCH=<arch>); do not default to a specific GPU architecture."
    #endif

Why: the old default meant a build with no -DGFX_ARCH would silently target gfx942 regardless of the actual GPU. The runtime Python builder (gemm_utils.py) already passes -DGFX_ARCH="{config.gfx_arch}", and config.gfx_arch is resolved via arch detection (get_arch), so the correct path is unaffected, this change only removes the dangerous silent fallback and makes the mismatch loud at compile time.

Scope note: the get_arch / _DEFAULT_ARCH Python defaults referenced in the feedback live in the shared codegen/unified_gemm_codegen.py and python/gemm_utils.py, which are owned by other bridge PRs and intentionally left untouched here to avoid cross-PR conflicts. This PR only owns gemm_multi_abd_ctypes_lib.cpp (+ its test), so the fix is applied there.

clang-format-18 clean. Commit: 18a1a9f.

@ozturkosu
ozturkosu requested a review from Copilot July 21, 2026 21:03
@ozturkosu
ozturkosu enabled auto-merge (squash) July 23, 2026 02:06
@ozturkosu
ozturkosu disabled auto-merge July 23, 2026 02:48
…raint

expand_sweep emitted the full wave_m x wave_n x wave_k cartesian product but
never applied Old-TE's validate_warp_configuration gate
(WARP_SUPPORTED_COMBINATIONS[arch], which allows only [1,4,1]/[2,2,1]/[4,1,1]
on CDNA). The bridge therefore generated warps-per-block triples (product 1/2/16)
that Old-TE never emits, diverging the two instance sets (gfx950 whole-config:
bridge 5308 vs Old-TE 4752 buildable, overlap only 1920).

Add _warp_config_supported() (reusing the canonical table from
gemm_validation_utils with a hardcoded fallback) and gate the emit loop on it,
right after the tile-divisibility check, so the bridge and Old-TE produce the
same warp configurations for a given config+arch.
@ozturkosu
ozturkosu merged commit 34cb16f into develop Jul 23, 2026
26 checks passed
@ozturkosu
ozturkosu deleted the users/muozturk/ck/bridge_gemm_multi_abd branch July 23, 2026 18:10
assistant-librarian Bot pushed a commit to ROCm/composable_kernel that referenced this pull request Jul 23, 2026
feat(ck-tile): multi-ABD GEMM TE to dispatcher bridge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

ISSUE ID: #8997

## Motivation

The CK Tile dispatcher could already generate and launch regular GEMM
through the
TileEngine → Dispatcher bridge, but it had no path for the multi-tensor
**gemm_multi_abd** op. Multi-ABD is used when a GEMM needs to combine
several A and
B operands and fuse several D operands in the epilogue
(`E = cde_op(a_op(As) @ b_op(Bs), {Ds})`), which is a real Old-TE
capability with no
dispatcher equivalent. This PR closes that gap so Python callers can
drive multi_abd
through the dispatcher at parity with the legacy Tile Engine version,
without touching
C++.

It follows the divergent-ABI pattern established by the grouped bridge
(#9000) because
multi_abd needs **arrays** of A/B/D device pointers, not the
single-pointer regular
GEMM ABI. The capability set matches the Old-TE
`gemm_multi_abd_instance_builder.py`
exactly: `fp16`, `rcrr` layout, configurable A/B/D tensor counts, and
the element-wise
op set `{PassThrough, AddScale, MultiDMultiply, MultiDAdd}`.

## Test Plan

- Run the CPU-only unit tests (no GPU required):
  `python3 -m pytest dispatcher/tests/test_multi_abd_bridge.py -v`
- On-GPU numeric verification through the bridge launch path (gfx942 /
MI300X),
512x512x512 fp16 rcrr, across the default 2/2/2 all-PassThrough config
and
  non-PassThrough element-wise ops.
- Confirm the CI and default config expansions yield the expected kernel
counts.

## Test Result

- CPU-only unit tests pass (10 passed).
- Numeric verification (bridge launch path), 512x512x512 fp16 rcrr:
  - default 2/2/2 all-PassThrough: `max_rel = 2.9e-4`
  - CDE = MultiDAdd: `max_rel = 5.7e-4`
  - A-op = MultiDAdd: `max_rel = 4.1e-4`
  - all far below the fp16 tolerance (2e-2); 0 failed measurements.
- CI config expands to 16 arch-valid kernels; `default_config.json` →
8896.
- `standard` variant `expand_sweep` regression clean.
- clang-format-18 (18.1.8) clean on `gemm_multi_abd_ctypes_lib.cpp`.
- Serialized A/B perf-parity vs Old-TE (MI300X / gfx942, fp16 rcrr, 16
stems × 5
shapes = 80 rows, interleaved, fair 50/100/flush/rotating both sides):
**at
parity** — median gap -0.24%, mean -0.66%, 100% within ±15%, 87.5%
within ±5%
  (range [-9.57%, +5.35%]). See the parity comment for details.
ThruptiRajLakshmanaGowda added a commit that referenced this pull request Jul 23, 2026
Resolve CMakeLists.txt conflict by keeping both the AQuant/ABQuant ctypes
library targets (from this branch) and the TileEngine bridge ctypes libraries
(from develop, PRs #9305/#9306/#9328).

Co-Authored-By: Claude <noreply@anthropic.com>
bghimireamd pushed a commit that referenced this pull request Jul 28, 2026
ISSUE ID: #8997

## Motivation

The CK Tile dispatcher could already generate and launch regular GEMM
through the
TileEngine → Dispatcher bridge, but it had no path for the multi-tensor
**gemm_multi_abd** op. Multi-ABD is used when a GEMM needs to combine
several A and
B operands and fuse several D operands in the epilogue
(`E = cde_op(a_op(As) @ b_op(Bs), {Ds})`), which is a real Old-TE
capability with no
dispatcher equivalent. This PR closes that gap so Python callers can
drive multi_abd
through the dispatcher at parity with the legacy Tile Engine version,
without touching
C++.

It follows the divergent-ABI pattern established by the grouped bridge
(#9000) because
multi_abd needs **arrays** of A/B/D device pointers, not the
single-pointer regular
GEMM ABI. The capability set matches the Old-TE
`gemm_multi_abd_instance_builder.py`
exactly: `fp16`, `rcrr` layout, configurable A/B/D tensor counts, and
the element-wise
op set `{PassThrough, AddScale, MultiDMultiply, MultiDAdd}`.



## Test Plan

- Run the CPU-only unit tests (no GPU required):
  `python3 -m pytest dispatcher/tests/test_multi_abd_bridge.py -v`
- On-GPU numeric verification through the bridge launch path (gfx942 /
MI300X),
512x512x512 fp16 rcrr, across the default 2/2/2 all-PassThrough config
and
  non-PassThrough element-wise ops.
- Confirm the CI and default config expansions yield the expected kernel
counts.

## Test Result

- CPU-only unit tests pass (10 passed).
- Numeric verification (bridge launch path), 512x512x512 fp16 rcrr:
  - default 2/2/2 all-PassThrough: `max_rel = 2.9e-4`
  - CDE = MultiDAdd: `max_rel = 5.7e-4`
  - A-op = MultiDAdd: `max_rel = 4.1e-4`
  - all far below the fp16 tolerance (2e-2); 0 failed measurements.
- CI config expands to 16 arch-valid kernels; `default_config.json` →
8896.
- `standard` variant `expand_sweep` regression clean.
- clang-format-18 (18.1.8) clean on `gemm_multi_abd_ctypes_lib.cpp`.
- Serialized A/B perf-parity vs Old-TE (MI300X / gfx942, fp16 rcrr, 16
stems × 5
shapes = 80 rows, interleaved, fair 50/100/flush/rotating both sides):
**at
parity** — median gap -0.24%, mean -0.66%, 100% within ±15%, 87.5%
within ±5%
  (range [-9.57%, +5.35%]). See the parity comment for details.



---

**Related PRs / references (TileEngine → Dispatcher GEMM bridge
series):** #8997 (regular GEMM fp16/bf16 all-layout), #9000 (grouped
GEMM), #9028 (stream-K), #8887 (fp8/bf8/int8). This PR is a sibling in
the same bridge effort tracked across those PRs.

---------

Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
ozturkosu added a commit that referenced this pull request Aug 17, 2026
…idges (#10612)

ISSUE ID: #8997

## Summary

This PR combines two sibling TileEngine → Dispatcher bridge ops into a
single PR:

- **batched GEMM** (previously #9306)
- **batched_contraction** (previously #9328)

Both follow the same **direct-launch, registry-bypass** pattern (as the
stream-K bridge #9028), because their launch ABIs carry variable-length
/ batch-specific arguments the single-pointer registry backend cannot
express. The two ops touch disjoint files except
`dispatcher/tests/CMakeLists.txt`, where both GPU-correctness test
registrations are kept.

## Motivation

The dispatcher had no path for batched GEMM (same GEMM across many
independent problems with per-batch strides) or batched_contraction
(generalized batched tensor contraction `E[G.., M.., N..] = sum_K A[G..,
M.., K..] * B[G.., N.., K..]` with multi-dim G/M/N/K index groups). Both
are real Old-TE ops; this bridge lets Python callers generate, build,
and launch them at parity with the legacy Tile Engine — without writing
C++.



## Test Plan / Result

- CPU-only unit tests for both bridges + gemm_utils: **74 passed**
(`pytest dispatcher/tests/test_batched_bridge.py
dispatcher/tests/test_batched_contraction_bridge.py
dispatcher/tests/test_gemm_utils.py`).
- Batched GEMM: end-to-end name-parity + correctness across batch counts
1/2/4/8 (`max_rel ~5e-4`), non-packed strides, split-K; full
`default_config` codegen 6672 kernels / 0 failures.
- Batched contraction: on-GPU verify (gfx950) across dtype × layout ×
shape × multi-dim × pipeline + D-tensor epilogue, all PASS.
- clang-format-18 clean on both ctypes libs.

### Perf parity vs Old-TE
- Batched GEMM (MI300X, fp16 rcr, batch=8): at parity / slightly ahead —
median gap +4.00%, 100% within ±15%.
- Batched contraction (MI350X, fp16 rcr): at parity — median gap -0.95%,
100% within ±15%.

## Scope / known limitations
- Batched contraction: `rcr` only, `k_batch==1` only (split-K is a
shared Old-TE kernel defect — hard-rejected, never silently-wrong),
non-tile-multiple M/N/K rejected by `IsSupportedArguments`.

---

Supersedes and closes #9306 (batched GEMM) and #9328
(batched_contraction).

**Related PRs (TileEngine → Dispatcher GEMM bridge series):** #8997
(regular GEMM), #9000 (grouped), #9028 (stream-K), #8887 (fp8/bf8/int8),
#9305 (multi-ABD), #9307 (preshuffle), #9308 (multi-D), #10439
(block-scale quant, 5 ops).

---------

Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Thrupti Raj Lakshmana Gowda <thruptiraj.lakshmanagowda@amd.com>
shumway pushed a commit to ROCm/composable_kernel that referenced this pull request Aug 18, 2026
feat(ck-tile): multi-ABD GEMM TE to dispatcher bridge

ISSUE ID: #8997

## Motivation

The CK Tile dispatcher could already generate and launch regular GEMM
through the
TileEngine → Dispatcher bridge, but it had no path for the multi-tensor
**gemm_multi_abd** op. Multi-ABD is used when a GEMM needs to combine
several A and
B operands and fuse several D operands in the epilogue
(`E = cde_op(a_op(As) @ b_op(Bs), {Ds})`), which is a real Old-TE
capability with no
dispatcher equivalent. This PR closes that gap so Python callers can
drive multi_abd
through the dispatcher at parity with the legacy Tile Engine version,
without touching
C++.

It follows the divergent-ABI pattern established by the grouped bridge
(#9000) because
multi_abd needs **arrays** of A/B/D device pointers, not the
single-pointer regular
GEMM ABI. The capability set matches the Old-TE
`gemm_multi_abd_instance_builder.py`
exactly: `fp16`, `rcrr` layout, configurable A/B/D tensor counts, and
the element-wise
op set `{PassThrough, AddScale, MultiDMultiply, MultiDAdd}`.



## Test Plan

- Run the CPU-only unit tests (no GPU required):
  `python3 -m pytest dispatcher/tests/test_multi_abd_bridge.py -v`
- On-GPU numeric verification through the bridge launch path (gfx942 /
MI300X),
512x512x512 fp16 rcrr, across the default 2/2/2 all-PassThrough config
and
  non-PassThrough element-wise ops.
- Confirm the CI and default config expansions yield the expected kernel
counts.

## Test Result

- CPU-only unit tests pass (10 passed).
- Numeric verification (bridge launch path), 512x512x512 fp16 rcrr:
  - default 2/2/2 all-PassThrough: `max_rel = 2.9e-4`
  - CDE = MultiDAdd: `max_rel = 5.7e-4`
  - A-op = MultiDAdd: `max_rel = 4.1e-4`
  - all far below the fp16 tolerance (2e-2); 0 failed measurements.
- CI config expands to 16 arch-valid kernels; `default_config.json` →
8896.
- `standard` variant `expand_sweep` regression clean.
- clang-format-18 (18.1.8) clean on `gemm_multi_abd_ctypes_lib.cpp`.
- Serialized A/B perf-parity vs Old-TE (MI300X / gfx942, fp16 rcrr, 16
stems × 5
shapes = 80 rows, interleaved, fair 50/100/flush/rotating both sides):
**at
parity** — median gap -0.24%, mean -0.66%, 100% within ±15%, 87.5%
within ±5%
  (range [-9.57%, +5.35%]). See the parity comment for details.



---

**Related PRs / references (TileEngine → Dispatcher GEMM bridge
series):** #8997 (regular GEMM fp16/bf16 all-layout), #9000 (grouped
GEMM), #9028 (stream-K), #8887 (fp8/bf8/int8). This PR is a sibling in
the same bridge effort tracked across those PRs.

---------

Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
ThruptiRajLakshmanaGowda added a commit that referenced this pull request Aug 19, 2026
…ops (#10132)

ISSUE ID: #8997

## Summary
Adds **on-device GPU correctness tests** for the merged bridge operators
that had **no in-tree GPU test**: grouped (#9000), multi-ABD (#9305),
multi-D (#9308), mx (#9329). Closes part of the GPU-test coverage gap
(11 of 15 bridge ops previously shipped only CPU-only unit tests).

## What each test does
Builds the op's dispatcher `.so`, runs it **on-device** via the op's
`Gpu*Runner`, and compares output to an independent **numpy fp32
reference** with a scale-relative tolerance (`max|err| / max|ref|`).
Real math — reference is `A@B` per group / `A@B + D0 + D1` / the
runner's built-in multi-ABD verify. No trivial always-pass asserts.

They **SKIP cleanly** (not fail) when no GPU / hipcc / static-lib is
present, so they add **zero cost on a CPU-only runner** and only execute
on a GPU runner.

## Verification (MI300X / gfx942)
- grouped — PASS (max_rel 7.6e-3 bf16 / 4.8e-4 fp16)
- multi-ABD — PASS (6.4e-4)
- multi-D — PASS (1.8e-3)
- mx — **gfx950-only**, so it arch-gates and SKIPs on gfx942;
verify-pending on a gfx950 runner.

Registered in `dispatcher/tests/CMakeLists.txt` under label
`dispatcher;python;bridge;gpu`.

## Scope
Merged ops only (their runners are on develop). The 7 open bridge ops
(batched/preshuffle/batched-contraction + 5 block-scale) get their GPU
tests in their own PR branches.

## Test plan
- [x] grouped / multi-ABD / multi-D PASS on gfx942
- [x] all SKIP cleanly with no GPU (CPU gate stays green)
- [ ] mx verified on a gfx950 runner
- [ ] CI runs the new `dispatcher_*_gpu_correctness` entries on a GPU
runner

---------

Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
Co-authored-by: Thrupti Raj Lakshmana Gowda <thruptiraj.lakshmanagowda@amd.com>
ozturkosu added a commit that referenced this pull request Aug 24, 2026
Fold the grouped (#9000), multi-D (#9308), and multi-ABD (#9305) variant
enablement onto the shared gfx1250 foundation so gemm_universal + these three
"full" variants ship in one PR:

- grouped:   gfx1250 CI config (WMMA 16x16x32) + CPU tests
- multi-D:   gfx1250 CI config (WMMA 16x16x32) + CPU tests + --arch help
- multi-ABD: gfx1250 CI config (WMMA 16x16x32, fp16-only) + CPU tests

Each merged variant pinned the CDNA MFMA 32x32x16 tile in its CI config, which
does not run on gfx1250 WMMA; the new configs use 16x16x32. No variant codegen
change was needed beyond the shared arch enablement.

Validated on MI400/gfx1250 (ROCm 10.1): grouped 64/64 fp16 OK; multi-D 80
measurements VERIFY (max_rel ~5e-4); multi-ABD 32/32 fp16 VERIFY. 63 CPU tests pass.
ozturkosu added a commit that referenced this pull request Aug 27, 2026
Fold the grouped (#9000), multi-D (#9308), and multi-ABD (#9305) variant
enablement onto the shared gfx1250 foundation so gemm_universal + these three
"full" variants ship in one PR:

- grouped:   gfx1250 CI config (WMMA 16x16x32) + CPU tests
- multi-D:   gfx1250 CI config (WMMA 16x16x32) + CPU tests + --arch help
- multi-ABD: gfx1250 CI config (WMMA 16x16x32, fp16-only) + CPU tests

Each merged variant pinned the CDNA MFMA 32x32x16 tile in its CI config, which
does not run on gfx1250 WMMA; the new configs use 16x16x32. No variant codegen
change was needed beyond the shared arch enablement.

Validated on MI400/gfx1250 (ROCm 10.1): grouped 64/64 fp16 OK; multi-D 80
measurements VERIFY (max_rel ~5e-4); multi-ABD 32/32 fp16 VERIFY. 63 CPU tests pass.
ozturkosu added a commit that referenced this pull request Sep 2, 2026
JIRA ID : AICK-2101
JIRA ID : AICK-2133

## Summary
This PR enables the **multi-ABD** (`a2_b2_d2`) GEMM TE→dispatcher bridge
(#9305) on **gfx1250 / MI400**. It was split out of #10921 so the other
three ops (`gemm_universal`, `grouped_gemm`, `gemm_multi_d`) weren't
held back.



Comparison of this PR's original file set vs `develop` (branch head
`56e7bc3`):



**So the substantive multi-ABD gfx1250 enablement is already on
`develop`; the remaining unique delta in this PR is a single config file
(`example_problems.json`).**

## Motivation (why the enablement was needed)
gfx1250 runs **WMMA**, not MFMA: the valid fp16/bf16 warp tile is
**16x16x32**, whereas the CDNA MFMA **32x32x16** tile in
`default_ci_config.json` instantiates zero runnable kernels there.

## Supported surface (gfx1250)
The `a2_b2_d2` variant is intentionally narrow — the codegen, ctypes
lib, and runner are all **fp16-only** and **rcrr-only** (`expand_sweep`
rejects other dtypes/layouts). So **fp16 × rcrr is the complete
supported matrix**.

## Validation (on-device, MI400 / gfx1250)
Old-TE has no gfx1250 back end, so this is **correctness-only** (GPU
output vs an fp32 CPU reference), not a performance baseline.

Full `default_config` sweep of the fp16/rcrr surface, 3 tile-divisible
shapes (768³ / 1536³ / 3072³):

| op | supported matrix | kernels | result rows | fail |
|----|------------------|--------:|------------:|-----:|
| `gemm_multi_abd` | fp16 × rcrr (only) | 1,640 | 4,920 | **0** |

Every kernel in the supported surface is correct on gfx1250 (rel err ≈
4e-4).

## Note on #11161 (compv3 + intrawave + 8-warp)
The full sweep initially surfaced 552 failures, all in the `compv3 +
intrawave + 8-warp (2x4x1 / 4x2x1)` class — the **#11161** defect (a
wave64 assumption in the `comp_v3` pipeline, wrong on gfx1250 wave32).
An interim gate excludes that class so the supported surface reports
clean; the real fix, **#11175** (`Use8WarpSchedule`), is already on
`develop`.

## Notes / dependencies
- Built on #10921's shared gfx1250 foundation (now on `develop` via the
sibling series).
- Supersedes the earlier closed attempt **#10917**.
- Sibling gfx1250 bridge PRs: #10921 (universal / grouped / multi-D),
#10927 (grouped quant), #10928 (batched GEMM + contraction), #11043
(rowcol / tensor quant), #10934 (contraction multi-ABD).

---------

Co-authored-by: Muhammed Emin Ozturk <3836908+ozturkosu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants