Skip to content

[AMDGPU] Address-space-at-source global memory optimization - #866

Merged
hughperkins merged 7 commits into
Genesis-Embodied-AI:mainfrom
paveltc:feat/amdgpu-global-as
Aug 28, 2026
Merged

[AMDGPU] Address-space-at-source global memory optimization#866
hughperkins merged 7 commits into
Genesis-Embodied-AI:mainfrom
paveltc:feat/amdgpu-global-as

Conversation

@paveltc

@paveltc paveltc commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

On the AMDGPU backend, device buffers are currently materialized as generic (flat) pointers. As a result, most memory traffic in generated kernels lowers to flat_load/flat_store instructions, which go through the slower generic-addressing path even though the underlying memory is always global. This PR tags device pointers as addrspace(1) (global) at the point where they are materialized, so that LLVM's InferAddressSpaces pass can propagate the global address space through the kernel and promote flat_* operations to global_* operations.

The change is fully gated behind the QD_AMDGPU_GLOBAL_AS environment variable and arch-guarded to AMDGPU only. When the gate is unset, code generation is byte-for-byte identical to upstream (verified — see Test Plan). CPU and CUDA backends are completely untouched.

What changes

The work is split into two layers:

Layer A — source-side tagging (codegen/amdgpu/codegen_amdgpu.cpp)
AMDGPU-specific codegen overrides that tag pointers as addrspace(1) where they are first materialized:

  • ExternalPtrStmt — ndarray data pointers
  • GlobalTemporaryStmt — runtime global-temporary buffer pointers
  • MatrixPtrStmt — preserves the origin address space through the byte-offset GEP, avoiding ptrtoint/inttoptr round-trips that would otherwise strip the address space.

Layer B — argument-walk provenance (codegen/llvm/codegen_llvm.cpp, .h)
Shared LLVM-layer changes that carry the global address space along the pointer-derivation chain, so the tag survives from the argument struct down to the element access and doesn't get re-stripped per iteration. A single choke-point helper, maybe_tag_amdgpu_global_ptr(), applies the tag only when the arch is AMDGPU and the gate is set; it is a no-op otherwise. This is applied in get_struct_arg(), get_args_ptr(), and visit(ExternalPtrStmt), and atomic_op_using_cas() is adjusted to preserve the destination's address space when forming the integer pointer for CAS loops (preventing invalid cross-addrspace bitcasts).

Diff footprint: 3 files, +103 / −5.

Effect on generated IR

With the gate on, generic (flat) loads collapse dramatically as the global address space propagates through the kernel — on the franka monolith kernel, flat loads dropped from 10,768 → 503, with the remainder promoted to global-addressed operations.

Relationship to #775

#775 (amdgpu-ieee / amdgpu-dx10-clamp attribute harmonization) is complementary but not required. This PR was built and validated on upstream main without #775 present, and delivers its speedups independently. The two compose cleanly and can land in either order.

Test Plan

All testing was performed on an MI300X (gfx942) node under ROCm/HIP, with GPU clocks locked to 1900 MHz for measurement determinism. Correctness and performance were measured A/B (gate ON vs. gate OFF) using the genesis test_rigid benchmark harness.

  • Correctness (franka + anymal, both arms): PASS. Both robots produce correct simulation results with the gate ON and OFF.
  • IR verification: PASS. With the gate ON, InferAddressSpaces promotes flat memory operations to global as intended (franka monolith: 10,768 → 503 flat loads).
  • Performance (A/B, gate ON vs OFF): franka +17.7% (n=8), anymal +4.7% (n=3).
  • Gate-OFF byte-identical IR: PASS. Built a pristine wheel from this branch's base commit (patch removed) and dumped the optimized AMDGPU IR for a representative kernel from both the patched-but-gate-OFF build and the pristine base. All 82 kernels are byte-identical (matching SHA-256 over the full set), confirming the gate is a true zero-footprint switch.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5cba42cc23

ℹ️ 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".

Comment thread quadrants/codegen/llvm/codegen_llvm.cpp Outdated
paveltc and others added 2 commits August 14, 2026 16:13
Tag AMDGPU device pointers as addrspace(1) where they are materialized so
InferAddressSpaces propagates the tag and dependent loads/stores lower to
global_load/global_store/global_atomic instead of generic flat_*.

