Skip to content

feat(rocm): serve on AMD GPUs through the HIP toolchain - #137

Open
paralin wants to merge 1 commit into
FlashML-org:mainfrom
paralin:amd
Open

feat(rocm): serve on AMD GPUs through the HIP toolchain#137
paralin wants to merge 1 commit into
FlashML-org:mainfrom
paralin:amd

Conversation

@paralin

@paralin paralin commented Aug 24, 2026

Copy link
Copy Markdown

FreeToken built and served only against CUDA: setup.py linked its host-side extensions against cudart, the kernel JIT compiled with nvcc-only flags, and several triton call sites passed NVIDIA-only launch options. On an AMD GPU the engine could not even finish booting.

Build _pinned_tensor and _cpu_moe against HIP through a shim header that maps every CUDA runtime symbol they use onto its HIP equivalent. Teach the JIT the same trick: drop --expt-relaxed-constexpr (hipcc rejects it; relaxed constexpr is already its default) and force- include a shim that maps the CUDA launch-config surface onto hipLaunchKernelEx. PDL launch attributes have no equivalent on this runtime; every kernel served on AMD builds with use_pdl=false, so the shim drops attributes instead of setting them.

Three smaller fixes complete the port:

  • norm.py and activation.py pass launch_pdl to triton only when it is true; the AMD launcher rejects the keyword outright.
  • attention.py floors block_h at 16 under HIP because RDNA3 WMMA cannot select an instruction for tl.dot below M=16. Padded head lanes were already masked.
  • fast_index_copy.cuh replaces PTX streaming-load/store inline asm with plain device loads and stores. The cache-policy hints are dropped; correctness is unchanged.

Drop the explicit triton==3.6.0 pin so the ROCm-bundled AMD-backend triton survives installation; torch's own wheels already constrain their matching triton build. Every edit is gated on HIP detection at build or run time, so NVIDIA paths are unchanged.

FreeToken built and served only against CUDA: setup.py linked its
host-side extensions against cudart, the kernel JIT compiled with
nvcc-only flags, and several triton call sites passed NVIDIA-only
launch options. On an AMD GPU the engine could not even finish
booting.

Build _pinned_tensor and _cpu_moe against HIP through a shim header
that maps every CUDA runtime symbol they use onto its HIP equivalent.
Teach the kernel JIT the same trick: drop --expt-relaxed-constexpr
(hipcc rejects it; relaxed constexpr is already its default) and
force-include a shim, packaged with the kernel csrc tree, that maps
the CUDA launch-config surface onto hipLaunchKernelEx. PDL launch
attributes have no equivalent on this runtime; every kernel served on
AMD builds with use_pdl=false, so the shim drops attributes instead
of setting them.

Three smaller fixes complete the port:

- norm.py and activation.py omit the triton launch_pdl keyword under
  HIP; the AMD launcher rejects it outright. NVIDIA keeps the
  upstream launch_pdl=pdl call for both true and false.
- attention.py floors block_h at 16 under HIP because RDNA3 WMMA
  cannot select an instruction for tl.dot below M=16. Padded head
  lanes were already masked.
- fast_index_copy.cuh guards its PTX streaming-load/store inline asm:
  CUDA keeps it verbatim; HIP has no equivalent asm, so those builds
  fall back to plain device loads and stores. The cache-policy hints
  are dropped on HIP only; correctness is unchanged.

setup.py detects the backend from torch: ROCm builds of torch take
the HIP branch, CUDA builds keep the original cudart link and nvcc
toolchain check unchanged, and anything else fails with a clear
error. Every other edit is gated on HIP detection at run time.

ROCm installs should resolve torch from the ROCm wheel index first;
the pinned PyPI triton conflicts with the ROCm-bundled one.
skywalk1411 added a commit to skywalk1411/FreeToken that referenced this pull request Aug 27, 2026
Both were made mid-investigation, before the real cause of a since-fixed crash
(the raw-PTX bug in activation.py, and separately the e4m3_native() tuple-
comparison bug) was actually found. Re-tested each in isolation -- eager,
batched, and inside real CUDA graph capture+replay -- now that those are
fixed, and both work fine at the original, CUDA-tuned settings:

- decode_paged_attention: the block_h>=16 floor (kept -- RDNA WMMA genuinely
  has no instruction below M=16, confirmed independently and matches
  upstream FlashML-org#137) was sufficient on its own. The USE_TL_DOT broadcast-sum
  fallback this PR had added on top was solving a problem that was actually
  in a different kernel; removed, restoring real matrix-core-accelerated
  decode attention.
- _select_extend_tile: the 128x64 -> 64x32 shrink on HIP was diagnosed as a
  VGPR-exhaustion issue via a py-spy trace mid-investigation, before the
  session had isolated the actual crash to activation.py. Re-verified
  end-to-end against Qwen3.6-35B-A3B-FP8's GDN/split-extend path (the
  kernel this shrink targeted) at the original tile size: no crash, correct
  output. Reverted to the CUDA-tuned tile.

Both re-verified against real chat completions (Qwen3-8B for the decode
path, Qwen3.6-35B-A3B-FP8 for the extend/split path) after reverting, not
just the isolated kernel tests.
dbourdea pushed a commit to dbourdea/FreeToken that referenced this pull request Aug 29, 2026
Both were made mid-investigation, before the real cause of a since-fixed crash
(the raw-PTX bug in activation.py, and separately the e4m3_native() tuple-
comparison bug) was actually found. Re-tested each in isolation -- eager,
batched, and inside real CUDA graph capture+replay -- now that those are
fixed, and both work fine at the original, CUDA-tuned settings:

- decode_paged_attention: the block_h>=16 floor (kept -- RDNA WMMA genuinely
  has no instruction below M=16, confirmed independently and matches
  upstream FlashML-org#137) was sufficient on its own. The USE_TL_DOT broadcast-sum
  fallback this PR had added on top was solving a problem that was actually
  in a different kernel; removed, restoring real matrix-core-accelerated
  decode attention.
- _select_extend_tile: the 128x64 -> 64x32 shrink on HIP was diagnosed as a
  VGPR-exhaustion issue via a py-spy trace mid-investigation, before the
  session had isolated the actual crash to activation.py. Re-verified
  end-to-end against Qwen3.6-35B-A3B-FP8's GDN/split-extend path (the
  kernel this shrink targeted) at the original tile size: no crash, correct
  output. Reverted to the CUDA-tuned tile.

Both re-verified against real chat completions (Qwen3-8B for the decode
path, Qwen3.6-35B-A3B-FP8 for the extend/split path) after reverting, not
just the isolated kernel tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant