Skip to content

Commit 1e2137b

Browse files
feat(accel): exclusive capabilities, an open accel grammar, and the deprecated environment key removed from the manual (#562)
Three things, all of which come out of the multi-device design in .agents/docs/2026-09-05-multi-device-ecosystem-design.md. ## `[package] exclusive` Two packages providing one capability is usually fine and sometimes the point: an OpenBLAS package and an MKL package both provide `blas`, and a build that links one program against each is legitimate. What is not fine is two implementations of ONE accelerator interface in one link, because they define the same symbols and the link resolves every call to whichever archive it reached first. The engine cannot tell those apart. Seeing the symbol overlap needs object files that do not exist when capabilities are bound, and refusing every duplicate provider as a rule would break the BLAS case the manual documents. So the package says it: provides = ["gpu-blas"] exclusive = ["gpu-blas"] Two such packages in one graph are refused when capabilities are bound, before anything is compiled, naming the capability and both providers, and reporting `exclusive-capability` in `--format json`. A capability nobody declares exclusive behaves exactly as before. It is a list rather than a flag because a package may provide several capabilities and be the sole provider of only some. An entry naming a capability the package does not supply is a schema warning: exclusivity is a claim about this package's own symbols, and a silent no-op is how a typo survives. ## The `accel` grammar is open `cuda`, `hip`, `vulkan` and `sycl` were never a closed set, but the floor had only CUDA's spelling. `floor>=` is now accepted alongside `ptx>=` for the same field, so a backend whose portable form is SPIR-V does not have to borrow NVIDIA's word for it. The existing spelling still parses -- asserted, because descriptors written before this exist. Tests cover `vulkan1.3+{spirv1.6} floor>=1.4`, `sycl2020+{spir64,...}` and `hip6.4+{gfx942}`, including that an architecture carrying no leading number is compared by equality rather than by a level invented from its middle digits. ## `[xlings] deps` is gone from the manual `[xlings.workspace]` has been the one table since 2026.9.3.1, and `deps` is honoured with a warning. The manual still taught the old spelling in fourteen places in English and ten in Chinese, and `examples/09-cuda-kernel` -- shipped in 2026.9.5.1 -- was written with it. A deprecated form that still works is exactly the one a reader will copy, so the documentation no longer offers it. The compatibility path in the parser is untouched: a project that has it keeps building and is told what to write. ## Verified `tests/e2e/601_exclusive_capability.sh` asserts both halves, and the second half is the one that matters: the same graph without the claim still builds. Without it this test would also pass against an engine that refused every duplicate provider, which is the behaviour the key exists to avoid. 100 test binaries pass. The three new manifest tests and five new grammar tests were run individually to confirm they execute rather than being counted. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent a4043bf commit 1e2137b

27 files changed

Lines changed: 1800 additions & 45 deletions

.agents/docs/2026-09-05-multi-device-ecosystem-design.md

Lines changed: 1182 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# 多设备生态:实施计划与任务表
2+
3+
对应方案 `2026-09-05-multi-device-ecosystem-design.md`。本文只管**做什么、谁依赖谁、
4+
判据是什么、现在到哪一步**。方案改了本文跟着改,反之不成立。
5+
6+
## 0. 状态总览
7+
8+
| 批次 | 仓库 | 状态 |
9+
|---|---|---|
10+
| ⓪ 修已发布的错误示范 | mcpp | 🟡 T0.1/T0.2 ✅,T0.3 待载荷 |
11+
| ① 载荷 | xim-pkgindex | 🟡 **PR #759**(25 个包,已实测) |
12+
| ② 引擎 | mcpp | 🟡 进行中 |
13+
| ③ 发布 | mcpp ||
14+
| ④ 适配面 | mcpp-index ||
15+
| ⑤ 框架 | mcpp-index ||
16+
| ⑥ 生态验证 | 沙箱 ||
17+
18+
图例:⬜ 未开始 / 🟡 进行中 / ✅ 完成并有判据 / ⛔ 阻塞
19+
20+
---
21+
22+
## 1. 任务表
23+
24+
### ⓪ 修已发布的错误示范(mcpp,无依赖,立刻做)
25+
26+
| # | 任务 | 判据 | 依赖 |
27+
|---|---|---|---|
28+
| T0.1 | 从全部用户文档删除 `[xlings] deps`,`[xlings.workspace]` 为唯一形式 | `git grep -c 'xlings\] deps' docs/` 为 0(中英双份) ||
29+
| T0.2 | `examples/09-cuda-kernel` 改用 `[xlings.workspace]` | 示例中不出现 `deps =` | T0.1 |
30+
| T0.3 | 该示例去 host 化(`-L/usr/...``/usr/local/cuda/bin/nvcc`) | 示例与规则包里 `grep -c '/usr'` 为 0 | T1.1 |
31+
32+
### ① 载荷(xim-pkgindex,批内并行)
33+
34+
| # | 任务 | 版本 | 判据 | 依赖 |
35+
|---|---|---|---|---|
36+
| T1.1 | ✅ 24 个 CUDA 组件(编译/运行/调试/分析/算子库) | 12.9 线 + 13.3 线 | ✅ 载荷编 sm_89 并在 4080 上跑出 `12 24 36 48`;两线并存可切换 ||
37+
| T1.2 | `llvm-offload`(补 slim 载荷缺的 offload 工具) | 22.1.8 | `clang -x cuda -fgpu-rdc` 编链通过 ||
38+
| T1.3 |`dpcpp` | 7.1.0 |`sycl-ls``[cuda:gpu] NVIDIA CUDA BACKEND` ||
39+
| T1.4 | `pocl` | 7.2 | `clinfo` 出现 CPU 设备 ||
40+
| T1.5 | `mesa-lavapipe` | 25.2.8+ | `vulkaninfo` 出现 `PHYSICAL_DEVICE_TYPE_CPU` ||
41+
| T1.6 | ✅ 随 T1.1 一并落地 | 13.3.x / 2026.x | 静态检查通过;运行判据待 ⑥ ||
42+
| T1.7 |`libcublas`+四个算子库(上游归档含 static,未再拆) | 13.5.1.27 | ⚠️ static 分包推迟:上游一个归档同时含 shared 与 static,拆包要重打,先按上游形态发 ||
43+
| T1.8 | `chipstar` | 1.3.0 | 无卡机器上跑一个 CUDA kernel | T1.4 |
44+
| T1.9 | `adaptivecpp` | 25.10.0 | `--acpp-targets=omp` 跑 kernel | T1.2 |
45+
| T1.10 | `hip-runtime` / `hipcc` | 7.14.0 | `HIP_PLATFORM=nvidia` 跑 kernel | T1.1 |
46+
47+
### ② 引擎(mcpp,有序)
48+
49+
| # | 任务 | 判据 | 依赖 |
50+
|---|---|---|---|
51+
| T2.1 | **C-1 设备目标原语** `[[target]] kind = "device"` | 单测 + e2e:设备目标不参与常规链接 ||
52+
| T2.2 | **C-2 二次链接边** `role = "device-link"` | 跨 TU `__device__` 调用链接成功(C9) | T2.1, T1.2 |
53+
| T2.3 | **C-3 逐 glob 收窄** | 空集/非子集各报错一次 | T2.1 |
54+
| T2.4 |**C-4 `exclusive` 能力声明** | ✅ e2e 601:独占对被拒并点名双方;**对照** —— 不声明的两个提供者照常共存。3 条单测 + 中英文档 + `exclusive-capability` 进机器接口契约页 ||
55+
| T2.5 | **C-5 载荷可用性机制** + 探针通道 | 驱动只到 12.4 时请求 13.x ⇒ 构建前拒绝(C2) ||
56+
| T2.6 | **C-6 含设备代码的归档** | `.a``accel` 随包传播(C13) | T2.1 |
57+
| T2.7 | **C-7 `accel` 维语法开放** | `vulkan1.3` / `sycl:spir64` / `hip:gfx1100` 可解析比较 ||
58+
| T2.8 | **把 CUDA 探针搬进规则包** | 核心 grep 不到厂商名字(C15);卸掉规则包 doctor 安静(C16) | T2.5 |
59+
| T2.9 | **`accel` 表达驱动下界** | PTX 版本高于驱动 ⇒ 构建前拒绝(C20) | T2.5, T2.7 |
60+
| T2.10 | **未指定设备目标的构建期诊断** | 报「没有为任何可用设备编」而非运行期(C19) | T2.7 |
61+
62+
### ③ 发布(mcpp)
63+
64+
| # | 任务 | 判据 |
65+
|---|---|---|
66+
| T3.1 | 版本号 + CHANGELOG + 发布 | GitHub/GitCode 资产 sha256 一致;`ci-fresh-install` 全绿 |
67+
68+
### ④ 适配面(mcpp-index,依赖 ③)
69+
70+
| # | 任务 | 判据 | 依赖 |
71+
|---|---|---|---|
72+
| T4.1 | `compat.cuda-runtime``compat.cuda-driver` 改名 + `repo` 改正 | 旧名保留一个跳转期 | T3.1 |
73+
| T4.2 | `compat.vulkan-icd` / `compat.opencl-icd`(缺失时回落载荷) | 无卡机器上 dlopen 到软件实现 | T1.4, T1.5 |
74+
| T4.3 | `rules-cuda` / `rules-hip` / `rules-sycl` / `rules-spirv` 进索引 | 消费者一行依赖即可用 | T3.1, T2.8 |
75+
| T4.4 | `compat.cublas` / `cudnn` / `nccl` / `onemkl` | 闭包校验通过 | T1.7 |
76+
77+
### ⑤ 框架(mcpp-index,依赖 ④)
78+
79+
| # | 框架 | 判据(一律用上游自带测试) | 依赖 |
80+
|---|---|---|---|
81+
| T5.1 | llama.cpp 多后端(CPU/CUDA/Vulkan/SYCL/HIP) | 各后端推理出正确 token(C4) | T4.3 |
82+
| T5.2 | ncnn | lavapipe 上分类与 CPU 一致(C5) | T4.2 |
83+
| T5.3 | CUTLASS | GEMM 与参考比对(C6) | T4.3 |
84+
| T5.4 | oneDNN | `benchdnn` 两条 lane(C11) | T1.3 |
85+
| T5.5 | Kokkos | 自带 unit test 两份产物都过(C7) | T4.3 |
86+
| T5.6 | OpenCV CUDA 模块 | accuracy test(C13) | T4.4 |
87+
| T5.7 | FAISS | 自带测试 | T4.4 |
88+
| T5.8 | ONNX Runtime | model test + EP 共存(C12) | T4.4 |
89+
| T5.9 | libtorch 载荷消费 | 闭包校验(C14) | T4.4 |
90+
91+
### ⑥ 生态验证(沙箱)
92+
93+
| # | 任务 | 判据 |
94+
|---|---|---|
95+
| T6.1 | 新建 subos,配 CN mirror,装发布物 | 版本自述正确 |
96+
| T6.2 | 逐条跑 C0–C20 | 全绿,且每条模拟器 lane 有硬件对照 |
97+
| T6.3 | 对照:去掉包后判据必须变红 | 每条判据都测到了东西 |
98+
99+
---
100+
101+
## 2. 并行与关键路径
102+
103+
```
104+
⓪ ──────────────────────────────────────────────► (独立,最先完成)
105+
106+
① 载荷 ────┬─ T1.1 CUDA ──────────┐
107+
├─ T1.2 llvm-offload ──┤
108+
├─ T1.3 dpcpp ─────────┤
109+
├─ T1.4 pocl ──────────┼──► ④ ──► ⑤ ──► ⑥
110+
├─ T1.5 lavapipe ──────┤
111+
└─ T1.6-10 ────────────┘
112+
113+
② 引擎 ── T2.1 ─ T2.2 ─ … ─ T2.10 ┴─► ③ 发布 ─┘
114+
```
115+
116+
**关键路径是 ②**(引擎有序,十个任务串行),① 与它完全并行。
117+
⚠️ ⑤ 的九个框架是工作量主体,T5.1 是 gate —— 它若暴露设计问题,后八个都受影响。
118+
119+
---
120+
121+
## 3. 动态更新记录
122+
123+
方案允许按实际情况改,但每次改必须写下理由。
124+
125+
| 日期 | 改了什么 | 理由 |
126+
|---|---|---|
127+
| 2026-09-05 | 建表 ||
128+
| 2026-09-05 | §3.0 改正:xim **推荐** xlings-res 双镜像,但**允许**第三方 URL template + per-arch sha256 |`xpkg-creater/SKILL.md` §资源选择策略;索引里 247 个配方直连 github。我原先写「xim 不 re-host」过绝对 |
129+
| 2026-09-05 | T0.1/T0.2 完成 | 文档 24 处清零;示例改用 `[xlings.workspace]` |
130+
| 2026-09-05 | T1.1/T1.3/T1.6/T1.7 落地为 xim PR #759(25 包) | 实测通过:载荷编 sm_89 并在 4080 上跑通;两条线并存可切 |
131+
| 2026-09-05 | `cuda-cccl` 用显式 per-version URL 而非模板 | 上游把组件从 `cuda_cccl` 改名为 `cccl`,目录名进 URL,一个模板 404 |
132+
| 2026-09-05 | 配方用 `io.popen` 列文件 | `os.files``config()` 沙箱里不可用(`attempt to call a nil value`),`llvm.lua` 也用 popen |
133+
| 2026-09-05 | T2.4 完成 | `exclusive` 是列表不是布尔:一个包可提供多项能力而只有部分独占。schema 警告而非报错,因为绑定期那一处才是执行者 |
134+
| 2026-09-05 | ⚠️ 本机 shim 被 #582 剪掉一次 | 25 次 `xlings install``mcpp` 等 7 个裸名 shim 消失,store 完好。重装即恢复 —— 又一次受控复现,补进 issue |
135+
| 2026-09-05 | `libcublas` 暂不拆 static | 上游一个归档同时含 shared 与 static,拆分需要重打包并 re-host,与「不 re-host」冲突;先按上游形态发,拆分单列 |

docs/05-mcpp-toml.md

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,8 @@ runner = ["qemu-aarch64-static"]
13181318
The rules, for `mcpp run` and `mcpp test` alike:
13191319

13201320
- **A declared runner is used.** Its first element is located by mcpp: first in
1321-
the `bin/` directory of each payload declared under `[xlings] deps` (§2.13),
1321+
the `bin/` directory of each payload declared under `[xlings.workspace]`
1322+
(§2.13),
13221323
then on `PATH`. A bare name on `PATH` resolves to an xvm shim, which answers
13231324
for the current SubOS rather than for the package; the payload lookup is what
13241325
lets a runner name a program the project declared.
@@ -1334,14 +1335,14 @@ The rules, for `mcpp run` and `mcpp test` alike:
13341335
the manifest has no axis to carry; a project whose runner was written for
13351336
x86_64 developers is still readable on an aarch64 machine.
13361337

1337-
Provisioning the emulator through `[xlings] deps` is the form for a CI job or
1338-
a project built on one host class. `qemu-user-aarch64` in the index is built
1339-
for x86_64 Linux only, and `[xlings] deps` provisions on every host that builds
1340-
the project, so the entry is written per platform (§2.13):
1338+
Provisioning the emulator through `[xlings.workspace]` is the form for a CI job
1339+
or a project built on one host class. `qemu-user-aarch64` in the index is built
1340+
for x86_64 Linux only, and the table provisions on every host that builds the
1341+
project, so the entry is written per platform (§2.13):
13411342

13421343
```toml
1343-
[xlings]
1344-
deps = [{ linux = "qemu-user-aarch64" }]
1344+
[xlings.workspace]
1345+
"xim:qemu-user-aarch64" = { linux = "" } # present on Linux, any version
13451346

13461347
[target.aarch64-linux-musl]
13471348
runner = ["qemu-aarch64-static"]
@@ -1533,6 +1534,46 @@ ambiguity to pin: pinning replaces an error that names both candidates with one
15331534
that names a mangled symbol. Interchangeable *libraries* (BLAS implementations,
15341535
which export distinct symbol sets and are selected per link) are unaffected.
15351536

1537+
#### `exclusive` — a package declaring it is the only provider
1538+
1539+
The paragraph above describes a defect the engine cannot detect. Seeing that two
1540+
providers define the same symbols requires their object files, which do not
1541+
exist when capabilities are bound; and refusing every duplicate provider as a
1542+
rule would break the BLAS case in the same paragraph, which is legitimate.
1543+
1544+
So the package says it:
1545+
1546+
```toml
1547+
[package]
1548+
name = "compat.cublas"
1549+
provides = ["gpu-blas"]
1550+
exclusive = ["gpu-blas"]
1551+
```
1552+
1553+
Two packages that both provide `gpu-blas`, where at least one declares it
1554+
exclusive, are refused when capabilities are bound — before anything is
1555+
compiled, naming the capability and both providers:
1556+
1557+
```
1558+
error: capability 'gpu-blas' is provided by more than one package, and they
1559+
declare it EXCLUSIVE.
1560+
providers: [compat.cublas, compat.rocblas]
1561+
exclusive: [compat.cublas, compat.rocblas]
1562+
Two implementations of one interface define the same symbols, so the
1563+
link would resolve every call to whichever archive it reached first.
1564+
Keep one of them — a `[capabilities]` pin selects a provider for a
1565+
REQUIREMENT and cannot make two definitions of one symbol safe.
1566+
```
1567+
1568+
The refusal reports `exclusive-capability` in `--format json` (chapter 11).
1569+
1570+
**It is a claim about this package's own symbols**, so an entry that names a
1571+
capability the package does not provide is reported as a schema warning: there
1572+
is nothing to be exclusive about. And a capability nobody declares exclusive
1573+
behaves exactly as before — two BLAS implementations still coexist, and the
1574+
existing "two or more, unpinned" error still applies only when something
1575+
*requires* the capability.
1576+
15361577
### 2.8.2 `[feature-deps.<name>]` — dependencies a feature pulls in
15371578

15381579
A dependency declared under `[feature-deps.<name>]` is **optional**: it is
@@ -2526,8 +2567,8 @@ build_finished = "mcpp-hooks-audioplayer niulai-mm"
25262567
build_failed = "mcpp-hooks-audioplayer niulai-niulai"
25272568
side_effect = false
25282569

2529-
[xlings]
2530-
deps = ["xim:mcpp-hooks-audioplayer@0.0.1"]
2570+
[xlings.workspace]
2571+
"xim:mcpp-hooks-audioplayer" = "0.0.1"
25312572
```
25322573

25332574
A different sound for a successful or failed build. `side_effect = false` is

docs/07-build-mcpp.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ int main() {
104104
| `mcpp::dep_bin(pkg, tool)` *(2026.8.5.1+)* | reads `MCPP_DEP_<PKG>_BIN_<TOOL>` — the absolute path of a **host tool** built by a dependency (see below) |
105105
| `mcpp::link_script(p)` *(2026.8.19+)* | `mcpp:link-script=` |
106106
| `mcpp::runner(tok)` *(2026.8.19.2+)* | `mcpp:runner=` — see below |
107-
| `mcpp::xpkg_dir(ns, name)` / `mcpp::xpkg_dir(name)` *(2026.8.19+)* | the payload directory of a package this manifest declared in `[xlings] deps`; `""` when it was not declared or is not installed (see below) |
107+
| `mcpp::xpkg_dir(ns, name)` / `mcpp::xpkg_dir(name)` *(2026.8.19+)* | the payload directory of a package this manifest declared in `[xlings.workspace]`; `""` when it was not declared or is not installed (see below) |
108108
| `mcpp::warning(text)` *(2026.8.21.2+)* | `mcpp:warning=` — see below |
109109
| `mcpp::action{…}.submit()` *(2026.8.5.1+)* | `mcpp:action=` — declares a **build-graph node** instead of doing the work here (see below) |
110110

@@ -199,7 +199,7 @@ package whose content is implementation-neutral. Asking follows whatever
199199
the compiler payload or through the runtime binding, and nothing has to look
200200
for it.
201201

202-
### Finding an `[xlings] deps` payload: `xpkg_dir` (2026.8.19+)
202+
### Finding an `[xlings.workspace]` payload: `xpkg_dir` (2026.8.19+)
203203

204204
`dep_dir` answers for **mcpp** dependencies. An xlings package is a different
205205
namespace with a different store layout, and `xpkg_dir` is the interface for it:

docs/11-machine-output.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ a program classifying the outcome reads `reason`:
377377
| `os-mismatch` | the requested and resolved triples name different systems |
378378
| `layer-requirement` | a package requires a layer the resolution did not give it |
379379
| `layer-ordering` | the five layers do not stack |
380+
| `exclusive-capability` | two packages provide one capability and at least one declared it exclusive |
380381
| `other` | a refusal whose branch has not been given a token yet |
381382

382383
⚠️ **Exit 0 whenever the question was answered, including "refused".** "Would

docs/13-baremetal.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ fact, selection is a board fact.**
291291
| Board-support package | Which startup object and libraries to select, which linker script, which emulator invocation | `-lcrt0-semihost`, `picolibcpp.ld`, `qemu-system-riscv64 -machine virt …` |
292292
293293
The middle row is what keeps a package from having to name a C library.
294-
Earlier versions of both ecosystem packages declared
295-
`[xlings] deps = ["xim:picolibc-riscv@1.8.12"]`, which bound a package to one
296-
libc, one architecture and one compiler implementation. That declaration is no
294+
Earlier versions of both ecosystem packages named a libc package in the
295+
environment table directly, which bound a package to one libc, one architecture
296+
and one compiler implementation. That declaration is no
297297
longer required, and the target's sysroot column replaced it.
298298
299299
A second board on the same ISA is a change of the three values in the bottom
@@ -641,8 +641,8 @@ it needs under `[xlings.workspace]`, exports one C++ module for consumers, and
641641
emits its board facts from `build.mcpp`.
642642

643643
**A declaration there provisions the package on the first build** (since
644-
2026.8.29; the table is `[xlings.workspace]` since 2026.9.3.1, and the older
645-
`[xlings] deps` still works and says so). It is also what lets `mcpp::xpkg_dir` answer *"where
644+
2026.8.29; the table is `[xlings.workspace]`). It is also what lets
645+
`mcpp::xpkg_dir` answer *"where
646646
did that package land"*. Both halves matter: the same declaration installs the
647647
emulator and tells the build program where it went.
648648

docs/15-openkal-cross.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ package that provides the emulator is declared for the hosts that can install
197197
it:
198198

199199
```toml
200-
[xlings]
201-
deps = [{ linux = "qemu-user-aarch64" }]
200+
[xlings.workspace]
201+
"xim:qemu-user-aarch64" = { linux = "" }
202202

203203
[target.aarch64-linux-musl]
204204
runner = ["qemu-aarch64-static"]

docs/17-the-project-environment.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ and in CI, whatever else those two machines happen to have installed.
88
```toml
99
[xlings]
1010
subos = "tools"
11-
deps = ["xim:qemu-riscv@9.2.4-1"]
11+
12+
[xlings.workspace]
13+
"xim:qemu-riscv" = "9.2.4-1"
1214
```
1315

1416
Working project: `examples/07-project-subos/`.
@@ -85,7 +87,7 @@ should beat a file.
8587

8688
## 3. What the declaration does not decide
8789

88-
`[xlings] deps` names packages to be present in the environment, and each one's
90+
`[xlings.workspace]` names packages to be present in the environment, and each one's
8991
payload directory is delivered separately as `MCPP_XPKG_<NAME>_DIR`. That is a
9092
different question from `PATH` and stays a different answer: a build program
9193
that needs a package's data files (protoc's well-known `.proto` files, say)
@@ -132,7 +134,7 @@ gives the full rule.
132134

133135
Against that: an isolated environment is a directory that has to be created and
134136
populated, and the first build pays for it. Since 2026.8.29 mcpp does that
135-
work — a declared `[xlings] deps` is provisioned on first use, and a named
137+
work — a declared `[xlings.workspace]` entry is provisioned on first use, and a named
136138
`[xlings] subos` that does not exist yet is created rather than refused — but
137139
the cost is real: the first build on a clean machine downloads and installs
138140
before it compiles anything. A project whose tools are ordinary and whose

docs/18-devices.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ mcpp::run_exclusive(); // this target's runs cannot overlap
5959
```
6060
6161
⭐ **Name the program, not its path.** mcpp locates it: the `bin/` of a payload
62-
declared under `[xlings] deps` by **any package in the graph** — the consuming
62+
declared under `[xlings.workspace]` by **any package in the graph** — the consuming
6363
project first, then its dependencies — and then `PATH`. A board-support package
6464
is precisely the thing that knows which emulator or probe reaches its machine,
6565
so it declares that payload itself and the consumer declares nothing. Writing an

docs/20-accelerators.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,31 @@ Family targets and portable forms are what keep the variant matrix finite.
173173
Publishing one artifact per chip does not scale; publishing one per generation
174174
does.
175175

176+
### The grammar is open
177+
178+
`cuda`, `hip`, `vulkan` and `sycl` are not a closed set. A backend name, a
179+
version, an architecture list and an optional floor are the whole shape, and
180+
mcpp compares them without a table of who exists:
181+
182+
```
183+
vulkan1.3+{spirv1.6} floor>=1.4
184+
sycl2020+{spir64,nvptx64-sm_89}
185+
hip6.4+{gfx942}
186+
```
187+
188+
An architecture whose spelling carries no leading number — `gfx942` — is
189+
compared by equality, because there is no ordering to read out of it. That is
190+
the answer rather than a guess: reading `942` out of the middle would invent a
191+
level AMD does not define.
192+
193+
`floor>=` is the backend-neutral spelling of the portable-form floor. `ptx>=`
194+
is CUDA's word for the same field and remains accepted, so descriptors written
195+
before this are unaffected; a backend whose portable form is SPIR-V writes
196+
`floor>=` instead of borrowing NVIDIA's term.
197+
198+
Which spellings mean what for a given backend is the business of that backend's
199+
rule package. What the engine holds is the shape and the comparison.
200+
176201
When nothing matches, the refusal names the dimension and both sides:
177202

178203
```

0 commit comments

Comments
 (0)