Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

12 KiB

rocKE — C++ engine style guide

Conventions for the rocke C++ engine (platform/cpp/). Like the Python guide, these rules are derived from the existing code: the goal is that new code is indistinguishable from what is already here.

Scope. This guide governs the rocke C++ engine under platform/cpp/ — the C99-style, extern "C" port that mirrors the Python authoring frontend and emits byte-identical LLVM IR. It is self-contained; follow it for all engine C++.

If a rule here conflicts with AGENTS.md, AGENTS.md wins — it owns the hard invariants (byte-identity, relative paths, cross-platform).


0. The paradigm (read this first)

The engine is a C99-style port of the Python engine, exposed through a flat extern "C" ABI (nearly all public headers declare extern "C"). C++ is used only at the boundary (exceptions/guards in ckc::, .hpp). Every translation unit mirrors a specific Python module 1:1 — file headers literally say "bucket N of the C99 port of rocke.core.ir" and carry a Python↔C99 correspondence table. Two consequences drive everything below:

  • Byte-identity is the #1 invariant. Each C++ TU must emit the same LLVM-IR bytes as its Python twin. Mirror every emission change in both engines and re-run the gate from platform/ (build the C++ engine first — see AGENTS.md "Build / test / run"): python tools/check_byte_identity.py. A "style" cleanup that changes emitted text is not a style change.
  • The ABI stays C-callable. Prefer C-expressible constructs on the public surface; keep C++ features to the internal boundary layer.

1. Cardinal rules (never break)

Compliance (export controls, restricted data, NPI, product/marketing/code names, performance data, legal/marketing, internal links) is defined in AGENTS.md "Compliance" — the source of truth, not restated here. Two file-level rules AGENTS.md does not cover:

  • MIT license header only — every file starts with the AMD copyright + SPDX-License-Identifier: MIT. No other license text.
  • ASCII only in anything that can be serialized for codegen (IR text, hipRTC).

2. Files & headers

  • .h for headers, .cpp for sources. .hpp is reserved for the small C++-only boundary headers (e.g. include/rocke/error.hpp). Do not introduce .hpp for C-ABI headers, and there is no _impl.hpp template split (the engine is not template-heavy).
  • #ifndef include guards, named ROCKE_<PATH>_H (matching the file). The engine does not use #pragma once.
  • File preface order: MIT copyright, then a block comment describing the file's role and its Python correspondence, then the guard. The correspondence block is the house style — an ASCII table (use ->, not a Unicode arrow) mapping each Python symbol to its C99 counterpart, e.g.:
    Python (target.py)            C99 (this file)
    ArchTarget.from_gfx(gfx)  ->  rocke_arch_target_from_gfx()
    known_arches()            ->  rocke_known_arches()
    
    See include/rocke/arch_target.h for a full example. A brand-new file with no Python twin may omit the table but must still state its role.
  • Public headers wrap declarations in extern "C":
    #ifndef ROCKE_ARCH_TARGET_H
    #define ROCKE_ARCH_TARGET_H
    #include <stdbool.h>
    #include "rocke/ir.h"
    #ifdef __cplusplus
    extern "C" {
    #endif
    /* ... declarations ... */
    #ifdef __cplusplus
    }  /* extern "C" */
    #endif
    #endif  /* ROCKE_ARCH_TARGET_H */
    
  • Files terminate with a newline; no trailing whitespace (clang-format enforces).
  • Keep files reasonably sized; the port is already split into per-concern "buckets" (core/ir/ir_arith.cpp, ir_mem.cpp, ir_tile.cpp, …) — follow that granularity.

