Skip to content

Commit eda31f8

Browse files
committed
docs: accelerators chapter, and the manifest keys it introduces
Chapter 20 states the two shapes an accelerator toolchain takes, says which one mcpp implements, and gives the reasoning for the three decisions a reader will otherwise find arbitrary: why device sources are excluded from the default glob, why the seam module is an architectural unit rather than a workaround for nvcc's lack of module support, and why the accelerator dimension travels beside the compatibility tag instead of inside it. The manifest reference gains [build] accel and [package] accelerators, kept apart on purpose: one is measured from a build, the other is written by hand.
1 parent 866909a commit eda31f8

6 files changed

Lines changed: 415 additions & 0 deletions

File tree

docs/05-mcpp-toml.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,22 @@ Linux and macOS perform no such conversion, so nothing is skipped there. A
791791
package that builds on one and not the other, with an
792792
`internal: unhandled exception` from a code-page message, was mcpp#516.
793793

794+
### 2.3.1 `[build] accel` — the accelerator this build targets
795+
796+
```toml
797+
[build]
798+
accel = "cuda12.8+{sm_80,sm_90f} ptx>=90"
799+
```
800+
801+
Which device backends and architectures this build compiles for. Overridden for
802+
one build by `--accel`, the relationship `--target` has with `[toolchain]`;
803+
`--no-accel` requests none explicitly, which is how a CPU-only variant of a
804+
package that also publishes device builds is selected.
805+
806+
The value is compared against the `accel` field of any prebuilt artifact the
807+
build consumes, and a build asking for none is satisfied by every artifact. See
808+
[20 — Accelerators](20-accelerators.md).
809+
794810
### 2.4 `[lib]` — Library Root Module Convention
795811

796812
```toml
@@ -1866,6 +1882,21 @@ built" means is the same question `--target` answers (docs/08 §7.4).
18661882
Both are warnings, never errors: coverage is release discipline, and the person
18671883
who can judge it is looking at the release, not at this build.
18681884

1885+
### 2.12b `[package] accelerators` — Accelerator Declaration
1886+
1887+
```toml
1888+
[package]
1889+
accelerators = ["cuda", "rocm"]
1890+
```
1891+
1892+
Declares the accelerator backends the package supports. Mirrors `platforms`: a
1893+
statement of intent and a CI-matrix hint, shown by `mcpp why`, never a gate.
1894+
1895+
Distinct from an artifact's `accel` field on purpose. A declaration is written
1896+
by hand and may be aspirational; `accel` is measured from the build that
1897+
produced a binary and is what a consumer is refused against. See
1898+
[20 — Accelerators](20-accelerators.md).
1899+
18691900
### 2.13 `[xlings]` — the project's environment
18701901

18711902
```toml

