Skip to content

Commit cb14d58

Browse files
committed
docs(plan): attribution, the programming-model three-way split, and a falsification target
Revises the design doc. Two of its own judgements were wrong and are corrected in place rather than appended to, because a design doc whose corrections live at the bottom is read top-down and gets the retracted version. * RDC is plugin-side, engine change zero. Rechecked against the attribution rule this revision adds: it names a vendor (test 1), changes no artifact property (test 2) and invents no edge kind (test 3). Every primitive it needs exists -- `action` with the `object` destination is "an external step's object joins the ordinary link", twice. It therefore does not sit on the critical path, which changes the staging. * stdpar is islandable, so calling it mutually exclusive with mcpp's model was an overclaim. `-stdpar` decomposes into a compile-side island and a link-side allocator, and the allocator is a whole-image property imposed by a dependency -- the shape `cxx_runtime` already established. What is lost is the selling point ("change no source"), not the capability. docs/20's two-way split should become three-way. Three gaps added. The first came out of a manifest that reads badly: [target.'cfg(not(any(accelerator = "cuda", accelerator = "vulkan")))'.build] `accelerator` is an open vocabulary by design, and `not(any(<enumeration>))` over an open vocabulary silently changes meaning as the ecosystem grows -- a fifth backend edits the meaning of every fallback predicate already written. The general rule is that an open vocabulary needs a "none" that does not enumerate, and the spelling is already in this manifest: `os = "none"` is bare metal, so `accelerator = "none"` is no device backend. Preferred over `cpu`, which would make the axis carry two questions and leave the truth value of `cfg(accelerator = "cpu")` under `accel = "cuda"` undecided. The other two are the package-wide barrier a generated header creates (`source`'s documented semantics: every compile edge of the package waits) and Fortran, which is recorded as identified and deliberately given no design. Also: an attribution rule (three ordered tests) so the engine/plugin boundary is decided rather than argued each time; a note that "island" collides with branch-island in linker vocabulary and cuts across single-source/separate- source, with an alignment sentence rather than a rename; and CANN as the experiment that could falsify the central claim, since all four existing lanes are in one vendor lineage. Its decisive criterion is `git diff src/` empty.
1 parent 46b1e9a commit cb14d58

1 file changed

Lines changed: 279 additions & 15 deletions

File tree

.agents/docs/2026-09-07-general-build-infrastructure-gaps-design.md

Lines changed: 279 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
# 构建系统的四处通用缺口
1+
# 通用构建基础设施:缺口、归属与验证
22

3-
2026-09-07。本文只处理**通用构建基础设施**:其他构建系统都有对应物、与异构无关、
4-
与本仓库的领域无关的那些能力。异构方向特有的缺口(RDC)不在本文,理由见 §0.2。
3+
2026-09-07。本文处理**通用构建基础设施**:其他构建系统都有对应物、其存在理由不引用
4+
任何领域概念的那些能力。异构方向特有的东西不在缺口清单里,但 §7 给出归属规则,
5+
§10 给出用一个陌生厂商证伪整条架构主张的实验。
6+
7+
本文经过一次修订(§14 变更记录)。修订推翻了初稿的两处判断,均已就地更正。
58

69
## 0. 范围
710

@@ -17,16 +20,23 @@
1720
| 包内布局 | `install(FILES ...)` / Meson `install_data` | 通用 |
1821
| 探测库 | `check_include_file` / `check_function_exists` / `check_type_size` | 通用 |
1922
| 配置头 | `configure_file` | 通用,但**不是引擎改动**(§5) |
23+
| 开放词表的"空"取值 | CMake 无对应物;Meson/Cargo 亦无 —— 但它们的谓词词表是封闭的,**因而不需要**;开放词表是本生态自选的性质,这条规则随之而来 | 通用(§6.1) |
24+
| 生成输入的依赖粒度 | CMake `add_custom_command(OUTPUT ...)` 的逐文件依赖;Ninja 的 `order_only` | 通用(§6.2) |
25+
| 多语言(Fortran) | 四者皆有 | 通用(§6.3) |
2026

21-
### 0.2 RDC 为什么不在本文
27+
### 0.2 RDC 为什么不在本文(初稿判断已更正)
2228

2329
relocatable device code 的存在理由是"设备编译器把 `__device__` 函数的跨 TU 调用
24-
推迟到一次设备链接"。这句话无法脱离设备概念陈述,因此它属于 docs/20 的领域,
25-
不属于本文。
30+
推迟到一次设备链接"。这句话无法脱离设备概念陈述,因此按 §0.1 的准入线它不是通用
31+
基础设施。
32+
33+
**更要紧的是:它也不是引擎缺口。** 初稿把 RDC 列为"真缺"并暗示需要引擎支持,按 §7
34+
的归属规则复核后不成立。它需要的原语今天全部存在:规则包用 `action``-rdc=true`
35+
编每个设备 TU(各自以 `object` 归宿进入链接),再用一个 `action` 跑设备链接步骤,
36+
其产物同样以 `object` 归宿加入普通链接;设备运行时库走 `link_lib`**没有新的边种类,
37+
没有新的产物性质,引擎零改动。**
2638

27-
它复用的原语在引擎里已经存在:`mcpp::action``object` 归宿就是"一个外部步骤
28-
产出的对象加入普通链接",SYCL lane 的 `sycl_device_link.o` 正是这个形状。所以
29-
RDC 是一个 rule 包的工作量,不是本文四项中的任何一项。
39+
结论:RDC 属于 `rules-cuda` 的工作量,与本文各项**并行**,不构成依赖。
3040

3141
## 1. 现状,实测
3242

@@ -205,7 +215,7 @@ mcpp:artifact=<role>=<relpath> mcpp::artifact(role, relpath)
205215

206216
### 5.1 现状
207217

208-
配置头生成今天就能写(§1.4)。缺的不是能力,是**公共实现**:每个移植过来的 C 项目
218+
配置头生成今天就能写(§1 第 4 条)。缺的不是能力,是**公共实现**:每个移植过来的 C 项目
209219
都要自己写一遍"这个头在不在""这个函数能不能链上""这个类型多宽"。不做的后果是
210220
CMake 模块生态碎片化的重演 —— 每个项目一份略有差异的 `check_function_exists`
211221

@@ -239,19 +249,256 @@ int main() {
239249
探测结果必须**按工具链指纹缓存**,否则每次构建重探。缓存键取
240250
`toolchain_fingerprint` 已有的值,不新造。
241251

242-
## 6. 准入自检(docs/05 附录 A)
252+
## 6. 新增的三处缺口(修订加入)
253+
254+
### 6.1 开放词表不能靠枚举取反
255+
256+
**问题。** CPU 回退今天只能这样写:
257+
258+
```toml
259+
[target.'cfg(not(any(accelerator = "cuda", accelerator = "vulkan")))'.build]
260+
sources = ["src/cpu/*.cpp"]
261+
```
262+
263+
`accelerator` 的取值是**开放的** —— docs/20 明确说"第五个后端是一个包,不是引擎改动"。
264+
因此这条谓词的含义会随生态增长**静默改变**:新增一个后端之后,每个工程的回退谓词都
265+
必须被编辑。漏一个的后果是 CPU 实现与设备实现同时进入编译集,或者该编时没编。
266+
267+
这不是措辞问题,是一条**一般规则**:
268+
269+
> 一个开放词表的键,`not(any(<枚举>))` 永远不等价于"该词表为空"。因此每个开放词表的
270+
> 层键**必须**提供一个不依赖枚举的"空"取值。
271+
272+
**设计。** 取值 `none`:
273+
274+
```toml
275+
[target.'cfg(accelerator = "none")'.build]
276+
sources = ["src/cpu/*.cpp"]
277+
```
278+
279+
`accelerator = "none"` 为真当且仅当本次构建的加速器集合为空。`not(accelerator = "none")`
280+
自然表达"有任意设备后端"。
281+
282+
**为什么是 `none` 而不是 `cpu`** 三条:
283+
284+
1. **本仓库已有这个拼法。** `os = "none"` 就是裸机(docs/05 §2.7.2)。同一份 manifest
285+
里,同一个词,同一个意思。
286+
2. `cpu` 引入歧义。`accel = "cuda"``cfg(accelerator = "cpu")` 是真是假?为真则 CPU
287+
源码永远参与编译,破坏互斥语义;为假则必须写 `accel = "cuda, cpu"`,改动既有 manifest。
288+
3. `accel` 这条轴回答的是"哪个设备编译器、哪个架构"。CPU 不需要设备编译器,它不在
289+
这条轴上。把它塞进去会让这条轴同时承载两种问题。
290+
291+
**"CPU 后端与设备后端并存"是另一个需求,今天已经可写。** 那种形态(如 ggml 的 CPU
292+
backend 与 CUDA backend 同时编入一个产物)不需要本项:CPU 源码放无条件的
293+
`[build] sources`,设备源码放 `cfg(accelerator = "x")``not(...)` 只在**互斥接缝**
294+
(要么这个实现,要么那个)时才需要,而互斥接缝正是本项要修的场景。
295+
296+
**实现规模。** cfg 求值器里 `accelerator` 走的是集合成员判定,`none` 需要一条特判:
297+
集合为空时为真。不是免费的,但是一条条件。
298+
299+
**推广。** 同一条规则适用于每个开放词表的层键。`compiler``c-abi``compiler-runtime`
300+
是否也需要 `none`,应在实现本项时一并裁定,而不是逐个再议 —— 否则这条规则会以每次
301+
一个键的方式被重新发现。
302+
303+
### 6.2 生成输入的依赖粒度
304+
305+
**问题。** `mcpp:generated=` / `source` 归宿的语义是(docs/07 原文)"**本包每一条编译边
306+
都等它**"。一个生成的头因此构成**包级栅栏**
307+
308+
134 个 shader 无所谓 —— 它们是叶子,没有别的 TU 等它们。但一个**被少数 TU 包含的生成
309+
**会让全包的编译边排在它后面。在驱动、编译器这一档的规模上,这是"并行构建"与
310+
"分阶段构建"的差别。
311+
312+
**设计方向。** 需要"某条编译边依赖某个具体生成文件",而不是"全包等全部生成物"。
313+
两个候选形状:
314+
315+
-`action` 的输出可被具体源码 glob 引用(声明式的边)
316+
- 让生成物携带一个标签,源码侧按标签声明依赖
317+
318+
**本文不选型。** 这一项与 ninja 图的构造方式耦合较深,选型前需要读 `src/build/plan`
319+
`ninja_backend`,确认哪种形状不会与既有的 dyndep/BMI 调度冲突。本文的职责是**指出
320+
它是通用缺口并给出触发条件**:当一个包同时具备(a)生成的头文件,且(b)编译边数量
321+
达到千级时,包级栅栏成为主要瓶颈。
322+
323+
### 6.3 多语言:Fortran
324+
325+
**问题。** mcpp 覆盖 C / C++ / 汇编。异构与 HPC 栈里 Fortran 不是边缘 —— 参考 LAPACK、
326+
大量求解器,以及 Fortran + OpenMP target 这个在科学计算代码里非常常见的组合。没有
327+
Fortran,数值栈的一大块进不来。
328+
329+
**归属。** 按 §7 三测试:不点名厂商(测试 1 不触发),不改变产物性质(测试 2 不触发),
330+
**新增一种编译边的种类**(测试 3 触发)⇒ **引擎侧**
331+
332+
**规模诚实说。** 这一项比本文其余各项都大:它要求工具链模型承认第三种编译器、模块/
333+
接口文件(`.mod`)有自己的依赖图(与 BMI 类似但不同)、以及 Fortran/C 的名字修饰与
334+
调用约定。**本文把它记为已识别的缺口,不给设计** —— 给它一个半成品设计比不给更坏。
335+
336+
## 7. 归属:引擎侧还是插件侧
337+
338+
不列清单,给三条测试。任一为是即归该侧;测试按顺序应用。
339+
340+
| # | 测试 | 归属 |
341+
|---|---|---|
342+
| 1 | 它是否点名某个厂商或工具? | **插件**。仓库已在强制这条:`tests/unit/test_core_vendor_probes.cpp` 以文件数为分母,断言 `src/` 去注释后不含厂商工具名 |
343+
| 2 | 它是否改变**产物是什么**(符号面、包内布局、身份)? | **引擎**。packer、索引、消费者三方必须就此达成一致,而这种一致无法住在插件里 |
344+
| 3 | 它是否**新增一种边或节点**? | **引擎**。插件声明边,不发明边的种类 |
345+
346+
应用到已识别的各项:
347+
348+
|| 归属 | 触发的测试 |
349+
|---|---|---|
350+
| `exports` | 引擎 | 2 |
351+
| `link-flag` | 引擎 | 3(边的属性) |
352+
| `role = "manifest"` | 引擎 | 2 |
353+
| `accelerator = "none"` | 引擎 | 3(谓词词表) |
354+
| 生成输入粒度 | 引擎 | 3 |
355+
| Fortran | 引擎 | 3 |
356+
| `kind = "device"` | 引擎 | 2 —— 它让 `mcpp pack`**测量**`accel` 而不是抄声明 |
357+
| 探测库 | 插件/包 | 1 |
358+
| **RDC** | **插件** | 均不触发(见 §0.2) |
359+
| `.omp` 岛、`.stdpar`| 插件 | 1 |
360+
361+
## 8. 编程模型的三分法,与岛化的边界
362+
363+
docs/20 目前是二分:SYCL 分得开,OpenMP offload 与 stdpar 分不开。**这个二分不成立,
364+
应改为三分。**
365+
366+
| 模型 | 可岛化? | 依据 |
367+
|---|---|---|
368+
| SYCL | **天然** | kernel 是 `submit` 里的闭包,本来就隔离 |
369+
| OpenMP offload | **重构后可以** |`target` 区域提成函数放进自己的 TU,该 TU 用 offload 标志编;**调用方不需要任何 offload 标志** |
370+
| stdpar | **重构后可以** | 见下 |
371+
372+
### 8.1 stdpar 可以岛化(初稿判断已更正)
373+
374+
初稿称 stdpar 与 mcpp 的收益"互斥"。这个说法过头了,应更正。
375+
376+
`-stdpar` 拆开是两件事,**两件都可表达**:
377+
378+
| 层面 | 是什么 | 表达为 |
379+
|---|---|---|
380+
| 编译侧 | 含并行算法调用点的 TU 由该编译器生成 kernel | 岛,与 `.sycl` 同形 |
381+
| 链接侧 | 整个进程的分配器换成托管内存 | **链接行属性**,`[runtime]` 的 link intent |
382+
383+
关键在于分配器替换是**链接期**的事。一个普通 TU 里分配的内存,只要最终链接进了托管
384+
分配器,就是设备可见的;不需要每个 TU 都由该编译器编译。
385+
386+
而且 mcpp 对"依赖强加给消费者的全镜像属性"已有先例:`cxx_runtime`。C++ 运行时的选择
387+
正是这个形状 —— 一个包的选择决定整个镜像并沿依赖传播。托管分配器是同一类东西。
388+
389+
**真正丢掉的是卖点,不是能力。** stdpar 的价值主张是"源码一行不改",而岛化要求一次
390+
重构。这是产品张力,不是架构矛盾。规范应当这样陈述,而不是宣称做不到。
391+
392+
### 8.2 唯一真正的对立,以及它会自己过期
393+
394+
只有一处:**整目标编译 + C++20 模块**。没有 offloading 编译器接受具名模块,所以一个
395+
整目标 target 就是一个没有模块的 target。
396+
397+
这是**编译器能力**造成的,不是设计取舍,并且是**时限性的** —— 等 offloading 编译器
398+
支持模块,这条对立自行消失。一条会过期的约束与一条设计取舍应分开记录,处理方式不同。
399+
400+
### 8.3 OpenMP 岛的已知边界
401+
402+
`declare target` 的全局数据跨 TU 时,岛会漏:设备镜像要求那个全局也在设备侧发出,
403+
而它定义在宿主 TU 里。`.omp` 岛成立的条件是"offload 区域对设备全局自包含",这条限制
404+
必须写进规则包的文档,不能留给用户在链接错误里发现。
405+
406+
## 9. 术语:"island" 这个词
407+
408+
概念是真的:**存在一组 TU,由另一个编译器二进制处理,其产物加入普通链接。** 这是一条
409+
构建系统的轴,业界没有为它命名 —— CMake 直接把 CUDA 当一门"语言"绕过去了。
410+
411+
但这个词有两个问题:
412+
413+
1. **它与业界既有的轴交叉。** 业界的二分是 single-source / separate-source。按那条轴
414+
**CUDA 是单源**(一个 `.cu` 里既有 `__global__` 又有主机代码,由 nvcc 内部拆分),
415+
而 docs/20 把 CUDA 称作岛。熟悉 CUDA 的读者会在这里卡住,因为两条轴用了同一批例子
416+
给出相反的归类。
417+
2. **`island` 在链接器词汇里已被占用。** ARM / Mach-O 的 *branch island*(veneer)是
418+
长跳转桩。mcpp 是一个谈链接的构建系统,这个碰撞是实际的。
419+
420+
**建议:保留词,补一句对齐**,写进 docs/20:
421+
422+
> 本文的"岛"是**构建系统**的轴:哪些 TU 交给另一个编译器二进制。它与编程模型的
423+
> single-source / separate-source 轴正交。CUDA 在后者是单源,在前者是岛。
424+
425+
不建议改词:"岛 / 接缝"这对比喻自洽,且已进入多份文档;改名的代价大于这句对齐。
426+
427+
## 10. 验证:用一个陌生厂商证伪
428+
429+
本文各项都是"补齐"。而整条架构最强的主张是另一句:**引擎里没有厂商知识,加一个厂商
430+
等于加一个规则包。** 它有测试在守(§7 测试 1),但**从未被一个引擎没见过的厂商检验
431+
** —— 现有四条 lane 全在 NVIDIA / Khronos 谱系里。
432+
433+
### 10.1 靶子
434+
435+
昇腾 CANN 栈。三条理由:
436+
437+
1. 完全不同的 ISA、编译器与运行时 API,与既有四条 lane 无谱系重叠。
438+
2. Ascend C 是岛形态 —— docs/20 已把它列为岛的例子,但那是**推断**,这里可变成实测。
439+
3. 垂直完整,从框架适配到驱动。
440+
441+
### 10.2 不做全栈,做一个跨层切片
442+
443+
全栈不可行,五条理由:驱动是内核态(按不变量本就排除);毕昇编译器是 LLVM 量级;
444+
Python 层(`pyasc``pypto`、框架适配)不在覆盖内;规模是数人年;**没有真机或模拟器
445+
则退化为编译验证** —— 正是本仓库给现有 lane 打的差评,代价放大百倍。
446+
447+
还有一条更微妙的:CANN 是普通 C++ 而非模块化 C++,所以这个实验测的是 mcpp 作为通用
448+
构建系统的能力,**不测它的差异化能力**。这不是反对理由,但必须清楚测的是哪一半。
449+
450+
切片:
451+
452+
|| 不做 |
453+
|---|---|
454+
| `rules-ascendc` 规则包 | 从源码构建毕昇编译器 |
455+
| 毕昇**作为载荷**(厂商 URL,与 dpcpp 同待遇) | `ge` 图引擎 |
456+
| 运行时 host API 作为载荷 | Python 层 |
457+
| 一个算子库的**少量算子**原生构建 | `driver` |
458+
| 算子注册元数据(若为 JSON,需核实)→ `role = "manifest"` 的真实用例 | `ops-nn` 全量 |
459+
| 一个最小消费者,在真机或模拟器上跑出正确结果 | |
460+
461+
这条切片贯穿应用 → 库(导出设备代码)→ 运行时 → 设备编译器 → 设备执行,并恰好压在
462+
`exports``role = "manifest"`、探测库、以及 RDC 的昇腾对应物上。**四项够用则通过;
463+
不够则暴露第五项 —— 那正是想要的产出。**
464+
465+
### 10.3 开工前的可行性闸
466+
467+
任一为否则不开始:(a) 有昇腾硬件或可用模拟器;(b) 切片内组件源码可得且许可允许;
468+
(c) 毕昇有稳定的厂商 URL 且可再分发。
469+
470+
### 10.4 裁决判据
471+
472+
阶段一(`rules-ascendc` + 一个 hello kernel)的判据有两条,第二条是整个实验的重点:
473+
474+
1. 在真机 / 模拟器上跑出正确结果,**断言设备名而非结果数值**(结果数值在静默回退时
475+
同样正确)。
476+
2. **`git diff src/` 为空。**
477+
478+
第二条为否,即"这条架构主张在第一个陌生厂商面前就没成立" —— 这个结论比移植成功更有
479+
价值,也更该早点知道。
480+
481+
### 10.5 范围警告
482+
483+
交付物是**验证物,不是承诺长期维护的 fork**。算子库切片钉在某个上游版本,不承诺跟随。
484+
否则这个实验会悄悄变成"维护一个 CANN 分支",而那不是任何人想签的字。
485+
486+
## 11. 准入自检(docs/05 附录 A)
243487

244488
|| 是否重复了别处已给出的答案 |
245489
|---|---|
246490
| `exports` | 否。没有任何 section 回答"这个产物发布哪些符号" |
247491
| `link-flag` | 否。`ldflags` 是声明式的,本项是构建程序算出来的 |
248492
| `role = "manifest"` | 否,且**刻意复用** `[runtime].artifacts` 而非新开 section |
493+
| `accelerator = "none"` | 否,且**刻意复用** `os = "none"` 的既有拼法,不新造词 |
494+
| 生成输入粒度 | 否。现有语义是包级,本项是同一概念的细化,不是第二个概念 |
495+
| Fortran ||
249496
| 探测库 | 不是键 |
250497

251498
四项均为封闭语法、开放词表:`exports` 的内容由作者定,引擎只负责渲染;
252499
`link-flag` 的内容引擎不解释;role 的白名单加一项而语义由读者定义。
253500

254-
## 7. 判据
501+
## 12. 判据
255502

256503
每条都要求两侧可测 —— 拿掉实现会红,而不是"没测成"与"通过"同读数。
257504

@@ -263,18 +510,35 @@ int main() {
263510
| C4 | 构建程序发出的 `link-flag` 出现在链接命令行上,顺序在 `ldflags` 之后;**且不出现在消费者的链接行上**(私有性的反向断言) |
264511
| C5 | `role = "manifest"` 的文件在打包后位于声明的相对路径上,内容里的路径为包内相对路径。判据读**打包后的产物**,不读构建目录 |
265512
| C6 | 探测库在一台**没有宿主编译器**的机器上仍能完成探测。这是 §5.2 唯一能证伪的判据 |
513+
| C7 | 声明 `cfg(accelerator = "none")` 的回退源码,在 `accel` 为空时编译、在任意后端被命名时不编译;**且新增一个后端后该谓词的行为不变** —— 这条才是本项的理由,单后端下绿零信息量 |
514+
| C8 | 一个包同时具备生成头与千级编译边时,生成头****阻塞与它无关的编译边。对照组是今天的包级栅栏 |
515+
516+
**Fortran(§6.3)没有判据,因为本文没有给它设计。** 这是有意的:一个没有设计的条目配上
517+
一条判据,会让它看起来比实际成熟。它在 §13 里也不占期次。
266518

267519
C6 值得单独说明:它是这批里唯一无法在开发机上验证的判据 —— 开发机总有
268520
`/usr/bin/cc`,一个错误读宿主的实现在那里永远绿。它必须跑在 hermetic 容器里,
269521
本仓库已有 `hermetic e2e (no host toolchain, container)` 这个 job。
270522

271-
## 8. 分期
523+
## 13. 分期
272524

273525
|| 内容 | 依据 |
274526
|---|---|---|
275-
|| `link-flag` | 最小:一条指令 + 一处透传。且它是 §2.4 的逃生口,应先于 `exports` 落地 |
527+
|| `accelerator = "none"` | 最小,且它修的是一条**会随生态增长而静默失效**的写法 —— 越晚落地,要改的既有 manifest 越多 |
528+
|| `link-flag` | 同样最小:一条指令 + 一处透传。且它是 §2.4 的逃生口,应先于 `exports` 落地 |
276529
|| `exports` + 隐含 hidden | 打开"可发布稳定 ABI 的 `.so`"这一档,同时惠及运行时与驱动 |
277530
|| `role = "manifest"` + `mcpp:artifact` | 只有驱动这一档需要;且受 §4.2 的发布顺序约束,越早落地引擎越好 |
278531
|| `mcpplibs:probe` | 与引擎正交,任何时候可做;但 C6 要求它一开始就跑在 hermetic job 里 |
532+
|| 生成输入粒度 | 触发条件明确(§6.2),未达到该规模前不做 |
533+
|| Fortran | 已识别,本文不给设计 |
534+
| 并行 | RDC(`rules-cuda`)、`.omp`| 插件侧,不依赖以上任何一项 |
535+
536+
一、二、三期合计的引擎改动量小于 RDC 一项,且互不阻塞。RDC 归插件侧之后,**关键路径
537+
上不再有大件**
279538

280-
前三期合计的引擎改动量小于 RDC 一项,且互不阻塞。
539+
## 14. 变更记录
540+
541+
| 版本 | 变更 |
542+
|---|---|
543+
| 初稿 | 四处通用缺口:`exports``link-flag`、包内布局、探测库。配置头与包内布局在核实 main 后各自缩小 |
544+
| 修订 | 两处判断被推翻并就地更正:**RDC 归插件侧,引擎零改动**(§0.2);**stdpar 可岛化,初稿的"互斥"说法过头**(§8.1)。新增三处缺口(§6)、归属规则(§7)、编程模型三分法(§8)、术语对齐建议(§9)、以及用陌生厂商证伪的实验(§10) |

0 commit comments

Comments
 (0)