3. Includes

  • Order: the translation unit's own header first, then a blank line, then C/C++ system headers in <...> (e.g. <stdint.h>, <string.h>), then a blank line, then other project headers in "rocke/..." (see core/ir/serialize.cpp). SortIncludes only sorts within a blank-line-separated group; it does not move <...> ahead of "..." (no IncludeBlocks: Regroup is configured), so the group order is a manual convention, not auto-enforced.
  • Project includes are spelled "rocke/<name>.h" — headers live in cpp/include/rocke/ and cpp/include/ is on the include path, so #include "rocke/ir.h", #include "rocke/ir_internal.h". No relative ../ paths, no absolute paths.
  • Include what you use. The C99 port is deliberately libc-only in many places (e.g. arch data is embedded as static tables, no JSON parser). Reserve C++ STL (<string>, <exception>) for the ckc:: boundary layer, not the extern "C" TUs.

4. Naming

The engine is snake_case with layered prefixes, driven by the flat C-ABI and the 1:1 Python mirror.

Kind Convention Example
Public ABI / free functions rocke_<layer>_<verb> snake_case rocke_b_op, rocke_ir_parse, rocke_ll_lower_kernel
Internal helpers rocke_i_* rocke_i_emit, rocke_i_set_err
Types rocke_<name>_t rocke_ir_builder_t, rocke_arch_target_t, rocke_mma_op_t
Enums / status / caps ROCKE_UPPER_SNAKE ROCKE_OK, ROCKE_ERR_VALUE, ROCKE_ERR_MSG_CAP
Macros & constants ROCKE_UPPER_SNAKE ROCKE_ERR_MSG_CAP
C++ boundary namespace ckc (lowercase) namespace ckc
C++ boundary types PascalCase ckc::Error, ckc::ValueError, ckc::NotImplError
C++ boundary functions snake_case ckc::raise_status, ckc::guard_builder, ckc::format_error
Files / directories snake_case, mirroring Python layers core/lower_llvm/mma.cpp
  • The prefix encodes the layer/module and is load-bearing. Match the prefix to the directory you're editing:

    • core/ir/ → rocke_ir_*, rocke_op_*, rocke_attr_*, rocke_value_*; builder entry points rocke_b_*, internal helpers rocke_i_*
    • core/lower_llvm/ → rocke_ll_* (shared lowering helpers rocke_lower_*)
    • helpers/ → rocke_h_*; string-buffer utilities → rocke_strbuf_*

    When unsure, grep a sibling file in the same directory and match it.

  • Names mirror their Python counterpart where one exists (ArchTarget.from_gfx → rocke_arch_target_from_gfx); keep the mapping obvious.

5. Formatting (owned by .clang-format — do not hand-fight it)