docs/20-accelerators.md

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# 20 — Accelerators
2+
3+
How mcpp builds device code, and how a prebuilt artifact states which devices
4+
it can run on.
5+
6+
## Two shapes, one of which mcpp implements today
7+
8+
Accelerator toolchains come in two shapes, and they are not variations of one
9+
model.
10+
11+
An **island** keeps device code in separate translation units compiled by a
12+
separate compiler. CUDA, HIP, Ascend C and Metal all work this way. The device
13+
compiler produces an object (or, for shading languages, a runtime resource)
14+
that joins the ordinary link.
15+
16+
A **whole-target** model puts device code in ordinary `.cpp` files and compiles
17+
the entire target with a compiler capable of offloading. SYCL, OpenMP offload
18+
and stdpar work this way. There is no island to separate.
19+
20+
This document describes the island shape, which is what mcpp implements.
21+
22+
## Device translation units
23+
24+
A source whose extension is `.cu` or `.hip` is a **device translation unit**.
25+
mcpp classifies it as such and treats it accordingly: it is never scanned for
26+
imports and never produces a BMI, because no device compiler accepts C++20
27+
modules.
28+
29+
`.cuh` and `.hiph` are classified as headers. They are not compiled, but
30+
editing one can change what the graph should be, so they invalidate the fast
31+
path exactly as any other header does.
32+
33+
Device extensions are **not** in the default source glob. A package that
34+
vendors a `.cu` it builds elsewhere must not begin compiling it on an mcpp
35+
upgrade, which is a break its author cannot fix once that version has shipped.
36+
Device sources are opted into by naming them.
37+
38+
## The seam
39+
40+
A device translation unit cannot import a module, so the boundary between it
41+
and the rest of a project is a header. Consumers do not see that header: a
42+
module includes it in its global module fragment and exports a C++ interface,
43+
and everything downstream imports the module.
44+
45+
That module is worth naming a *seam*, because its reason for existing is not
46+
the module boundary. It is the single place where the island underneath can be
47+
exchanged — for HIP, for a CPU fallback — without any consumer changing, and
48+
the single place a `cfg(accelerator = ...)` section has to apply. A project
49+
without a seam has no boundary at which a backend can be substituted.
50+
51+
Two constraints on the interface follow from what the compilers are, not from
52+
taste. It should be `extern "C"`, because the device compiler drives a host
53+
compiler that mcpp did not choose and the two sides therefore do not share a
54+
C++ ABI. The island should avoid the standard library, because an island that
55+
links libstdc++ puts a second copy of the C++ runtime into a program whose own
56+
copy came from mcpp's toolchain.
57+
58+
## Compiling an island
59+
60+
The command that invokes a device compiler is not built into mcpp. It is
61+
supplied by a **build-rule package**, consumed with `host-module = true`,
62+
which emits build-graph edges whose outputs join the link. See
63+
[07 — build.mcpp](07-build-mcpp.md) for the mechanism and
64+
`examples/09-cuda-kernel` for a working CUDA rule.
65+
66+
The division is deliberate. mcpp owns the graph, the artifact's identity and
67+
the set of architectures; a vendor's flag spelling, its architecture syntax and
68+
its host-compiler requirements belong to the rule.
69+
70+
## The host compiler a device compiler will accept
71+
72+
nvcc refuses host compilers newer than a bound it states in its own
73+
`crt/host_config.h`, and mcpp's toolchain payload is frequently newer than that
74+
bound. Because mcpp supplies the host compiler, it can report the pairing
75+
before anything is compiled:
76+
77+
```
78+
$ mcpp self doctor
79+
Checking device toolkit
80+
warning: cuda will refuse this host compiler: gcc 13 exceeds the bound of 12
81+
stated in /usr/include/crt/host_config.h.
82+
```
83+
84+
The bound is read from the toolkit rather than tabulated in mcpp, so a toolkit
85+
mcpp has never seen still answers, and a header mcpp cannot parse yields no
86+
bound and therefore no claim.
87+
88+
This is reported rather than enforced: a project that compiles no device code
89+
is unaffected by an incompatible pair.
90+
91+
## Declaring what a build targets
92+
93+
```toml
94+
[build]
95+
accel = "cuda12.8+{sm_80,sm_90f} ptx>=90"
96+
```
97+
98+
overridden for one build by `--accel`, which is the relationship `--target`
99+
has with `[toolchain]`. `--no-accel` is not the absence of `--accel`; it is an
100+
explicit request for no accelerator, which is what selects a CPU-only variant
101+
of a package that also publishes device builds.
102+
103+
A source package may declare which backends it supports:
104+
105+
```toml
106+
[package]
107+
accelerators = ["cuda", "rocm"]
108+
```
109+
110+
This mirrors `[package] platforms`: a statement of intent and a CI-matrix hint,
111+
not a gate. It is a different field from an artifact's `accel` on purpose — a
112+
declaration is written by hand and may be aspirational, while an artifact's
113+
field is measured from the build that produced it.
114+
115+
## What a prebuilt artifact states
116+
117+
An artifact that carries device code records it beside its compatibility tag:
118+
119+
```toml
120+
[[runtime.artifacts]]
121+
role = "static-library"
122+
path = "lib/libgpukit.a"
123+
provenance = "mcpp-pack/1"
124+
abi = "x86_64-linux-gnu-gcc16-libstdcxx16-c++23"
125+
accel = "cuda12.8+{sm_80,sm_90f} ptx>=90"
126+
```
127+
128+
The field is separate from the tag rather than a segment of it because an
129+
architecture list is a set, and the tag is a dash-joined string whose triple
130+
already contains a variable number of dashes.
131+
132+
An absent `accel` means the artifact carries no device code and constrains
133+
nothing, which is why a CPU-only library is usable by every build.
134+
135+
### How a consumer is matched
136+
137+
A build's request is satisfied by an artifact when, for each backend the build
138+
asks for, the artifact declares that backend, agrees on the toolkit's major
139+
version, and covers every requested architecture. An architecture is covered
140+
when it is named, when a family target of the same major and an equal-or-lower
141+
minor is named, or when the embedded portable form's floor is at or below it.
142+
143+
Family targets and portable forms are what keep the variant matrix finite.
144+
Publishing one artifact per chip does not scale; publishing one per generation
145+
does.
146+
147+
When nothing matches, the refusal names the dimension and both sides:
148+
149+
```
150+
error: mcpplibs.gpuonly@0.1.0: no prebuilt artifact matches this toolchain.
151+
your toolchain : x86_64-linux-gnu-gcc16-libstdcxx16-c++23 accel=cuda12.8+{sm_86}
152+
published tags :
153+
x86_64-linux-gnu accel=cuda12.8+{sm_90f}
154+
closest is x86_64-linux-gnu, and it differs on:
155+
accel needs cuda12.8+{sm_90f}, this build has cuda12.8+{sm_86}
156+
fix: build for an architecture the package carries (--accel), or take
157+
a variant that carries no device code (--no-accel), or ask the
158+
publisher for one covering yours.
159+
```
160+
161+
This is the failure the dimension exists to move. Without it the build links
162+
cleanly and the program fails at its first kernel launch with a message naming
163+
neither the package nor the architecture either side expected.
164+
165+
### Publishing several variants
166+
167+
A package may publish several artifacts, and a consumer takes the first whose
168+
tag accepts it. **List the CPU-only artifact first.** An mcpp that predates the
169+
`accel` field ignores it, and ordering is what still gives such a client an
170+
artifact that runs anywhere.
171+
172+
## Conditioning on the backend
173+
174+
`accelerator` is a target-side layer, resolved after the dependency graph is,
175+
and it holds a set rather than a single value:
176+
177+
```toml
178+
[target.'cfg(accelerator = "rocm")'.build]
179+
cxxflags = ["-DMYAPP_ROCM"]
180+
```
181+
182+
The comparison is membership, so a build enabling both CUDA and ROCm answers
183+
true to each. `any`, `all` and `not` compose over it as ordinary boolean
184+
combinators.
185+
186+
## Not implemented
187+
188+
The whole-target shape (SYCL, OpenMP offload, stdpar), device linking for
189+
relocatable device code, static libraries containing device code, and
190+
accelerator payloads supplied through xim. See
191+
`.agents/docs/2026-09-05-accelerator-support-design.md` for the design these
192+
follow from.

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
- [17 - The Project Environment](17-the-project-environment.md)
2323
- [18 - Reaching a Device](18-devices.md)
2424
- [19 - Supported Versions and Compatibility](19-supported-versions.md)
25+
- [20 - Accelerators](20-accelerators.md)
2526