Layer A (codegen_amdgpu.cpp): source-side tagging of ExternalPtr /
GlobalTemporary data pointers and address-space-preserving MatrixPtr GEPs.

Layer B (codegen_llvm.cpp/.h): carry global provenance through the shared
argument-pointer walk via maybe_tag_amdgpu_global_ptr() (get_args_ptr,
get_struct_arg, ExternalPtrStmt) so per-iteration re-casts collapse, and
preserve dest address space in atomic_op_using_cas.

All tagging is gated on QD_AMDGPU_GLOBAL_AS and arch-guarded to AMDGPU, so
CPU/CUDA emit byte-identical IR whether or not the patch is present.

Reconstructs AMD-Ecosystem PRs Genesis-Embodied-AI#7 (6c9889f, 2d78505) + Genesis-Embodied-AI#3 (4e59c11)
plus the argument-walk provenance fix, rebased onto current upstream.
get_args_ptr() is shared between the top-level kernel and @qd.real_func
(Function) callees. Only the top-level kernel's arg buffer is device-staged
in global memory; a Function callee receives a caller-local alloca buffer
(see visit(FuncCallStmt)), so casting it to addrspace(1) under
QD_AMDGPU_GLOBAL_AS made the callee read its scalar parameters through global
memory pointing at private storage.

Guard the args-buffer tag on the callable not being a Function. The ndarray
data-pointer tags in get_struct_arg() and ExternalPtrStmt are unaffected, so
top-level kernel promotion (and the measured speedups) are preserved: the
franka gate-ON optimized IR is byte-identical before and after this change.

Fixes the get_args_ptr provenance issue flagged in review.

Co-authored-by: Cursor <cursoragent@cursor.com>
@paveltc
paveltc force-pushed the feat/amdgpu-global-as branch from 6b3b56c to 31c6a64 Compare August 14, 2026 21:13
@hughperkins

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31c6a644f3

ℹ️ 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".

Comment thread quadrants/codegen/llvm/codegen_llvm.cpp Outdated
Comment thread quadrants/codegen/llvm/codegen_llvm.cpp Outdated
Comment thread quadrants/codegen/amdgpu/codegen_amdgpu.cpp Outdated
Comment thread quadrants/codegen/amdgpu/codegen_amdgpu.cpp Outdated
@hughperkins

Copy link
Copy Markdown
Collaborator

Checked with Opus. Here are its comments:

Screenshot 2026-08-24 at 11 04 47 Screenshot 2026-08-24 at 11 05 35

Please could you address Opus concerns, in addition to addressin the codex comment I flagged earlier, and removing the env var gating.

@hughperkins hughperkins added the awaiting-contributor-action awaiting-contributor-action label Aug 24, 2026
…params

Address review on Genesis-Embodied-AI#866:

- Remove the QD_AMDGPU_GLOBAL_AS env-var gate and make global address-space
  tagging the default AMDGPU behavior (per maintainer request to drop the gate
  and the old flat path). maybe_tag_amdgpu_global_ptr keeps its arch==amdgpu
  guard, so CPU/CUDA remain byte-identical. This also moots the cache-key and
  env-var-docs review comments, since there is no longer a mode toggle.

- Fix get_struct_arg() to not tag Function (@qd.real_func) callee args as
  addrspace(1): qd.ref(...) reference parameters point at a caller-local alloca
  (private memory), so tagging them global was unsound (Codex P1 / test_ref).
  Mirrors the existing get_args_ptr() Function guard. Top-level ndarray
  promotion is unaffected; in-callee ndarray data pointers are still tagged at
  ExternalPtrStmt where the global-backed data pointer is loaded.

- Remove the now-redundant AMDGPU visit(ExternalPtrStmt) override: the shared
  base codegen already tags ndarray data pointers for AMDGPU.

- Add tests/python/test_amdgpu_global_as.py: an AMDGPU-only IR-shape regression
  guard asserting flat_* -> global_* promotion (global_* present, flat_* near
  zero) so a future refactor can't silently drop the optimization.

Co-authored-by: Cursor <cursoragent@cursor.com>
@paveltc

paveltc commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 593b93d addressing the review (@hughperkins, Codex, and the Opus notes).

Core change

  • Removed the QD_AMDGPU_GLOBAL_AS env-var gate; address-space-at-source tagging is now the default AMDGPU behavior and the old flat path is gone. maybe_tag_amdgpu_global_ptr keeps its arch == amdgpu guard, so CPU/CUDA remain byte-identical.

