Conversation
Enable building FastGeodis on AMD GPUs with ROCm PyTorch. The existing CUDA kernels work on HIP through PyTorch's build-time hipify mechanism with no kernel-level changes needed. Changes: - setup.py: Detect ROCm PyTorch via ROCM_HOME when CUDA_HOME is not set, enabling the GPU build path on AMD systems - .gitignore: Exclude *.hip files generated during the hipify process The port is entirely automatic: PyTorch's CUDAExtension invokes hipify to translate the CUDA source at build time. All 4 kernels (2D/3D geodesic distance transforms, row/plane raster scans) use only standard CUDA runtime APIs and block-level synchronization that map 1:1 to HIP, with no warp-level intrinsics or textures. This work was developed with the assistance of Claude, an AI assistant by Anthropic. Test Plan: Built and tested on AMD MI250 (gfx90a) with ROCm 7.2 and PyTorch 2.13: ``` HIP_VISIBLE_DEVICES=0 PYTORCH_ROCM_ARCH=gfx90a pip install -e . --no-build-isolation python -m pytest tests/ -v ``` Result: 300 passed in 71.80s, covering 2D/3D geodesic distance transforms on both CPU and GPU paths.
c10.dll built with clang does not export the inherited constructor c10::ValueError(SourceLocation, string) because MSVC does not re-export inherited constructors from dllimport bases even for C10_API classes. MSVC-compiled extension .cpp files that include <torch/extension.h> trigger TORCH_CHECK_VALUE which generates a dllimport reference to that constructor, causing LNK2001. Fix: add a Windows-only /ALTERNATENAME linker directive in setup.py that redirects the missing ValueError(SourceLocation, string) dllimport thunk to Error(SourceLocation, string), which IS exported by c10.dll. ValueError IS-A Error with no additional data members; the constructors are semantically identical. Authored with Claude (claude-sonnet-4-6) as part of AMD ROCm porting. Test Plan: # Windows gfx1201 (RX 9070 XT, RDNA4, wave32) HIP_VISIBLE_DEVICES=0 PYTORCH_ROCM_ARCH=gfx1201 ROCM_HOME=<venv>/_rocm_sdk_devel DISTUTILS_USE_SDK=1 python.exe -m pip install -e . --no-build-isolation HIP_VISIBLE_DEVICES=0 python.exe -m pytest tests/ -v # Result: 300 passed
Everything downstream of a review PR existing was already automated -- fetch it, snapshot who approved and against which commit, refuse to publish if a push or an edit overtook that approval, then open the upstream PR with the approved title and body verbatim. Nothing opened one. Twenty-seven ports are PR-ready with none open, which is the most wasteful state MOAT has. `--review` lists ports whose gates pass and that have no review PR yet; `--review --apply --name <p> --title <t> --body-file <f>` opens it on our own fork and records it, so `--publish` picks it up once a human clicks approve. It checks two things `pr_ready` does not. The title and body ARE the upstream PR's, so they are scanned for in-house vocabulary here, at the last point where fixing it is cheap. And the port branch must exist and differ from the base -- pr_ready never looked, so a fork with no port at all could present as ready to submit. That second check earned itself immediately: rmcl reports "no common ancestor between main and moat-port". Its port branch was built on unrelated history, so a pull request from it would show the entire tree as new and could not merge. It was sitting in the PR-ready list. Its state is left alone -- that is a porting problem, not bookkeeping. load_status now falls back to the refs, because 29 call sites take a project name and expect a record, and after the migration an in-flight project's folder is on its own branch. pr_ready was the one that crashed on bam; converting callers one at a time is how the rest end up quietly answering "not adopted" instead. save_status refuses a cross-branch write rather than creating a second copy, and names the branch to check out. Canary: AMD-Ecosystem/FastGeodis#1, moat-port -> master, 2 files. Body records 300/300 tests on four GPUs covering both wavefront widths. Test Plan: ``` python3 utils/upstream.py --review # 27 ready, 1 blocked (rmcl) python3 utils/upstream.py --review --apply --name FastGeodis \ --title "Add AMD GPU support via ROCm/HIP" --body-file <f> python3 utils/upstream.py --review | grep -c FastGeodis # 0, no longer offered python3 utils/jargon.py <body> # clean, before opening python3 utils/check.py --fast ``` Authored with Claude (Opus 5) as the AI assistant.
The FastGeodis review PR went out hand-wrapped at 80 columns. CLAUDE.md has said "do not manually line-wrap GitHub or markdown prose; let it reflow" for a long time, and the run that wrote that body broke it. A rule that exists only as prose is one nobody checks. GitHub reflows markdown to the reader's width, so frozen line breaks look wrong on a wide screen and worse on a phone, and every later edit reflows a whole paragraph into a diff nobody can read. utils/prose.py flags a line that continues a long previous line: not the start of a new block, following a line long enough that the break was not a choice. Fenced code is exempt, since breaks are the content there, and tables, lists and headings start their own blocks. Verified against the body actually shipped (caught, 4 lines) and against clean prose containing a table, a list and a code block (passes). Wired into both points where MOAT authors text GitHub will render: opening a review PR, and publishing upstream. The second is not redundant -- the approved body is republished verbatim, a maintainer may have asked for an edit, and an edit is where hand-wrapping creeps back in. Scope is deliberately narrow. It does not run over the repo's own markdown, which is wrapped throughout and read in an editor as often as on the web. AMD-Ecosystem/FastGeodis#1 has been rewritten unwrapped. `gh pr edit` fails on that repo with a Projects-classic GraphQL deprecation, so the body went through the REST endpoint instead -- worth knowing before the next one. Test Plan: ``` python3 utils/prose.py <the shipped body> # 4 hard-wrapped lines python3 utils/prose.py <clean sample> # clean, with table + list + fence gh api repos/AMD-Ecosystem/FastGeodis/pulls/1 --jq .body | python3 utils/prose.py - python3 utils/check.py --fast ``` Authored with Claude (Opus 5) as the AI assistant.
|
To approve this port, leave a review comment containing this line by itself: Use Review changes -> Comment, or The title and body above are what gets opened upstream, verbatim, so approving here approves all three: the code, the title and the body. Anything pushed afterwards, or any edit to the title or body, voids it and needs a fresh one. |
|
/moat approve |
|
Submitted upstream as masadcv#68 with the title and body approved here. Closing this review PR; the discussion continues on the upstream one. |
FastGeodis currently builds its GPU extension only when
CUDA_HOMEis set, so on a ROCm PyTorch install the extension silently falls back to CPU. This adds AMD GPU support.The change is small because PyTorch does most of the work: its build-time hipify translates
fastgeodis_cuda.cuon the way through, so no kernel source changes were needed and the CUDA path is untouched.setup.pyalso checksROCM_HOMEfromtorch.utils.cpp_extension, soBUILD_CUDAis true on a ROCm install.CUDA_HOMEbehaviour is unchanged..gitignoreignores the*.hipfiles PyTorch generates during a ROCm build./ALTERNATENAMElink flag maps thec10::ValueError(SourceLocation, string)import toc10::Error(SourceLocation, string). A clang-builtc10.dlldoes not export the inherited constructor, so MSVC callers fail with LNK2001;ValueErrorderives fromErrorand adds no members, so the redirect is safe. It is guarded tosys.platform == "win32"and does not affect Linux or CUDA builds.Tested with
python -m pytest tests/(300 tests: 2D and 3D geodesic distance transforms, signed and unsigned, GPU and CPU):Both wavefront widths are covered: 64 on gfx90a and 32 on the RDNA parts. No CUDA hardware was available to re-run the NVIDIA path, but no CUDA-side source is modified.
This work was done with the assistance of an AI coding agent.