2627
## Specifications
2728

docs/zh/05-mcpp-toml.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,20 @@ warning: 'C:/.../pkg/test/www' contains names this system's active code page can
690690
Linux 与 macOS 不做这种转换,因此那里不会跳过任何名字。一个包在一边能构建、在另一
691691
边报 `internal: unhandled exception` 并指向代码页,就是 mcpp#516
692692

693+
### 2.3.1 `[build] accel` — 本次构建面向的加速器
694+
695+
```toml
696+
[build]
697+
accel = "cuda12.8+{sm_80,sm_90f} ptx>=90"
698+
```
699+
700+
本次构建为哪些设备后端与架构编译。单次构建可用 `--accel` 覆盖 ——
701+
这与 `--target``[toolchain]` 的关系相同;`--no-accel` 是显式请求「不要加速器」,
702+
也就是在一个同时发布了设备构建的包中选中 CPU-only 变体的方式。
703+
704+
该取值会与构建所消费的任何预建产物的 `accel` 字段比较,而请求为空的构建被任何产物满足。
705+
[20 — 加速器](20-accelerators.md)
706+
693707
### 2.4 `[lib]` — 库根模块约定
694708

695709
```toml
@@ -1591,6 +1605,20 @@ platforms = ["linux", "macos", "windows"]
15911605
两者都只是 warning,绝不报错:覆盖度属于发布纪律,而能作判断的人看的是发布,
15921606
不是这一次构建。
15931607

1608+
### 2.12b `[package] accelerators` — 加速器声明
1609+
1610+
```toml
1611+
[package]
1612+
accelerators = ["cuda", "rocm"]
1613+
```
1614+
1615+
声明该包支持的加速器后端。与 `platforms` 同形:一个意图声明与 CI 矩阵提示,
1616+
`mcpp why` 展示,**不是门**
1617+
1618+
与产物的 `accel` 字段刻意不同。声明由人手写、可以是期望值;`accel` 是从产生该二进制的
1619+
那次构建测量出来的,并且是消费者被拒绝时所依据的东西。见
1620+
[20 — 加速器](20-accelerators.md)
1621+
15941622
### 2.13 `[xlings]` — 工程的环境
15951623

15961624
```toml

0 commit comments

Comments
 (0)