Codex P1s

  • Real-func reference params: fixed — get_struct_arg() now Function-guards the tag like get_args_ptr(), so qd.ref(...) params (caller-local alloca) stay generic.
  • Cache key and env-var docs: moot now that there is no mode toggle.

Opus notes

  • Q1 (redundant visit(ExternalPtrStmt) override): removed — the shared base codegen already tags ndarray data pointers.
  • Q2 (get_struct_arg not Function-guarded): fixed as above.
  • Q3 (off-by-default / slow path): resolved by making it the default.
  • IR-level regression guard (the "one real gap"): added tests/python/test_amdgpu_global_as.py, an AMDGPU-only test asserting flat_* -> global_* promotion (global_* present, flat_* near zero) so a future refactor can't silently drop the optimization.
  • f16 atomics: I'll confirm test_atomic_float_ops runs (not skipped) and is green on MI300X.

Validation note: the code is lint-clean but I have not re-run the AMD suite on this revision yet; I'll post MI300X results for test_function.py::test_ref, test_atomic_float_ops, and the new IR-shape test.


Hardware validation — done (AMD Instinct MI308X, gfx942, ROCm 7.2.4)

Built from source with the AMDGPU backend (-DQD_WITH_AMDGPU=ON -DQD_WITH_CUDA=OFF, ROCm clang 22, bundled LLVM 22.1.0) and ran the three tests at 4cda381a4:

test_function.py::test_ref[arch=amdgpu]                                              PASSED
test_atomic.py::test_atomic_float_ops[arch=amdgpu-{f16,f32,f64}-{add,sub,min,max}]   PASSED
test_amdgpu_global_as.py::..._promotes_flat_to_global[arch=amdgpu]                   PASSED

14 passed in 14.5s. The f16 cases ran (not skipped) — the device advertises 16-bit atomics, so Opus's f16-CAS path is actually exercised. test_ref confirms qd.ref(...) params still work with the get_struct_arg() Function-guard, and the new IR-shape test confirms flat_* -> global_* promotion is live.

I validated on an MI308X rather than an MI300X; both are gfx942 with the same ISA and 16-bit atomic support, so it's a faithful stand-in.

One build fix required (pushed as 4cda381a4): in get_struct_arg(), loaded was auto-deduced from CreateLoad() (→ LoadInst*), but maybe_tag_amdgpu_global_ptr() returns llvm::Value*, so reassigning the tagged pointer didn't compile under clang. Declared loaded as llvm::Value*. This was only caught by an actual AMDGPU build.

@paveltc
paveltc requested a review from hughperkins August 24, 2026 18:07
@hughperkins hughperkins removed the awaiting-contributor-action awaiting-contributor-action label Aug 24, 2026
maybe_tag_amdgpu_global_ptr() returns llvm::Value*, but `loaded` was
auto-deduced from CreateLoad() as LoadInst*, so reassigning the tagged
pointer failed to compile under clang ("incompatible pointer types
assigning to 'LoadInst *' from 'llvm::Value *'"). Declare `loaded` as
llvm::Value* so the addrspace-tagged result can be assigned back.

Caught building with the AMDGPU backend (ROCm clang 22) on gfx942.

Co-authored-by: Cursor <cursoragent@cursor.com>
@hughperkins

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 959fe317b5

ℹ️ 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".

Trim the address-space-at-source comments to document only the non-obvious
rationale (why addrspace(1) tagging plus InferAddressSpaces, and why real_func
callees are excluded), dropping restatements of the code and change history.
Remove comments deducible from the code (the tag call, the helper body, the
functional-sanity label) and keep only the gotchas: why the tag exists, why
MatrixPtr/atomic-CAS preserve addrspace, and why real_func callees are excluded.
@hughperkins

Copy link
Copy Markdown
Collaborator

@codex review

@hughperkins

Copy link
Copy Markdown
Collaborator

actually, I'll probably ignore any codex cometns, since it already came back green before, and it's hard for me to test any chagnes.

@hughperkins

Copy link
Copy Markdown
Collaborator

I think I'll just wait for amdgpu ci to pass, then merge.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: f06ecaeb79

ℹ️ 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".

@hughperkins
hughperkins merged commit 91c5905 into Genesis-Embodied-AI:main Aug 28, 2026
59 of 67 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants