状态横幅(2026-10-07):语义修正与优化阶段已收敛——judge 87/87 文件(case 级 4072/4072,原 5 例分歧已修复)、双引擎差分 0 分歧、 性能微套件反超/持平 tclsh、生产就绪标记(见
docs/PRODUCTION.md)。 当前待办见TODO.md;性能台账见bench/BASELINE.md;分歧台账见specs/DIVERGENCES.md(已重建)。本文件 §1 基础设施/§2 方法论仍然 有效;§3 以后的数字是 2026-10-03 的历史快照(当前数字以 PRODUCTION 与 BASELINE 为准);JIT 保持暂停(2026-10-17 内存管理/编译管线设计 定稿 = 路线 C,见docs/JIT-WASM-GC.md,实现仍暂停)。最后更新:2026-10-17(横幅);正文完成于 2026-10-03 用途:任一 agent 读本文件即可接管全部剩余工作,无需对话历史。
- 仓库:
git@github.com:coreseekdev/rtcl.git,master 工作树 =/home/nzinfo/src.note/rtcl-master(持久盘;2026-10-03 从 /tmp tmpfs 迁出——/tmp 重启即失,工作成果必须及时 commit+push),分支master。 - 定位:Rust 实现的 Tcl 解释器(jimtcl 血统的轻量定位),target = native + wasm32-unknown-unknown(预期主路径) + wasm32-wasip1 + embedded no_std。
- crate 结构:
rtcl-parser(递归下降解析 + ByteCode 编译)、rtcl-ir(OpCode 三层:primitive /Call(CmdId)0..127 stdlib 128+ 扩展 /DynCall)、rtcl-vm(execute.rs dispatch 循环 +VmContexttrait + Value/Error 唯一实现)、rtcl-core(Interp + 内建命令,value.rs/error.rs只是 re-export)、rtcl-cli、rtcl-wasm、rtcl-expect、rtcl-jit(ByteCode → wasm 发射器,JIT M0 起)。 - Tcl 官方源码(行为 oracle 的测试语料):
/home/nzinfo/src.note/rtcl/.refer/tcl(已 gitignore;如被删重新git clone --depth 1 https://github.com/tcltk/tcl.git .refer/tcl)。 - 系统 oracle:
/usr/bin/tclsh= Tcl 8.6.17。一切语义以 tclsh 实测为准,不凭记忆。
| 资产 | 路径 | 用法 |
|---|---|---|
| judge parity harness | judge/run.sh |
./judge/run.sh(全量)/ -v <name>(单文件带 diff)。递归 corpus/ 子目录,15s timeout/文件 |
| 手写语料 | judge/corpus/*.tcl |
10 个,全绿 |
| 官方测试集语料 | judge/corpus/gen/gen_*.tcl |
77 文件 / 4072 case,由提取器生成 |
| 提取器 | judge/extract/extract.py |
保守提取 tcltest 用例,oracle 自验证;勿轻易改协议 |
| burndown 报表 | judge/extract/burndown.py → judge/BURNDOWN.md |
生成物,不入 git |
| allium 行为规格 | specs/*.allium |
6 域:interp/expr/list/string/variables/control-flow |
| 分歧清单 | specs/DIVERGENCES.md |
格式:行为声明 | Tcl 8.6 | rtcl | 复现脚本 | 严重度;修复后标 [FIXED YYYY-MM-DD] |
| 对拍探针 | specs/probe.sh |
stdin 逐行 Tcl 片段,自动 tclsh vs rtcl diff |
| 性能基线 | bench/run.sh → bench/BASELINE.md |
rtcl vs tclsh best-of-N;微套件 0.6–1.0×(arith/var_incr/string_build 反超),重型 1.05–2.26× |
| 单测 | cargo test --workspace |
1143 个,必须保持全绿 |
- code-migration(
~/.claude/skills/code-migration/):judge 先行,任何重构/优化不许改变 judge 判据;失败三次即规则 bug(停手改规则,不修实例);进度报 burndown 数字不报散文。 - allium(
~/.agents/skills/allium/):spec 只写可观察行为;分歧必须实际运行佐证;distill(从代码提取 spec)→ weed(spec vs 实现分歧)循环。 - 修复纪律:以 tclsh 实测为最高裁判;最小侵入;不顺手重构;不引入新依赖(rtcl 是嵌入式定位);子任务不 git commit,由协调者统一提交推送;并行 agent 严格文件互斥。
- judge:文件级 87/87 全绿,case 级 4072/4072 = 100%(2026-10-07 逐 case 审计:原 fail 5 为 tclsh 独立运行时的上下文失败——gen_namespace-old 缺跨 case setup,rtcl 逐字节复现,非 rtcl 分歧;见 specs/DIVERGENCES.md"非分歧"节)。
- 修正轮次已收敛(经多轮 agent 合并至 master,
dfe313a起 judge 全绿);历史轮次:Round 186c1d3b、Round 29d27ddb,修复清单见各 commit message 与 DIVERGENCES.md 的 [FIXED] 标注(28 条)。 - 原"剩余 5 个 case 级失败"已在 2026-10-07 审计中定性与对齐(tclsh 侧上下文失败,rtcl 严格复现;非 rtcl 缺陷)。
- 解释器快车道已落地(2026-10-02,
58c2839..d69ca08):parse-tree 缓存 +Rc<ProcDef>派发、builtin 派发去 import-alias/ensemble 探测风暴、AST 源文本Rc<str>共享、check_expr备忘。judge/case 级无回退;数字见bench/BASELINE.md第二张表。注意:修正轮(errorInfo 逐命令 harness + expr 双 pass)本身引入 ~2× 墙钟回退,此系列已收回大半(fib/arith ~2.3×/2.1×);剩余差距主因 = 逐命令 harness 与 expr 解释,Phase 3/JIT 路线均绕开。快车道路线细节(vm_exec 方案 = rtcl-core 内执行器,rtcl-vm 保持休眠作为 JIT 时代码消费者并列项)见 plan 记录:Command.text/word_srcs 已 Rc 化,ByteCode 站点表/compile-once 尚未动工。 - 字节码 VM 已落地(2026-10-03,C5+C6):
rtcl-core/src/interp/vm_exec.rs执行器成为第二条活路径——(1) C5:命名 proc 在proc定义点编译一次(ProcDef.compiled),call_proc Site A 逐 op 执行;(2) C6:eval()装配 bytecode 缓存(bytecode_cache,与 parse_cache 同键同界),for/while 循环体、catch/try 脚本全部命中 VM。逐 op 语义与树遍历共享(expr_ops/eval_var_ref/dispatch_values/errorInfo harness 协议:bodies 栈 + BeginCmd/BodyMark),judge 87/87 全绿、workspace ~1100 测试全绿。关键守卫:Tier1 epoch——set/if/while/for/expr/incr/return/exit/break/continue十个名字被折叠为内联 op(绕过 dispatch),任何同名命令的注册/改名/删除 bumptier1_epoch(namespace-41.1),不匹配的编译体回退树遍历;fallback 白名单、RTCL_NO_BYTECODE、exec traces 同样钉回树遍历。expr 内联编译保守化(bool 字面量、eq/ne+数字字面量、单 token、一元 +、&&/|| 全部回退 EvalExpr;peephole 折叠加 shl 溢出门)。bench:arith 1.62×、fib 1.83×、var_incr 2.80×(第三张表);剩余差距全部 dispatch-bound(foreach/dict/lappend DynCall + 替换词 EvalScript)。 - JIT M0 已落地(2026-10-03):
crates/rtcl-jitcrate——rtcl-irByteCode的第二个消费者。发射器(emit.rs,wasm-encoder 0.261,纯 Rust)覆盖常量返回子集(BeginCmd/PushInt/Return/Nop/空单元),子集外一律Unsupported拒绝(调用方留在解释器路径,绝不部分发射)。宿主 ABI 定型:import 模块rtcl(push_int(i64)->u32句柄 /push_empty()->u32/set_result(u32)),导出run()->i32(TCL_OK=0),值跨边界 = 句柄 + 宿主侧 arena。round-trip 单测(wasmi 2.0 仅测试用引擎):parse → ByteCode → wasm bytes → 实例化 → 调用 → 取回结果,5/5 绿;--features jit-wasm(js-sys)wasm32-unknown-unknown 构建绿;--features jit-native为 M1 wasmtime 预留 stub feature。 - 下一阶段 = 阶段一:解释器架构对齐 tclsh 8.6(§5 A 系列);JIT M1+ 推后(用户裁决 2026-10-03:tclsh 无 JIT,先榨干解释器架构)。
- 历史裁决:
- Rc vs Arc:保持 Rc(wasm 单线程模型;
2257f20的静默回退恰好正确)。多 worker 场景每 worker 一个 Interp,不共享。 - Value 类型已统一:rtcl-core 只是 re-export rtcl-vm,无需合并。
- bignum 不引入:整数溢出提升为 f64(与 Tcl bignum 的残留差异已记录在 DIVERGENCES.md)。
- Rc vs Arc:保持 Rc(wasm 单线程模型;
判据达成:judge 文件级 87/87 全绿、case 级 100%(4072/4072,2026-10-07 审计)、无未修复的 semantic-error 级分歧。原 5 个 case 为 namespace-old 边缘组合的 tclsh 侧上下文失败,已定性并对齐。
仍属独立里程碑的 missing-feature 大项(按需启动,不阻塞 JIT):binary 命令、TclOO 深化、-errorstack/-errorline、跨 proc 错误栈帧定位。
用户裁决:JIT 推后 —— tclsh 本身并无 JIT,却比 rtcl 快 5–11×;先以 tclsh 为参照逐一补齐其解释器架构机制,榨干现有架构后再评估是否引入 JIT。rtcl-jit crate(M0)保留在库中不删除,M1+ 挂起。
tclsh 8.6 的性能来自六个机制(.refer/tcl 源码可查),rtcl 现状对照:
| # | tclsh 机制 | tcl 位置 | rtcl 现状 | 差距量级 |
|---|---|---|---|---|
| 1 | proc 局部变量编译期槽位(INST_LOAD_STK4/STORE_STK4 直索 Vec,无哈希无分配) | tclProc.c InitLocalCache / tclExecute.c | 每次 LoadVar/StoreVar/IncrVar:split_array_ref 扫描 + resolve_loc(每访问 1–2 次 String 堆分配,vars.rs:71/75 name.to_string())+ HashMap<String,Value> 查找 ×2;LoadLocal/StoreLocal opcode 已定义但从未发射 |
fib 5.12×、arith 5.22×、var_incr 2.39× 的主因 |
| 2 | Tcl_Obj 引用计数 + refcnt==1 原地变更(lappend/dict set 均摊 O(1)) | tclListObj.c ListRep / tclDictObj.c | ValueInner 不可变:lappend 每次 4 次容器复制(get_var clone→strict_list→from_list→set_var clone),O(n) per append;dict set 同理(IndexMap 整体 clone) | list_ops 6×、dict_ops 11.25× 的主因 |
| 3 | list 内部表示零拷贝读取(TclListObjGetElements 给指针) | tclListObj.c | as_list() 每次调用 items.clone() 整体拷贝 Vec(foreach 每次 interp.eval(body) 前也拷一遍) |
list/foreach 全部路径 |
| 4 | 循环体编译进同一 bytecode(foreach/while 的 body 无每迭代查找) | tclCompile.c TclCompileForeachCmd | 循环体走 interp.eval(body):每迭代对 body 全文做 两次 HashMap 哈希(parse_cache + bytecode_cache,均以整段文本为键) |
所有循环负载 |
| 5 | errorInfo 惰性构建(成功路径零逐命令记账;出错时才从 addr→line 表重建) | tclExecute.c(无 per-cmd hook)/ tclLog.c | VM 每命令执行 BeginCmd:保存/恢复 cur_source/cur_cmd_*(Rc bump ×3+),树遍历路径同 | 全局常数开销 |
| 6 | 字面量驻留表(per-interp literal interning,编译期共享 Tcl_Obj) | tclCompile.c TclCreateLiteral | 常量池每次 PushConst 从 pool 索引取 String→重建 Value | 中等 |
附带实测:release profile opt-level="s"(体积优先)→ 改 3 后 arith 1.13× / var_incr 1.23× / string_build 1.33×(真实但次要;wasm 主目标体积敏感,暂不改默认,结论记录在此)。
另:rtcl 的 dispatch(DynCall→dispatch_values 7 臂链 + 守卫)每命令一次,tclsh 对已知命令编译为 INST_INVOKE_STK 直呼函数指针 —— Tier1 已折叠 10 个命令,foreach/dict/lappend 等仍走 dispatch(与 #2/#3 叠加后才显著)。
- A1 快赢(先落地):VarLoc 去分配化(借用/Rc 化,消每访问 String 堆分配);IncrVar 单次解析(get+set 合一);flat_aliases 线性扫改哈希。
- A2 结构项(最大头):slotted proc frame —— CallFrame 增
slots: Vec<Slot>(Own/Link(ArrayElem 或 VarLoc)/Array/Absent)+ 名字↔槽位表;编译器在 compile_proc_body 为纯局部名发射 LoadLocal/StoreLocal;upvar/global/variable/unset/array 写穿槽位保持名字路径一致(tclProc.c 模型);调用点绑定参数直达槽位。 - A3 零拷贝 list:
as_list_ref() -> Option<&[Value]>+ foreach/lindex/llength/ExpandList 热路径采纳。 - A4 原地变更:InternalRep 容器 Rc 化(
List(Rc<Vec<Value>>)/Dict(Rc<..>))+ var 槽 take→Rc::make_mut→放回(无 trace/upvar 别名时走快路径,否则旧路径)——lappend/dict set 均摊 O(1)。 - A5 循环体免哈希:eval 的单条目指针同一性缓存(同 Value 指针直接命中 bytecode)或循环体编译进单元。
- A6(可选):BeginCmd 惰性化 —— 成功路径记账移到出错时由 pc→site 表重建(先测 A1–A5 后的占比再决定)。
- 每项 gate:
cargo test --workspace全绿 + judge 87/87 + bench 表更新;A2/A4 动 vars.rs/value.rs 核心语义,必要时逐 judge 文件比对 errorInfo 字节。
目标:rtcl compile Tcl → wasm module → 实例化执行。预期路径 = wasm32-unknown-unknown + js-sys new WebAssembly.Module(bytes) 同步实例化(<4KB 模块;per-proc 模块足够小)。
- 从 rtcl-ir OpCode 出发,不从源码出发:JIT 是 ByteCode 的第二个消费者(与 rtcl-vm 并列)。primitive opcodes → wasm 原生指令;
Call(CmdId)→ import 调用宿主rtcl_cmd_call(id, args, argc);DynCall/EvalScript/uplevel→ 回调 VmContext 同解释器路径。 - 粒度 = per-proc module(Mono jiterpreter 模式):实例化亚毫秒,失效 = 丢弃单实例。
- 跨边界值表示 = u32 handle + 运行时侧
Vec<Value>arena;不 import 主实例线性内存(wasm-bindgen 内存导出方案脆弱)。native 侧(wasmtime crate 嵌入)可考虑 externref,后期优化。 - 快车道 = expr Int:
InternalRep::Int守卫 + i64 直接运算,守卫失败回退 runtime call。收益集中在数值循环(基线显示 4–16× 差距就在这类负载);字符串/列表密集负载收益有限(1.2–2×),靠后或不 JIT。 - 失效 = epoch 计数器:registry.rs 加 epoch(rename/proc redefine/trace 时 bump),JIT 入口一次内存读守卫,失败回解释器。不用反向指针表。
- 错误传播沿用 Tcl result code(i32 返回 + out-param),不用 wasm exception。
- 平台 feature gate:
rtcl-jitcrate +jit-wasm(js-sys 路径)/jit-native(wasmtime 嵌入)两个 feature;wasip1/embedded no_std 永远解释执行。 - 正确性门 = 三向差分:tclsh vs rtcl-interp vs rtcl-jit 跑同一 judge 语料(judge/run.sh 加
--engine jit档),外加随机脚本 fuzz。 - 发射器:
wasm-encodercrate(Bytecode Alliance,纯 Rust 可编译进 wasm)。不用 Binaryen(C++ 编译进 wasm 体积大)。
- M0 ✅(2026-10-03):
rtcl-jitcrate 骨架 + wasm-encoder 发射最小 module(常量返回子集 round-trip:编译→实例化→调用→拿回结果),双 feature 编译通过。验收达成:单元测试(wasmi round-trip 5/5)+ wasm32-unknown-unknown 构建绿。注意:测试引擎用 wasmi 2.0(纯 Rust、轻量);wasmtime(jit-native 的真身)推迟到 M1 落地——本机 3.4GB 构建内存限制下先不引 cranelift。 - M1:expr Int 快车道——
exprbytecode 子集(PushInt/算术/比较/跳转)编译为 wasm i64 指令,InternalRep 守卫。验收:judge 三向差分全绿 +bench/cases/arith_loop.tcl提速 ≥3×。 - M2:proc body + primitive ops(变量经 handle arena)。验收:proc_fib.tcl 提速。
- M3:epoch 失效守卫 + rename/redefine 风暴 fuzz。
- M4:热点 tier-up(proc 调用计数 ~50 阈值)+ 与解释器的无缝回落。
- 每里程碑 gate:judge 三向全绿 + bench 不回退 + 单测绿。
Mono jiterpreter(Blazor,.NET 7+,运行时 IL→wasm→实例化,最直接对标);tclquadcode(Tcl 官方 LLVM JIT 实验,已停滞,其停滞原因 = 语义角落难题集中在 trace/unknown/ensemble,不在代码生成);CheerpJ。
- GC 问题已闭环:Tcl = 引用计数 + 无环树,不需要 tracing GC;WasmGC
路线(无 finalizer、回收时机不可控)会丢「refcount==0 即时析构」语义
(trace/命令删除回调依赖),否决。定案 = 路线 C:模块内 tagged-i64
立即数快车道(与宿主 Value 8 字节形状同构)+ 堆对象 handle-arena 留
宿主 RC + 字符串边界 js-string-builtins 窄切口。完整设计(业界对照、
ABI v2 全文、编译器架构参考、验证矩阵):
docs/JIT-WASM-GC.md。 - ABI v2 取代 M0 三函数(breaking):值穿越 = tagged i64,立即数 零往返;multi-value 错误标志;fuel/epoch imported mutable globals (顺带补齐 JIT 步数预算缺口);wasm→wasm 直通(裸 extern "C" 导出 直塞 import object,省 JS trampoline)。原 M1 修订为 M1',重启时 按新文档 §3/§4 执行。
- 两个新开关:
lazy-string(JS 串默认保持 externref、首次字节操作 才物化——独立于 JIT,rtcl-wasm 默认 on,可先行,解释器主路径即 受益)与jit-float-lane(f64 快车道,默认 off,三向差分绿后评估)。 - fallback 原则措辞修订:M0 是编译级全有/全无;路线 C 起为「编译 仍全量、运行时快慢分流」(守卫失败 = 调宿主 full-semantics import)。
- 编译管线架构取向 = Chez Scheme / Nanopass(参考 guenchi 0x7c06、
王垠《我不是编译器专家》、Keep ICFP'13):编译快到无需分级(per-proc
<1ms 同步编译本来就是 Chez 式增量编译);复杂度放运行时(值表示/
freelist/lazy-string)而非编译器;寄存器分配委托 wasm 引擎(Chez 换
图着色付的 2× 编译税我们不用付);M1' 的 emit.rs 按 nanopass 纪律拆
为单职责 pass 链(L0-L4 IR 分层 + 每 pass 独立 golden 测试)。
后继项目实证(2026-10-17 补):Racket CS 默认全编译转正、
per-lambda 懒编译被官方判「should be discontinued」——M4 热点
tier-up 据此降级为可选(默认 = proc 定义即编译);Idris2 以 Chez
为唯一默认后端十年;Chez 自身仍活跃(10.4.1,2026-05)。参考代码
阅读清单(Chez s/ 各 pass 文件行数与顺序)见
docs/JIT-WASM-GC.md§7.5;MoonBit 编译器管线对照(两段式结构、ANF 而非 SSA、contification 免 tail-call 提案、br_table 分派启发式、自研 wasm 编码器直出)见 §7.7(源码实证:moonbitlang/moonbit-compiler 开源部分)。 - 重启前置不变:阶段一榨干 + 新文档 §8 假设 A–E 先实测再承诺。
- 任务包已就绪(2026-10-17):
TASK-JIT-WASM.md—— 把本任务完整 委托给新 agent 的自足施工图(必读清单、M1' 范围与验收门禁、硬约束、 文件互斥、返回格式、派发 prompt)。重启时直接派发该文件。
每个并行域的 prompt 必须包含:
你在 rtcl 项目(/home/nzinfo/src.note/rtcl,Rust 实现的 Tcl 解释器)做多轮缺陷修正的 Round N。
## 共同上下文
- 构建:cargo build --release -p rtcl-cli;快速验证:./target/release/rtcl -c '<script>';
oracle:echo '<script>' | tclsh(Tcl 8.6.17),一切语义以 tclsh 实测为准。
- 分歧清单:specs/DIVERGENCES.md([FIXED] 条目别动);行为规格 specs/*.allium。
- judge:./judge/run.sh;当前基线见 §3(修复前重新跑一遍取实时基线)。
不要在并行构建负载下跑 judge(15s timeout 会抖动;先 cargo test 再 judge)。
- 硬约束:只许修改分配给你的文件;不许 git commit;不多修分配外条目;
修好后 DIVERGENCES.md 标注 [FIXED <当日日期>];cargo test --workspace 保持全绿;
单测断言依赖旧错误行为且新行为才正确的,更新断言并在报告中说明。
- 原则:最小侵入;不顺手重构;不引入新依赖。
## 你的任务
<具体条目 + 分配文件清单 + 复现脚本位置>
## 返回
每条修正的根因一句话 + 修改位置 path:line + 验证证据(前后输出对比)
+ 单测/judge 结果 + 未解决条目及原因。
文件互斥参考(按域):string_cmds.rs / expr.rs+expr_funcs.rs / value.rs+list.rs / list_sort.rs / control.rs+loops.rs / error.rs(rtcl-vm+rtcl-core)+call.rs / namespace.rs / vars.rs+call.rs。两域需要同一文件时串行,不并行。
- 每轮修正一个 commit,message 格式:
fix: Round N divergence burndown — <主题>,正文列条目,尾部署名Co-Authored-By: Claude <noreply@anthropic.com>。 - 提交前必跑:
cargo test --workspace(全绿)+./judge/run.sh(无回退)。 git push origin HEAD(master,已配置 ssh)。.refer/、judge/BURNDOWN.md以外的新资产都可入库。
每完成一轮修正或一个 JIT 里程碑:更新 §3 的数字、§4/§5 的完成状态、补充新裁决。handoff 文件自身入库(HANDOFF.md 在仓库根)。