MOAT ports popular CUDA GitHub projects to ROCm/HIP, one repo at a time. Coverage is expressed as gates -- wave64, wave32, windows -- so a port is proven once each gate has an architecture that validated it on real hardware. It is driven by AI coding agents running through either Claude Code or Codex: intake screens a candidate, a planner analyses it, a porter applies the change on a fork in the AMD-Ecosystem org, a reviewer checks it, and a validator runs the real tests on AMD GPUs. Submitting the result upstream, and everything that follows with a maintainer, stays with a person. This repo is the control plane; it tracks progress and holds the porting knowledge in the cuda-to-rocm skill.
Prefer pictures? VISUAL.md walks through all of this in twelve diagrams.
- If you maintain a project we might send a pull request to
- How it works
- What a person decides, and what agents may never do
- Suggest a project to port
- Licence
- Scope and honesty
- Projects
- Layout
Read this section and skip the rest; it is what concerns you.
Every pull request from this effort is written by an AI agent and read by a person before it is opened. It says so in its own body, along with a link back here. The person who approves it is approving the diff, the title and the body together, on one page, before any of it is visible on your repository.
You get at most one pull request per project. It is opened from a fork under the AMD-Ecosystem organisation against your default branch. There is no bot that follows up on a schedule -- every reply to you is a separate decision by a person, in a session they are sitting in front of.
We do not open it until the port has been built and its real test suite has been run on AMD hardware, on both wavefront widths (see the table below) -- that is where a CUDA port most often breaks. A compile is not evidence and does not count.
You can tell us to stop, and that is the end of it. No reason needed. Either:
- comment on any pull request of ours saying so, or
- open an opt-out issue naming your username, your organisation, or one repository.
An owner-level opt-out covers everything you own, including repositories nobody here
has looked at yet. It is recorded in data/optout.json, and it is
not a promise to remember: the record is enforced in three places in the tooling --
your repositories drop out of the candidate list, no project can be adopted from them,
and nothing can be submitted to them even if the port is already finished and approved.
Closing anything of ours still open and deleting the fork are done by a person, because
anything that touches your repository always is. Withdrawing an opt-out needs a person
on our side and someone asking on yours; an agent may carry that decision into the
record but never make it.
If a pull request is not how you would like to receive this, say that instead. Filing an issue with the findings and no code, or waiting for a release window, is a smaller ask than stopping and we would rather do it.
Five agents run in sequence, and each hands over through a state file rather than a conversation, so either supported agent harness on any host can pick up where the last one stopped: intake screens a candidate for licence and viability, planner analyses the build system and the CUDA surface, porter writes the port on the fork, reviewer reads the diff, and validator builds it and runs the project's own tests on a real AMD GPU. The reviewer and the validator can both send it back to the porter. What happens after approval -- submitting the pull request, maintainer rounds, merge -- is not a pipeline stage, because every step of it ends at a person. The submission itself runs only in an attended session; an agent may run that one command, since what it publishes was already approved wholesale on the review PR.
Each project gets a folder under projects/ holding its plan, notes, and a per-platform status file. A fresh Claude Code or Codex run in this repo detects its AMD architecture, finds the next actionable project, and continues the pipeline. No platform leads: whichever host picks the project up first does the porting, and the rest validate the same fork branch independently and in parallel, since the AMD targets share one unified ROCm port.
A platform is an architecture on an operating system, and it carries the gates implied by both -- its wavefront size, which is fixed by the architecture, and its OS. The same GPU on a different OS is a different platform covering different gates. A gate is satisfied once any one platform carrying that attribute has validated, so the gates can be covered by as few as two hosts.
Four decisions are held by a person. Each is enforced by the tooling rather than by an instruction an agent is asked to remember, and none of them is a checkbox an agent can tick on its own.
| the decision | how it is made | what an agent may do |
|---|---|---|
| Is this project's licence one we may contribute to? | Two tiers are cleared in advance (config/licenses.toml): permissive licences, and the copyleft licences approved for contributing to third-party projects; anything else waits for a named person, one project at a time |
Read the licence and record which one it is. That is a fact. The clearance is not, and a record without a name satisfies nothing |
| Do we take this project up at all? | Someone creates the fork in the AMD-Ecosystem org. Agents have no permission to create one, so its existence is the decision and nothing else has to record it | Write the case for and against, and recommend |
| Do we decline it? | Screens collect into one issue, a person answers in prose, and merging a small pull request recording the declines is the record -- approving is impossible on a self-authored pull request, and the merge carries the same actor and timestamp | Write the case. Never the verdict |
| Does this port go upstream, exactly as written? | A review pull request on our own fork carries the diff, the title and the body together. A person approves it there, and their approval is bound to that commit and that text | Open the review pull request and read the answer. It may never write the approval line |
The last one is the gate that matters to a maintainer receiving this. An approval covers what was on screen when it was given: a commit pushed afterwards, or an edit to the title or body, voids it and the submission step refuses. That check compares GitHub against GitHub -- the commit the review was attached to against the branch tip now -- because the local record is a file agents can write and the approval is not.
Opening the upstream pull request is one command run by a person in their own session, never by an unattended job.
Anyone can ask for a CUDA project to be ported: file a port request. It lands in the same queue our own discovery feeds, and a person decides whether to take it up before any work starts.
MIT, matching the ROCm projects this work targets. See LICENSE. The ports themselves live on forks and stay under each upstream project's own licence; nothing in this repository changes that.
The project list below is a best-effort ranked union of targeted GitHub searches, not a census of every CUDA repo (GitHub search caps results per query and misses repos whose dominant language is not Cuda). Ports aim to be minimally invasive: for pure CMake projects we prefer enable_language(HIP) plus a single cuda-to-hip compat header; for pytorch extensions we rely on torch's build-time hipify. A CPU-only build smoketest proves compilation only; correctness is gated on real-GPU test runs. See the cuda-to-rocm skill.
Projects we will not port (already ported, already supported, can't be ported, or not a real target) are recorded with reasons in data/dispositions.json and kept out of the actionable list; utils/triage.py manages those decisions.
Coverage is proven per gate, not per machine. A gate is met once any AMD GPU with that property has run the project's real test suite. Which specific card did it is recorded in each project's status file.
| gate | what it covers |
|---|---|
wave64 |
data-center cards -- Instinct MI200/MI300 class (CDNA), 64 threads per wavefront |
wave32 |
desktop and workstation cards -- Radeon RX / PRO (RDNA), 32 threads per wavefront |
windows |
the port builds and runs on Windows, not only Linux |
A wavefront is AMD's analogue of an NVIDIA warp, which is always 32 threads. Code that silently assumes 32 is the single most common way a CUDA port breaks on AMD, so the two widths are proven separately rather than assumed to follow from each other.
| cell | meaning | outcome | meaning | |
|---|---|---|---|---|
| β | proven on the current code | π£ | contribution merged upstream | |
| π | proven earlier; the code has moved since | π’ | pull request open | |
| π§ | in progress | π΄ | pull request closed | |
| β¬ | not started | βοΈ | licence bars contributing the port | |
| π« | blocked, with a reason recorded | βͺ | set aside, with the reason recorded | |
| π« | waived for this project, with maintainer approval | βΈ | on hold, deliberately not being worked | |
| β | nothing recorded | β | nothing recorded |
The project name links upstream.
| Project | wave64 |
wave32 |
windows |
Outcome |
|---|---|---|---|---|
| 3DGS-LM (fork) | β | β | β | π’ #15 |
| 3DUNDERWORLD-SLS-GPU_CPU (fork) | π§ | π | π | π’ #33 |
| 3P-ADMM-PC2 (fork) | β | β | β | π£ #10 |
| accelerated-scan (fork) | β | β | β | π’ #17 |
| aihwkit (fork) | β | β | β | π’ #770 |
| alien (fork) | β | β | π | π£ #710 |
| amgcl (fork) | β | β | β | π£ #315 |
| anari-visionaray (fork) | β | β | β | π’ #10 |
| arbor (fork) | β | β | β | π£ #2512 |
| arrayfire (fork) | π§ | β | β | π’ #3708 |
| AutoDock-GPU (fork) | β | β | β | π’ #320 |
| bam (fork) | π« | π« | π« | βΈ on hold |
| barney (fork) | β | β | β | π£ #46 |
| baspacho (fork) | β | β | β | π’ #10 |
| bellhopcuda (fork) | β | β | β | π’ #65 |
| brian2cuda (fork) | β | β | π | π’ #327 |
| catboost (fork) | β | β | β | π’ #3111 |
| colmap (fork) | β | β | β | β |
| CPM.cu | π« | β | β | βͺ not-portable |
| CubbyFlow (fork) | β | β | β | π’ #145 |
| cuBQL (fork) | β | β | β | π£ #35 |
| cubvh (fork) | β | β | β | π£ #33 |
| cuda-efficient-features (fork) | β | β | β | π’ #3 |
| CUDA-L2 | π« | β | β | βͺ not-portable |
| CUDA-ScanMatcher-ICP (fork) | β | β | β | π’ #10 |
| cuda_voxelizer | β¬ | β¬ | β¬ | β |
| cudaKDTree (fork) | β | β | β | π£ #40 |
| CudaSift (fork) | β | β | β | π’ #97 |
| CuMesh (fork) | π§ | π | π | π’ #36 |
| cuPDLP-C (fork) | β | β | β | π’ #41 |
| cuPDLPx (fork) | β | β | π | π£ #94 |
| cupoch (fork) | β | β | β | π£ #143 |
| CuRast (fork) | β | β | β | π’ #2 |
| cuSZ (fork) | β | β | β | π’ #91 |
| CV-CUDA (fork) | β | β | π« | π’ #293 |
| DEM-Engine (fork) | β | β | β | π’ #69 |
| dgSPARSE-Lib (fork) | β | β | β | π’ #39 |
| dietgpu (fork) | β | β | π | β |
| diff-surfel-rasterizations (fork) | β¬ | β¬ | β¬ | β |
| diff-surfel-tracing (fork) | β¬ | β¬ | β¬ | β |
| DiffPhysDrone (fork) | β | β | β | π’ #45 |
| DynOSAM | β¬ | β¬ | β¬ | β |
| egg.c (fork) | β | β | β | π’ #8 |
| ElasticFusion (fork) | β | β | β | β |
| EnvGS (fork) | β | β | β | β |
| espresso (fork) | β | β | π« | β |
| evogp (fork) | β | β | β | π£ #12 |
| faiss (fork) | β | β | β | β |
| FaithC (fork) | β | β | β | π’ #12 |
| Fast-Poisson-Image-Editing (fork) | β | β | β | π£ #25 |
| faster-gaussian-splatting (fork) | β | β | π | β |
| FastGeodis (fork) | β | β | β | π’ #68 |
| fdtd3d (fork) | β | β | β | β |
| ffpa-attn (fork) | β | β | β | π£ #268 |
| FLAMEGPU2 (fork) | β | β | β | β |
| FlashKDA | β¬ | β¬ | β¬ | β |
| FlashMoE | π« | β | β | βͺ not-portable |
| FlashRT | π« | β | β | βͺ not-portable |
| foldmason (fork) | β | β | β | β |
| foldseek (fork) | β | β | β | β |
| gaussian_splatting (fork) | β | β | β | π’ #37 |
| gdtk (fork) | β | β | β | β |
| GOMC (fork) | β | β | β | β |
| GooFit (fork) | π§ | β | β¬ | β |
| gpu4pyscf (fork) | β | β | π« | β |
| GPU_IPC (fork) | β | β | β¬ | β |
| Gpufit (fork) | β | β | β | π’ #149 |
| GPUMD (fork) | β | β | β | π£ #1538 |
| gpuRIR (fork) | β | β | β | β |
| gRASPA (fork) | β | β | β | β |
| gtsam_points (fork) | β | β | β | π’ #99 |
| h2o4gpu | β¬ | β¬ | β¬ | β |
| HEonGPU (fork) | β | β¬ | β¬ | β |
| icicle (fork) | β | β | π« | β |
| k2 (fork) | β | β | β | π£ #1353 |
| kaldi (fork) | β | β | π« | π’ #4986 |
| kaldifeat (fork) | β | β | β | β |
| KittenGpuLBVH (fork) | β | β | β | π’ #5 |
| LC-framework (fork) | β | β | β¬ | β |
| lc0 (fork) | β | β | π« | π’ #2420 |
| LEAP (fork) | β | β | β | β |
| libSGM (fork) | β | β | β | π’ #89 |
| LichtFeld-Studio (fork) | β | β | π« | β |
| LiteGS (fork) | β | β | β | β |
| llm-awq (fork) | β | β | β | β |
| llm.c (fork) | β | β | β | π’ #854 |
| llmq | π« | β | β | βͺ not-portable |
| mahout (fork) | β | β | π | π’ #1399 |
| marian-dev (fork) | β | β | π | π’ #1043 |
| MASt3R-SLAM (fork) | β | β | β | β |
| mcx (fork) | β | β | β | π£ #264 |
| metaeuk (fork) | β | β | β | β |
| mHC.cu (fork) | β | β | β | β |
| MMseqs2 (fork) | β | β | β | β |
| MPPI-Generic (fork) | β | β | β | β |
| mumax3 (fork) | β | β | β | π’ #400 |
| ntransformer (fork) | β | β | β | β |
| OCTproZ (fork) | β | β | β | β |
| ohm (fork) | β | β | β | β |
| oneflow (fork) | β | β | π« | β |
| op43dgs (fork) | β | β | β | β |
| Open3D (fork) | β | β | π | π’ #7509 |
| opencv (fork) | β¬ | β¬ | β¬ | β |
| opencv_contrib (fork) | β | β | β | π’ #4147 |
| PhoenixOS | π« | β¬ | β¬ | βΈ on hold |
| plumed2 (fork) | β | β | π« | β |
| plvs (fork) | β | β | π« | β |
| Pointcept (fork) | β | β | β | π£ #604 |
| popsift (fork) | β | β | β | π’ #186 |
| prismatic (fork) | β | β | β | β |
| pytorch3d | β | β | β | π£ #2039 |
| qrack (fork) | β | β | β | β |
| Quest (fork) | β | β | β¬ | β |
| QUICK (fork) | β | β | π« | β |
| rmagine (fork) | β | β | β | β |
| rmcl (fork) | β¬ | β¬ | β¬ | β |
| RWKV-CUDA (fork) | β | β | β | β |
| RXMesh (fork) | β | β | β | π’ #73 |
| SCAMP (fork) | β | β | β | π’ #145 |
| SpargeAttn | π« | β | β | βͺ not-portable |
| spconv | β¬ | β¬ | β¬ | β |
| splatad (fork) | β | β | β | π’ #24 |
| sppark (fork) | β | β | β | π’ #82 |
| stdgpu (fork) | β | β | β | π£ #484 |
| symforce (fork) | β | β | β | π’ #465 |
| TIGRE (fork) | β | β | β | π’ #747 |
| tiny-vllm (fork) | β | β | β | π£ #2 |
| torch-linear-assignment (fork) | β | β | β | π’ #31 |
| TornadoVM | β¬ | β¬ | β¬ | β |
| tsne-cuda | β¬ | β¬ | β¬ | β |
| TTT3R (fork) | β | β | β | β |
| TurboFNO (fork) | β | β | β | π£ #3 |
| unified-cache-management (fork) | β | β | π | π’ #1021 |
| Velvet (fork) | β | β | β | π’ #9 |
| visionaray (fork) | β | β | β | π£ #51 |
| yalm (fork) | β | β | β | π’ #12 |
| YarnBall (fork) | β | β | β | π’ #5 |
| ZhiLight (fork) | β | β | π« | π’ #81 |
See VISUAL.md for a diagram walkthrough of everything above, projects/README.md for the per-project files, the cuda-to-rocm skill for porting strategy and fault classes, and AGENTS.md for how either supported agent harness drives the pipeline.