[AMDGPU] Fix: apply amdgpu-ieee/amdgpu-dx10-clamp to all functions - #775
[AMDGPU] Fix: apply amdgpu-ieee/amdgpu-dx10-clamp to all functions#775paveltc wants to merge 3 commits into
Conversation
|
Note Outdated (Aug 20, 2026): This comment no longer applies to the current PR. Keeping it for historical reference. Depends on #774 — please review/merge that first. This branch is stacked on #774: of its three commits, the first two ( The actual change here is a single-file, +16/-6 edit to Plan: once #774 merges, I'll rebase this onto |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 23f2bb97b7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (this->config_.fast_math) { | ||
| F.addFnAttr("unsafe-fp-math", "true"); | ||
| F.addFnAttr("no-signed-zeros-fp-math", "true"); |
There was a problem hiding this comment.
Split unrelated AMDGPU behavior out of this fix
The stated inlining fix only requires propagating amdgpu-ieee and amdgpu-dx10-clamp, but this commit also changes fast-math attributes, scheduling and work-group heuristics, DAZ behavior, cache keys, bindings, and the public kernel API. These independent semantic changes greatly increase the regression surface of the backport; please partition them into separate changes and keep this fix localized to the two attributes.
AGENTS.md reference: AGENTS.md:L7-L13
Useful? React with 👍 / 👎.
| fastcache: bool = False, | ||
| graph: bool = False, | ||
| checkpoints: bool = False, | ||
| cuda_graph: bool = False, |
There was a problem hiding this comment.
Document the new cuda_graph decorator argument
This adds cuda_graph to the public @qd.kernel API and gives it deprecation and graph-enabling behavior, but neither the updated user guide nor this function's Args section documents it. Users therefore cannot discover its semantics or migration path from the public documentation; add the corresponding docs/ update if this alias remains.
AGENTS.md reference: AGENTS.md:L15-L22
Useful? React with 👍 / 👎.
| "amdgpu-max-num-workgroups", | ||
| "amdgpu-agpr-alloc", | ||
| "amdgpu-waves-per-eu", | ||
| "amdgpu-flat-work-group-size", | ||
| "amdgpu-sched-strategy", |
There was a problem hiding this comment.
Allow the guarded AMDGPU attributes through validation
When a user tries to override amdgpu-ieee or amdgpu-dx10-clamp through fn_attrs, _validate_fn_attrs rejects the decorator because neither name is registered here. This makes the new JIT hasFnAttribute guards—and the documented claim that user-supplied values win—unreachable for the two attributes central to this change; register both names or remove the unsupported override claim.
Useful? React with 👍 / 👎.
These two attributes are applied to all functions in jit_amdgpu.cpp behind `!F.hasFnAttribute(key)` guards, and the PR documents that a user-supplied value via @qd.kernel(fn_attrs=...) wins over the default. But neither name was in the fn_attrs registry, so _validate_fn_attrs rejected any attempt to override them, making that override path unreachable (Codex Genesis-Embodied-AI#775 P2). Register both names so the guards are actually reachable. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks @codex. Quick disposition of the three comments: P1 — "Split unrelated AMDGPU behavior out of this fix" ( Both of these are from the inherited #774 commits, not this PR's actual change. This branch is stacked on #774: of its three commits, the first two ( So the fix is already localized to the two attributes as requested; the extra surface Codex is flagging lives in #774. Once #774 merges, I'll rebase this onto P2 — "Allow the guarded AMDGPU attributes through validation" ( Good catch — this one is real and specific to this PR's claim. Fixed in |
|
To use Codex here, create a Codex account and connect to github. |
Set amdgpu-ieee=false and amdgpu-dx10-clamp=false on all functions in the AMDGPU module rather than only AMDGPU_KERNEL entries. LLVM's inliner refuses to inline a callee into a caller when they carry mismatching target-specific attributes. Applying these attributes to kernels only leaves internal runtime device functions (e.g. gpu_parallel_range_for and the body functions it dispatches) with a mismatching attribute set, so they are not inlined into the kernel entry. Without that inlining, InferAddressSpaces cannot follow the pointer chain from kernel parameters to field data and cannot promote flat_load/flat_store/flat_atomic to global_*, causing flat-atomic coherency issues and a ~4% throughput regression on gfx942 (MI300X). Applying the two attributes uniformly restores inlining and lets InferAddressSpaces emit global_load/global_store/global_atomic. Each write is guarded by hasFnAttribute so it remains idempotent. Self-contained backend change: single file, no public API, no dependency on the per-kernel fn_attrs work in Genesis-Embodied-AI#774. Co-authored-by: Cursor <cursoragent@cursor.com>
d62feed to
99a4eba
Compare
|
Given the instructions to merge/ review #774 first, will wait for 774 to be merged/reviewed before reviewing this PR. |
|
@hughperkins This PR actually no longer depends on 774. |
|
@codex review |
| function_pass_manager_addrcast.run(*func); | ||
| function_pass_manager_addrcast.doFinalization(); | ||
|
|
||
| // Apply amdgpu-ieee and amdgpu-dx10-clamp to ALL functions, not just |
There was a problem hiding this comment.
could we wrap this at 120c, rather than 80c please
There was a problem hiding this comment.
also, lets remove ' Applying them uniformly keeps every function's attribute set
// consistent so inlining and address-space inference run as intended. The
// hasFnAttribute guard keeps each write idempotent.', to try to keep the comment concise, please.
There was a problem hiding this comment.
Done in 4ec180d — comment is now wrapped at 120c.
There was a problem hiding this comment.
Done in 4ec180d. Dropped the 'Applying them uniformly...' / hasFnAttribute-idempotency sentence. (The guard itself is also gone now — see the summary comment.)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 99a4eba61b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| F.addFnAttr("amdgpu-ieee", "false"); | ||
| } | ||
| if (!F.hasFnAttribute("amdgpu-dx10-clamp")) { | ||
| F.addFnAttr("amdgpu-dx10-clamp", "false"); |
There was a problem hiding this comment.
Honor strict math before disabling AMDGPU IEEE mode
For every AMDGPU compilation, including qd.init(fast_math=False), these unconditional values change the kernel descriptor's default IEEE and DX10-clamp modes from enabled to disabled. Consequently, kernels that opt out of relaxed math can silently get different special-value behavior for NaNs, infinities, signed zero, or denormals. Gate the relaxed values on config_.fast_math, or use the enabled/default values consistently across callers and callees when strict math is requested.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 4ec180d. amdgpu-ieee is now gated on config_.fast_math: it keeps the IEEE-compliant default (true) unless fast_math is requested, in which case it relaxes to false. amdgpu-dx10-clamp stays false (the normal compute value). The important part — verified on gfx942 (MI308X) — is that the flat_→global_ promotion depends only on the values being uniform across caller and callee, not on the specific value, so strict-math users now keep full IEEE semantics with no loss of the promotion. Details in the summary comment below.
Address review feedback on the module-wide mode-attribute sweep: - Uniformity (not the specific value) is what restores inlining and the flat_*->global_* promotion. Confirmed on gfx942 (MI308X): the global_* ISA counts are identical with amdgpu-ieee=true vs =false. So keep the IEEE-compliant default (true) and only relax to false when fast_math is requested, instead of unconditionally downgrading FP/NaN semantics for fast_math=False users. - Overwrite unconditionally rather than guarding on hasFnAttribute: a value preset on a linked ROCm device-lib function would otherwise be preserved and re-introduce the exact mismatch this sweep eliminates. addFnAttr overwrites same-key string attrs, so no idempotency guard is needed. - Skip declarations/intrinsics (F.isDeclaration()) so target attrs aren't placed on external decls. - Rewrap comment at 120c per repo convention; drop redundant trailing note. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for the thorough review (and for relaying Opus's notes, @hughperkins). Pushed 4ec180d addressing all points. Summary + the empirical answer to the crux question: The crux: value vs. uniformityOpus's core question — "would setting both to
So the throughput win comes from uniformity restoring inlining (→ What changed
On the ~4% numberAgree the headline number deserves a full repro (commit hash, batch size, before/after ISA). The table above is the before/after |
Benchmark / mechanism follow-up (gfx942 MI308X, ROCm 7.2.4, pinned LLVM 22.1.0)Following up on the ~4% claim. Rather than just quote a number, I isolated the mechanism at the IR level and ran an A/B on hardware. Summary: the inliner mechanism this PR relies on is real and confirmed on the current toolchain. On this specific LLVM/ROCm combination the microbenchmarks show no delta (clean 1. Mechanism, proven with the pinned LLVM 22.1.0
|
| callee | caller (kernel) | result |
|---|---|---|
ieee=true |
ieee=true |
inlined |
ieee=false |
ieee=false |
inlined |
| (none) | (none) | inlined |
| (none) | ieee=false |
inlined |
ieee=false |
(none) | inlined |
ieee=true |
ieee=false |
NOT inlined |
dx10-clamp=true |
dx10-clamp=false |
NOT inlined |
The inliner blocks only when both sides carry the attribute with different values. Absent-vs-present is compatible; uniform (either value) inlines. This confirms the PR's core rationale — and confirms Opus's crux point: uniformity is what restores inlining, not the specific value (both true/true and false/false inline).
2. Hardware A/B (baseline = sweep stripped, = main; vs this PR)
| kernel (64 MB, f32) | baseline | this PR | delta |
|---|---|---|---|
| saxpy | 1159.0 GB/s | 1157.6 GB/s | ~0% |
| atomic_add reduction | 126.0 GB/s | 126.2 GB/s | ~0% |
Emitted AMDGCN is byte-for-byte identical between the two builds. The reason: on this LLVM 22.1.0 / ROCm 7.2.4 combination, clean main sets these attributes on nothing → the "neither set" row above → the kernels already inline and InferAddressSpaces already promotes flat_*→global_* without any help. So there is simply no mismatch present for the sweep to fix on this toolchain, which is why the microbenchmark delta is ~0% here.
3. Where the ~4% came from
The ~4% (301k → 314k) was measured on our AMD integration branch, where amdgpu-ieee/dx10-clamp were previously set kernel-only. That kernel-only placement created the caller/callee value mismatch (the BLOCKED row), and moving the attributes to all functions removed it. Upstream main sets neither attribute anywhere, so that mismatch does not exist here — which is why the A/B above is ~0% on main. The value of this PR upstream is guaranteeing the attributes can never be asymmetric (plus the IEEE-preservation fix), not a standalone perf win against main.
4. Device-lib guard check (Opus)
Checked the ROCm 7.2.4 bitcode (ocml, ockl, oclc_*): none carry explicit amdgpu-ieee/dx10-clamp, so on this ROCm they're the harmless "absent" case. But Opus's warning holds in principle — if a linked function carried a differing explicit value, the old hasFnAttribute guard would preserve it and re-create the BLOCKED row. Dropping the guard (4ec180d) is the robust choice and is what guarantees uniformity.
Repro
- Inliner truth-table: hand-written IR +
opt -passes='inline,instcombine'on the pinned LLVM 22.1.0. - A/B: same kernels, kernel-profiler timing, 7 trials x 1000 iters, built with and without the sweep on the same MI308X.
|
For the benchmark, I'm not sure I follow. I was imagining that the benchmark would use some scenario that shows a benefit on this PR, compared to main? |
|
@hughperkins Good instinct, and after tracing this all the way back the honest answer is: there isn't a scenario that beats The
The ~4% (301k → 314k) in the original description was measured on our AMD integration branch, where these attributes were set kernel-only — that kernel-only placement is what created the mismatch, and this change fixed it there. Upstream never had the kernel-only setting, so the regression scenario doesn't exist to reproduce against So I'd reframe this PR (and I've updated the description accordingly): on Totally fair if you'd prefer to hold this until there's an upstream consumer that actually sets these attributes (at which point the benefit becomes demonstrable) — your call on whether the forward-looking consistency + the IEEE-gating fix are worth landing now on their own. |
|
Note for reviewers / future upstreaming (scoping caveat). In our AMD integration branch, the This PR deliberately does not bring those upstream — it ports only Flagging it because |
|
Closed as the only use of this PR came from PR 774, which has been closed. |


Summary
Backend-only AMDGPU codegen change. No public API, no new user-tunable knobs, solver-agnostic. Self-contained; does not depend on #774.
Sets
amdgpu-ieeeandamdgpu-dx10-clampuniformly on all functions (not justAMDGPU_KERNELentries). LLVM's inliner refuses to inline a callee into a caller when the two carry different values for these mode attributes; if only kernels carry them, internal device functions (e.g.gpu_parallel_range_forand the body functions it dispatches) fail to inline, andInferAddressSpacescan no longer follow the pointer chain from kernel params to field data to promoteflat_load/flat_store/flat_atomictoglobal_*.Perf context (please read — the ~4% is not against
main)This change was ported from our AMD integration branch, where these attributes were previously set kernel-only. Against that kernel-only baseline, moving them to all functions removed the caller/callee mismatch and recovered ~4% throughput (301k → 314k).
Upstream
mainsets neither attribute anywhere, so there is no mismatch to fix onmainand no perf delta to show. I verified this on gfx942 (MI308X, ROCm 7.2.4, LLVM 22.1.0): builds with and without this change produce byte-identical AMDGCN and identical throughput (saxpy ~1159 GB/s, atomic reduction ~126 GB/s, both directions). I also confirmed the mechanism at the IR level with the pinned LLVM 22.1.0opt— the inliner blocks only on a present-vs-present value mismatch; uniform (either value), absent/absent, and absent/present all inline.So on
mainthis PR is best understood as a consistency + correctness change, not a standalone perf win:amdgpu-ieeefollowsfast_math(stays at the IEEE-compliant defaulttrueunlessfast_mathis requested).Full mechanism write-up and A/B data are in the follow-up comment below.
Change
Single file (
quadrants/runtime/amdgpu/jit_amdgpu.cpp): a small loop over every non-declaration function setting both attributes uniformly.amdgpu-ieeeis gated onfast_math(IEEE-complianttrueby default,falseonly underfast_math);amdgpu-dx10-clamp=false(the normal compute value). Attributes are written unconditionally (nohasFnAttributeguard) so a value preset on a linked ROCm device-lib function cannot re-introduce the mismatch. ~13 lines, no other files touched.Independence from #774
An earlier version of this fix was stacked on #774 and reused its per-kernel
fn_attrsregistry. That coupling has been removed — this PR applies againstmaindirectly and can merge with or without #774.