# Comgr Project Conventions Conventions for working in `amd/comgr/`. Apply these to all changes under this directory. This file is the single source of truth for general Comgr project conventions. The agent-tool wrappers (`amd/comgr/CLAUDE.md`, `.cursor/rules/comgr.mdc`) point here so updates only need to be made once. For hotswap-subsystem-specific conventions (patch-pass authoring, B0/A0 rewrite invariants, hotswap test driver), see [`src/hotswap/HOTSWAP_CONVENTIONS.md`](src/hotswap/HOTSWAP_CONVENTIONS.md). ## 1. Code reuse — Comgr first, LLVM second, custom never **Reuse existing Comgr APIs before writing new ones.** Notable hits: - `parseTargetIdentifier()` (`src/comgr.cpp:231`) — parses an ISA string (`amdgcn-amd-amdhsa--gfx1250:sramecc+`) into arch / vendor / OS / environ / processor / features. Don't re-implement string parsing for ISA names. - `ensureLLVMInitialized()` (`src/comgr.cpp:275`) — initializes the AMDGPU target stack with thread-safety. Don't roll your own `std::call_once` over `LLVMInitializeAMDGPU*` calls; reuse this. - `DisassemblyInfo::create()` (`src/comgr-disassembly.cpp:25`) — sets up the full MC stack (Target, MRI, MAI, MCII, STI, MCContext, MCDisassembler, MCInstPrinter). If a new feature needs a similar bundle, refactor `DisassemblyInfo` to share, don't duplicate. - Small refactors of existing Comgr APIs are **preferred over parallel implementations**. If extracting a shared helper enables your reuse story, do that. **Reuse existing LLVM APIs second**, especially the MC layer: - `MCCodeEmitter::encodeInstruction` for instruction encoding. - `MCRegisterInfo::regsOverlap` for register overlap checks. - `llvm::AMDGPU::*` from `llvm/TargetParser/TargetParser.h` for AMDGPU target queries (`parseArchAMDGCN`, `getArchNameAMDGCN`, etc.). - `llvm::object::ELFFile<>` for ELF parsing — don't hand-roll section or symbol iteration. - `llvm/Support/Compiler.h` macros for portable attributes. **For upstream LLVM APIs that need small reworks** to be usable here: add a `TODO` comment in the Comgr code **and** file a GitHub issue to fix upstream. Do not implement a parallel version inside Comgr. ## 2. Code quality - Follow LLVM coding guidelines (`BasedOnStyle: LLVM` in `.clang-format`). Run `clang-format` on changed files before submitting. - Apply upstream LLVM code review standards (small focused commits, meaningful commit messages, no unrelated changes). - **Avoid Windows-hostile code**: - Use `LLVM_ATTRIBUTE_WEAK` (from `llvm/Support/Compiler.h`), not `__attribute__((weak))`. MSVC does not understand the GCC syntax and will fail to build. - No GCC/Clang-only attributes without an LLVM-portable wrapper. - All assembly / disassembly goes through the MC layer (e.g., `assembleSingleInst`, `assembleInstructions`, `parseAsmToMCInsts`). **No hardcoded instruction opcodes or encoded byte sequences** — let the asm parser resolve them, and round-trip through `MCCodeEmitter::encodeInstruction` for any modification. - When invoking the asm parser, register the SourceMgr with `MCContext` via `MCContext::initInlineSourceManager()` so error diagnostics on bad input don't crash with `Either SourceMgr should be available`. - **Mnemonic identity is asm-level, not tablegen-level.** `MCInstrInfo::getName(Opcode)` returns the tablegen pseudo name — on gfx1250 the assembled `v_nop` has opcode name `V_NOP_e32_gfx12`, not `V_NOP_e32`. Resolve opcodes once at init via the asm parser, cache the resolved opcode / `MCInst`, and compare against the cached value. Verify any new mnemonic comparison with `llvm-mc -show-inst` against the target you care about. **Style.** Comgr follows the canonical LLVM coding conventions; for the full reference Read these source files (in this monorepo): - [`llvm/docs/CodingStandards.rst`](../../llvm/docs/CodingStandards.rst) - [`llvm/docs/AMDGPU/DeveloperGuideline.rst`](../../llvm/docs/AMDGPU/DeveloperGuideline.rst) Comgr-specific deviations and items recurring in code review that aren't covered upstream: - Avoid `auto`. Comgr leans stricter than upstream LLVM here — spell types out, including iterator types. - ASCII only in source comments — no box-drawing dividers, no smart quotes, no em-dashes. - Pass `MCRegister` (not `unsigned`) until you need the encoded id. **Error-return policy.** Don't mix signaling styles within one PR. - Public C API: `amd_comgr_status_t`. - Internal helpers: `bool` only when there is one meaningful failure mode; otherwise `std::optional` or `llvm::Expected`. - **No silent returns on failure.** Every failure path emits a specific `log()` message naming what was attempted and why it failed. - Don't return a count where 0 conflates "no candidates" with "found candidates but couldn't process them" — that distinction matters to downstream callers. ## 3. Testing Comgr has three test suites: - `test/` — legacy CTest C-based tests. - `test-lit/` — LIT integration tests with `comgr-sources/` tool binaries. - `test-unit/` — newer gtest-based unit tests. **Prefer LIT tests** over gtests where the public API is reachable. Compile a kernel with `%clang`, pipe through a LIT tool driver, verify with `%llvm-objdump` / `%llvm-readelf` / `%FileCheck`. - LIT inputs should be compiled with **`%clang` directly**, not through Comgr actions (`AMD_COMGR_ACTION_ASSEMBLE_SOURCE_TO_RELOCATABLE` etc.). Going through Comgr actions implicitly tests the Comgr compiler pipeline alongside whatever the test is checking, which makes failures harder to attribute. - **Reuse existing tools in `test-lit/comgr-sources/`** rather than adding parallel drivers. Extend an existing tool if needed (e.g., adding a new flag). Add a new tool only when an existing one is genuinely a bad fit. - When extending a shared driver, **unknown-flag handling rejects with an error**. Silent pass-through turns typoed RUN lines into false negatives. - gtests in `test-unit/` are appropriate for: - Pure functions with bit-level edge cases (e.g., encoding limits). - Internal helpers not reachable through any public API path. `make check-comgr` runs all three suites; the per-suite targets (`make test`, `make test-lit`, `make test-unit`) run them individually. **Verify under AddressSanitizer before submitting.** Comgr builds with `-DADDRESS_SANITIZER=On` (see `CMakeLists.txt`). Re-run `make check-comgr` against the ASAN build to catch use-after-free, leaks, and other memory bugs that won't surface in a normal release build. ## 4. PR workflow **One feature per PR.** Split by file-of-truth — one new `comgr-*-X.cpp` per PR. Refactors of pre-existing Comgr code go in their own PR; don't bundle a refactor (e.g. of `DisassemblyInfo`) into a feature PR. **Tests required.** Each PR is accompanied by tests aiming for 100% code coverage of the change being added. **Keep the branch rebased.** Resolve conflicts before requesting review. **Deferral protocol.** When a reviewer asks for a structural change that would require touching code outside the PR scope: 1. Acknowledge the direction. 2. File a tracking issue and link it inline in the comment thread. 3. State the trigger condition for picking it up. Deferring without a tracker is the failure mode — the concern gets silently lost. ## 5. Working as an agent These items address fingerprints that have surfaced in code review. Self-audit your diff before pushing. **Cite, don't claim.** Don't make "verified empirically" or "I confirmed" claims about LLVM internals (parser behavior, codegen choices, ABI specifics) without a citation — a link to an MC test, a TableGen `.td`, or actual `llvm-mc` output. If you can't produce one, downgrade the claim to "would need to verify" and verify before letting it become an architectural justification. **Self-audit for over-decomposition.** Grep your diff for one-line helpers that wrap a single call (a 4-byte `memcpy` wrapped as a named function; a `printInst` with one caller). If the helper has one caller and is one operation, inline it. **Self-audit for defensive checks against impossible conditions.** "Defensive against tablegen / decoder corruption" is not a justifiable guard — if the disassembler is producing garbage, the patch can't recover. `REQUIRES:` LIT gates that can never fail are dead code. Delete checks that can't fire. **Self-audit for duplicated `llvm/` content.** Before adding a constant, table, X-macro, or helper, grep `llvm/include/llvm/{Object,BinaryFormat,Support,MC}/` and `llvm/lib/Target/AMDGPU/Utils/` for the same content. If it exists and is reachable, use it; if it exists but is target-internal, file a tracking issue and add a `TODO` linking it.