The authoritative config is dnn-providers/hip-kernel-provider/.clang-format (WebKit-based; inherited — clang-format walks up from cpp/, there is no local copy under platform/ or cpp/). It is already hook-enforced: the repo-root .pre-commit-config.yaml runs clang-format on .c/.cpp/.h/.hpp repo-wide, so these are auto-applied — do not hand-fight them. Key points:

  • 4-space indent, no tabs, 100-column limit.
  • Allman braces — opening { on its own line for functions, classes, structs, enums, namespaces, and control statements; else on its own line.
  • No namespace indentation (NamespaceIndentation: None); align left.
  • PointerAlignment: Left (int* p), SpaceBeforeParens: Never (foo(x), if(x)), BinPackArguments/Parameters: false (one per line when wrapping).
  • Run clang-format before committing; never reformat to something it would undo.

6. Error model (the engine's core contract)

Follow the two-channel model exactly (include/rocke/error.hpp, core/ir/core_builder.cpp):

  • Internal C++ code throws a ckc::Error subclass where the Python reference would raise — ckc::ValueError ↔ ValueError, ckc::TypeError, ckc::KeyError, ckc::OOMError, ckc::NotImplError. Use [[noreturn]] raise helpers (rocke_i_set_err, ckc::raise_status).
  • Public extern "C" entry points catch at the boundary via ckc::guard_builder / ckc::guard_status and translate to the legacy rocke_status_t + a sticky, ROCKE_ERR_MSG_CAP-bounded message on the builder. Never let a C++ exception escape the ABI. (The actual symbols are ckc::guard_*; a stale doc comment in error_boundary.hpp calls them rocke_guard_* — ignore that.)
  • Error message text must stay byte-identical to the Python reference — parity and validation reject-reasons are compared verbatim. Format with ckc::format_error (printf-style, bounded); keep existing format strings unchanged.
  • Map failure kinds to the matching status: ROCKE_ERR_VALUE for bad input, ROCKE_ERR_NOTIMPL for unsupported-but-valid paths.

7. Memory & ownership

  • Arena allocation. IR nodes live in the builder's arena and are bulk-freed by rocke_ir_builder_free; do not manually free arena-owned nodes. Unwinding past in-progress builder calls leaks nothing because the arena is bulk-freed at the entry boundary.
  • Borrow vs own must be documented. Static SSOT tables (arch data) return borrowed pointers valid for program lifetime; say so in the header, as arch_target.h does. State ownership and lifetime for any pointer an ABI function returns or stores.

8. Types, structs, enums, comments

  • Prefer plain struct for POD-ish descriptor/value types (rocke_*_t); most engine state is an opaque handle behind the ABI (few top-level struct/class defs live in headers).
  • Plain enum with ROCKE_-prefixed UPPER_SNAKE values (the flat C-ABI needs C enums).
  • Comments focus on why and on ownership/lifetime/error-model, not what. The file-level block comment (role + Python correspondence table) is expected on non-trivial TUs. Use C-style /* */ for block/file comments and // inline. Doxygen is not used in the port (it is a C-ABI mirror); use plain comments. Use TODO: / FIXME: / NOTE: tags for future work.
  • C++ standard is C++20 (CMAKE_CXX_STANDARD 20); C++20 features (attributes, exceptions, <string>) are fine in the ckc::/.hpp boundary. Do not push C++-only constructs onto the flat C-callable surface.

9. A minimal conforming file

A new ABI entry point, header + source (bodies elided). Verify the exact type/function names against the current headers before copying — this is a shape guide.

include/rocke/foo.h

/* Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
 * SPDX-License-Identifier: MIT
 *
 * rocke/foo.h -- PUBLIC API for the C99 port of rocke.core.foo.
 *
 * Python (foo.py)     C99 (this file)
 * do_foo(b, x)   ->   rocke_foo_do(b, x)
 */
#ifndef ROCKE_FOO_H
#define ROCKE_FOO_H

#include <stdint.h>

#include "rocke/ir.h"

#ifdef __cplusplus
extern "C" {
#endif

rocke_value_t* rocke_foo_do(rocke_ir_builder_t* b, rocke_value_t* x);

#ifdef __cplusplus
}  /* extern "C" */
#endif
#endif  /* ROCKE_FOO_H */

core/foo.cpp

// Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
/*
 * foo.cpp -- C99 port of rocke.core.foo.
 */
#include "rocke/foo.h"

#include <stdint.h>

#include "rocke/ir_internal.h"

rocke_value_t* rocke_foo_do(rocke_ir_builder_t* b, rocke_value_t* x)
{
    if(!rocke_i_live(b))
    {
        return NULL;
    }
    /* ... emit ops via rocke_b_* / rocke_i_* ... */
    return x;
}

Quick checklist for a new C++ engine change

  • MIT copyright + SPDX-License-Identifier: MIT; ASCII only
  • .h + #ifndef ROCKE_*_H guard; extern "C" wrapper on public headers
  • File block comment states role + Python correspondence
  • Includes: own header → <system> → "rocke/..."; no ../ or absolute paths
  • snake_case rocke_<layer>_* names; rocke_*_t types; ROCKE_* enums/macros
  • clang-format clean (4-space, 100-col, Allman braces)
  • Errors: throw ckc::Error internally, guard+translate at the ABI, messages byte-identical to Python
  • Ownership/lifetime documented for returned/stored pointers; arena nodes not hand-freed
  • Emission changed? mirror in Python + re-run tools/check_byte_identity.py (from platform/)
  • Engine code stays C-callable; C++ features confined to ckc::/.hpp