[Feat] Added JAX-Triton bridge for ROCm - #649
Conversation
| signature=signature_with_constexpr, | ||
| ) | ||
| if is_hip: | ||
| # ROCm: active GPU target (gfx arch + 64-lane warp); binary is HSACO. |
There was a problem hiding this comment.
ROCm arch does not necessary have 64-lane wavefront
| compiled.asm["ptx"], # arg4: ptx (str) | ||
| "", # arg5: ttir (str) - empty | ||
| compute_capability, # arg6: compute_capability (int) | ||
| compiled.name, |
There was a problem hiding this comment.
Keep upstream comments as-is
| ) | ||
| if num_warps is None: | ||
| # 32 warps would exceed the 1024-thread block limit on AMD's 64-lane warp. | ||
| num_warps = 4 if is_hip_extension() else 32 |
There was a problem hiding this comment.
I picked 4 because that's Triton's own default num_warps, it is what triton.Config() and unspecified @triton.jit launch use.
Claude WalkthroughIntent. Extend TE's JAX/Triton custom-call bridge in Key changes.
Walkthrough.
Testing. New Notes for reviewers.
Generated by Claude. To request a code review, comment |
Claude reviewReviewed the PR-specific diff (
Verdict: looks clean. The ROCm/HSACO path, Gluon Copyright headers: OK — both modified files carry an AMD 2026 line above the preserved NVIDIA No new inline findings. |
|
@AllenFarcas Please resolve conflict and test against the latest CI image. |
| if is_hip: | ||
| fd, hsaco_path = tempfile.mkstemp(suffix=".hsaco", dir=_hsaco_dir()) | ||
| with os.fdopen(fd, "wb") as f: | ||
| f.write(binary) | ||
| binary = hsaco_path |
There was a problem hiding this comment.
The HSACO path ends up inside the serialized custom call, which makes the compiled executable non-portable across processes.
binary flows into TritonKernel → TritonKernelCall.to_proto() → zlib.compress(call_proto) → the backend_config of the triton_kernel_call custom call (utils.py:665-679). On CUDA that blob contains the PTX text, so the executable is self-contained. On ROCm it now contains /tmp/te_jax_hsaco_<rand>/tmp<rand>.hsaco, and _HSACO_TMPDIR is deleted when the interpreter exits.
So if JAX's persistent compilation cache is enabled (JAX_COMPILATION_CACHE_DIR — ci/jax.sh:73 sets it for the multi-GPU run), a cache hit in a later process replays an executable pointing at a temp dir that no longer exists, and the kernel fails to load at launch. Same issue for anything else that outlives the process that compiled it.
Writing to a stable, content-addressed location instead of mkstemp would fix it and would also avoid re-emitting an identical blob on every process start — cache_key is already a hash of everything that determines the binary, so e.g. <cache_root>/<cache_key>.hsaco under a persistent dir works, with the write done atomically (temp file + os.replace) to stay safe under concurrent processes.
| import triton | ||
| from jax._src.lib import gpu_triton | ||
| from triton.compiler import compiler as tc | ||
| from triton.backends.nvidia import compiler as cb |
There was a problem hiding this comment.
cb is now only used on the CUDA branch (utils.py:368), but this import is still unconditional at module scope. A Triton built with the AMD backend only (TRITON_CODEGEN_BACKENDS=amd) does not ship triton/backends/nvidia/, so this raises ImportError and gets rewritten by the handler below into "Triton is required … Install with: pip install triton" — a misleading message, and the ROCm path this PR adds never gets a chance to run.
Since make_backend is already imported lazily inside the is_hip branch, doing the same for cb in the else branch would make the module import cleanly on ROCm-only Triton builds regardless of how the container's Triton was configured.
|
|
||
|
|
There was a problem hiding this comment.
Two consecutive blank lines here, but this is inside the if HAS_GLUON: block (depth 1). Black only permits two blank lines at module level — below that the limit is one — so black --line-length=100 --preview (.pre-commit-config.yaml:31) will rewrite this and the lint gate will fail as-is.
| except Exception: # pragma: no cover - Gluon or active GPU target unavailable | ||
| gluon = gl = None | ||
| WARP_SIZE = None | ||
| HAS_GLUON = False |
There was a problem hiding this comment.
This combination makes a Gluon failure indistinguishable from a Gluon pass in CI.
except Exception swallows everything — a genuine breakage in triton.experimental.gluon, a driver that fails to initialise, a warp_size attribute rename — and sets HAS_GLUON = False. Because the test classes are then defined inside if HAS_GLUON: (:152), they are never collected, so the report shows no failures and no skipped entries either. The suite goes green having exercised nothing of what this PR adds.
Defining the classes unconditionally and gating with @pytest.mark.skipif(not HAS_GLUON, reason=...) would keep the same behaviour while making the non-execution visible in the junit XML. Narrowing the catch to (ImportError, AttributeError, RuntimeError) would also stop it from masking unexpected failures.
Related: tests/jax/test_triton_custom_calls.py is not in ci/jax.sh::run_test_config, so neither the existing Triton test nor these new Gluon tests run in ROCm CI at all. Given that ROCm/Gluon enablement is the whole point of the PR, adding run_default_fa 1 test_triton_custom_calls.py there is what would actually demonstrate it works — and it lines up with the "test against the latest CI image" ask.
Claude review (re-review)Re-reviewed the full PR diff against the current base ( Verdict: the ROCm/HSACO and Gluon compile paths are structurally sound and the CUDA path stays guarded and unchanged. Four issues worth addressing before merge, posted inline: one ROCm correctness risk around how the HSACO binary is delivered, one import-time fragility on ROCm-only Triton builds, one Black violation that will fail the lint gate, and one test-visibility gap that lets the new Gluon coverage silently not run. Copyright headers: OK — both modified files carry an AMD 2026 line above the preserved NVIDIA Note: the PR is currently in a conflicting state against |
Description
Extend TE's JAX Triton custom-call bridge to compile and dispatch AMD ROCm (HSACO) and Gluon kernels. This PR enables AMD's layout-explicit Gluon kernels to be called from JAX, mirroring NVIDIA's existing support.
Fixes https://github.com/ROCm/frameworks-internal/issues/16044
Type of change
Changes
Please list the changes introduced in this PR:
GluonASTSourcewith a full constexpr-marked signature.nanobind std::string), not raw bytes.num_warps/num_stagesfor non-autotuned Gluon layout matching.Checklist: