From 320d25243f2352f7aeb9512e2e3a112df1fd1f60 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:00:15 +0800 Subject: [PATCH 01/58] docs: define frontend machine-boundary cleanup plan --- .../2026-10-23-frontend-machine-boundary.md | 249 ++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md diff --git a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md new file mode 100644 index 00000000..e8eaf539 --- /dev/null +++ b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md @@ -0,0 +1,249 @@ +# 前端机器边界清理计划 + +## 概述 + +这份计划处理前端代码中的机器映射泄漏。前端指 `corec` 中从词法、语法、类型检查到 IR 生成的部分。后端指 `src/arch`、`src/format`、`src/os` 和 `src/targets`。计划只改变代码所在层和接口位置,不改变语言语义、`.ccr` 格式、ELF 产物和运行结果。 + +## 范围 + +| 任务 | 内容 | 主要位置 | 产物要求 | +|---|---|---|---| +| 1 | 把 x86 后端数组和扩容函数移出共享前端文件 | `globals.cr`、`dyn_arr.cr`、`src/arch/x86_64/` | 逐字节不变 | +| 2 | 把后端入口、后端旗标和分配器契约移出 `src/compiler` | `corearch.cr`、`backend_flags.cr`、`regalloc-consistency.cr` | 逐字节不变 | +| 3 | 拆分 HDFG 构图与 `.ccr` 线性调度降级 | `dataflow.cr` | 逐字节不变 | +| 4 | 建立 Mapping 接口,迁移尺寸、对齐和偏移计算 | `ir_gen.cr`、`provenance_verify.cr`、`ptr_analysis.cr` | 逐字节不变 | +| 5 | 把环境变量和 OS 内建判断移出语义检查器 | `checker.cr`、`main.cr`、`globals.cr` | 行为不变 | +| 6 | 把 `.ccr` 记录布局和后端优化键移出语义模块 | `type_terms.cr`、`iface_registry.cr`、`ty_shadow.cr`、`ast.cr` | 格式不变 | +| 7 | 迁移数值字面量转换,登记无法在当前 ABI 下移除的机器限制 | `lexer.cr`、`parser.cr`、`goroutine.cr` | 行为不变 | + +## 全局约束 + +1. 每个任务单独提交。一个任务验证通过后再开始下一个任务。 +2. 任务 1 到 4 要求 `.ccr`、ELF 和 canary 值逐字节不变。任务 5 到 7 要求可观察行为不变;如果必须改变产物,先停止并记录原因。 +3. 不使用全局字符串替换修改代码。移动函数时保留函数名、参数名、常量名和注释中的技术事实。 +4. `docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md` 是当前工作区中的独立文件,不纳入本次提交。 +5. 每个任务完成后运行: + +```bash +nice -n 19 python3 build_selfhost_native.py +./build/corec check src/compiler +nice -n 19 python3 tests/selfhost/test_type_engine.py +nice -n 19 python3 tests/selfhost/test_purity.py +``` + +6. 任务 1、2、3、6、7 还要运行: + +```bash +nice -n 19 bash tools/baseline/canary_check.sh +``` + +7. 任务 4 还要运行旧计划第 5 节的逐处证明检查,保留 `-1` 后备路径。正控和反控都通过后才能继续。 +8. 全部任务完成后运行三阶段自举,并比较 `corec2` 与 `corec3`: + +```bash +nice -n 19 ./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 +nice -n 19 /tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 +cmp /tmp/corec2 /tmp/corec3 +``` + +## 任务 1:移出 x86 后端数组 + +### 文件 + +- `src/compiler/globals.cr` +- `src/compiler/dyn_arr.cr` +- 新建 `src/arch/x86_64/backend_globals.cr` +- 新建 `src/arch/x86_64/backend_arrays.cr` +- `build_selfhost_native.py` +- `src/targets/x86_64-linux/_import.cr` + +### 步骤 + +1. 在 `src/arch/x86_64/backend_globals.cr` 中声明所有 `g_x86_*` 全局变量。保留原有的字段顺序、类型和注释。 +2. 在 `src/arch/x86_64/backend_arrays.cr` 中放置 `grow_x86_vars`、`grow_is_enum`、`grow_is_global`、`grow_global_off`、`grow_str_offs`、`grow_rip_patch`。 +3. 从 `src/compiler/globals.cr` 删除这些 `g_x86_*` 声明。保留 `g_opt_*`、`g_sgs`、`.so` 侧表和规格注记侧表。 +4. 从 `src/compiler/dyn_arr.cr` 删除 x86 扩容函数。保留 `grow_so_side`、`grow_spec_side` 和其余前端扩容函数。 +5. 在 `build_selfhost_native.py` 的 `arch_x86_64_files` 开头加入两个新文件,顺序为 `backend_globals.cr`、`backend_arrays.cr`、`regalloc.cr`。 +6. 在 `src/targets/x86_64-linux/_import.cr` 中加入 `import backend_globals` 和 `import backend_arrays`,位置在 `import regalloc` 之前。 + +### 验收 + +1. 构建通过,`corec`、`corearch`、`corelsp` 三个清单都通过存在性检查。 +2. `./build/corec check src/compiler` 通过。 +3. `src/compiler` 中不再出现 `g_x86_` 或 `grow_x86_`。 +4. canary 五条全部通过。 +5. `.ccr` 和 ELF 产物与改动前逐字节相同。 + +## 任务 2:移出后端入口和契约文件 + +### 文件 + +- `src/compiler/corearch.cr` 移到 `src/arch/x86_64/corearch.cr` +- `src/compiler/backend_flags.cr` 移到 `src/arch/x86_64/backend_flags.cr` +- `src/compiler/regalloc-consistency.cr` 移到 `docs/maintainer/design/regalloc-consistency.md` +- `build_selfhost_native.py` +- `src/targets/x86_64-linux/_import.cr` +- 相关路径引用 + +### 步骤 + +1. 移动文件,保留文件内部函数名和代码。 +2. 更新 `backend_support_files` 中的路径。 +3. 更新 `src/targets/x86_64-linux/_import.cr` 的导入名。 +4. 搜索 `src/compiler/corearch.cr`、`src/compiler/backend_flags.cr` 和 `regalloc-consistency.cr` 的路径引用,全部改为新位置。 +5. 不修改 `src/targets/x86_64-linux/main.cr` 的行为。 + +### 验收 + +1. 构建通过。 +2. `src/compiler` 目录不再包含这三个文件。 +3. canary 五条全部通过。 +4. `corearch` 产物逐字节不变。 + +## 任务 3:拆分 dataflow 的构图与降级 + +### 文件 + +- `src/compiler/dataflow.cr` +- 新建 `src/compiler/ccr_lower.cr` +- `build_selfhost_native.py` + +### 步骤 + +1. 保留 HDFG 节点、区域、变量生产者、纯度计算和 DOT 输出在 `dataflow.cr`。 +2. 把 `lower_to_ccr` 及其专用辅助函数移到 `ccr_lower.cr`。 +3. 在 `corec_files` 中把 `ccr_lower.cr` 放在 `dataflow.cr` 之后、`ccr_io.cr` 之前。 +4. `corearch` 清单不加入 `ccr_lower.cr`,因为后端从 `.ccr` 重建线性调度。 +5. 保留所有函数名和签名。 + +### 验收 + +1. 构建通过。 +2. `./build/corec check src/compiler` 通过。 +3. 全量自举产物逐字节一致。 +4. canary 五条全部通过。 + +## 任务 4:建立 Mapping 接口 + +### 文件 + +- 新建 `src/compiler/mapping.cr` +- `src/compiler/ir_gen.cr` +- `src/compiler/provenance_verify.cr` +- `src/compiler/ptr_analysis.cr` +- `build_selfhost_native.py` + +### 接口 + +```core +fn mp_size(ti: int) -> int +fn mp_align(ti: int) -> int +fn mp_field_offset(field_index: int) -> int +fn mp_stride(ti: int) -> int +fn mp_extent(count: int, ti: int) -> int +fn mp_offset(index: int, ti: int) -> int +``` + +这些函数在 `mapping.cr` 中实现。当前实现使用经典 x86-64 映射,返回值与现有 `type_size`、`type_align` 和 `* 8` 完全相同。接口是后续非经典映射的替换点,不在本任务改变数值。 + +### 步骤 + +1. 把 `ir_gen.cr` 的 `type_size` 和 `type_align` 函数体改为调用 `mp_size` 和 `mp_align`,或者把调用点直接改为 `mp_size` 和 `mp_align`。 +2. 把 `provenance_verify.cr` 的两处 `s1 * 8` 和 `si_field_count(fi) * 8` 改为 `mp_extent`。 +3. 把 `ptr_analysis.cr` 的 `idx_val * 8` 和 `low * 8` 改为 `mp_offset`。 +4. 保留所有 `-1` 未知分支和运行时检查路径。 +5. 在 `corec_files` 中加入 `mapping.cr`,位置在 `ast.cr` 之后、`ir_gen.cr` 之前。 + +### 验收 + +1. 构建通过。 +2. 旧计划第 5 节逐处证明表更新,`-1` 路径仍可触发。 +3. 正控程序仍被拒绝,反控程序仍返回 0。 +4. `.ccr`、ELF 和 canary 值逐字节不变。 + +## 任务 5:移出环境变量和 OS 内建判断 + +### 文件 + +- `src/compiler/checker.cr` +- `src/compiler/globals.cr` +- `src/compiler/main.cr` +- 新建 `src/compiler/prelude.cr` + +### 步骤 + +1. 在 `globals.cr` 增加 `g_s1p_enabled`,默认值为 0。 +2. 在 `main.cr` 启动时读取 `CORE_S1P`,把结果写入 `g_s1p_enabled`。 +3. 删除 `checker.cr` 中两处 `get_env("CORE_S1P")`,改为读取 `g_s1p_enabled`。 +4. 把 `syscall3` 和 `syscall4` 的返回类型判断移到 `prelude.cr`。`checker.cr` 只调用 `prelude_builtin_return_ti`。 +5. 删除未使用的 `g_ni_syscall3` 和 `g_ni_syscall4`。 +6. 不改变 `extern` 的可选类型拒绝规则。该规则属于外部接口边界,保留在解析器中,并在文档中记录原因。 + +### 验收 + +1. `CORE_S1P=1 ./build/corec check ` 与改动前输出相同。 +2. 不设置 `CORE_S1P` 时输出相同。 +3. `syscall3` 和 `syscall4` 的类型检查结果相同。 +4. 全部自举测试和 canary 通过。 + +## 任务 6:移出 `.ccr` 记录布局和后端优化键 + +### 文件 + +- 新建 `src/compiler/ccr_schema.cr` +- `src/compiler/type_terms.cr` +- `src/compiler/iface_registry.cr` +- `src/compiler/ty_shadow.cr` +- `src/compiler/ast.cr` +- `build_selfhost_native.py` +- `src/targets/x86_64-linux/_import.cr` + +### 步骤 + +1. 把 `ESZ_TYPE_TERM`、`OFF_TT_*`、`ESZ_IFACE_ENTRY`、`OFF_IFM_*` 和 `.ccr` 段布局常量移到 `ccr_schema.cr`。 +2. 在 `type_terms.cr`、`iface_registry.cr` 和 `ty_shadow.cr` 中保留语义结构和判定函数,只引用 `ccr_schema.cr` 的常量。 +3. 把 `OPT_KEY_REG_ASSIGN`、`OPT_KEY_STACK_SHARE` 和 `OPT_KEY_CSE` 移到后端使用的 `src/arch/x86_64/backend_schema.cr`,因为只有后端读取这些键。 +4. `ast.cr` 不再声明后端优化键。`ir_gen.cr` 和 `opt.cr` 不使用这些键。 +5. 更新 `corec`、`corearch`、`corelsp` 三个清单的常量可见顺序。 + +### 验收 + +1. 三个构建单元都通过。 +2. `.ccr` 的 TYPE、IFACE、NOD 段逐字节不变。 +3. `test_ccr_types.py`、`test_ccr_v7.py` 通过。 +4. canary 五条全部通过。 + +## 任务 7:数值字面量与执行策略 + +### 文件 + +- `src/compiler/lexer.cr` +- `src/compiler/parser.cr` +- `src/stdlib/goroutine.cr` +- 相关文档 + +### 步骤 + +1. 把 `str_to_f64_bits` 从 `lexer.cr` 移到 `parser.cr` 或独立的数值字面量模块。词法层只保留原始数字字符串。 +2. 为 `str_int_literal` 的 64 位上限增加明确注释,说明这是当前 token 载体的编码限制,不是语言语义上限。是否支持任意精度由后续多字整数设计决定,本任务不改变行为。 +3. 把 `goroutine.cr` 的 `alloc(16384)` 改为命名常量 `G_STACK_SIZE`,值仍为 16384。该常量属于运行时执行策略,不进入前端。 +4. 更新 `docs/maintainer/design/execution-model.md` 中的栈尺寸表述,说明常量位置。 + +### 验收 + +1. 浮点字面量、整数溢出错误和协程行为的测试结果不变。 +2. canary 五条全部通过。 +3. 文档链接目标存在。 + +## 不包含 + +1. 不改变 `.ccr` 格式、ELF 格式、SysV ABI 和 x86-64 指令编码。 +2. 不实现任意精度整数。 +3. 不改变 arena 的语义地位,也不改变解释器与 ELF 路径在 arena 和地址上的既有差异。 +4. 不修改 `docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md`。 + +## 未决问题 + +1. `extern` 的可选类型拒绝规则是否应移到独立的 FFI 映射层。当前保留在解析器,因为它描述外部接口的表示能力,而不是目标机器细节。 +2. `IR_ALLOC_ARRAY` 的元素步长 8 是否应进入 Mapping 接口。当前后端固定按 8 计算,前置改动需要同时修改 IR 语义和后端发射。 +3. 64 位整数上限是否解除,取决于多字整数的前端表示和后端运算是否完整覆盖。 From 28373e0f3f62787afb7e28456c1f568f249a7dea Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:01:56 +0800 Subject: [PATCH 02/58] refactor: move x86 backend arrays out of shared frontend files --- build_selfhost_native.py | 2 + src/arch/x86_64/backend_arrays.cr | 295 ++++++++++++++++++++++++++++ src/arch/x86_64/backend_globals.cr | 111 +++++++++++ src/compiler/dyn_arr.cr | 294 --------------------------- src/compiler/globals.cr | 109 +--------- src/targets/x86_64-linux/_import.cr | 2 + 6 files changed, 411 insertions(+), 402 deletions(-) create mode 100644 src/arch/x86_64/backend_arrays.cr create mode 100644 src/arch/x86_64/backend_globals.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index a9889d09..0a878745 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -62,6 +62,8 @@ # 编码原语 → instr 之后;frame.cr → tag2l(pf_epilogue 函数尾块调 e2_mw_*; # tag2l 反向消费 frame 的 g2_tag_off——同轴双向引用,波 1 Task 4 裁定可接受)。 arch_x86_64_files = [ + 'src/arch/x86_64/backend_globals.cr', + 'src/arch/x86_64/backend_arrays.cr', 'src/arch/x86_64/regalloc.cr', 'src/arch/x86_64/sizes.cr', 'src/arch/x86_64/instr.cr', diff --git a/src/arch/x86_64/backend_arrays.cr b/src/arch/x86_64/backend_arrays.cr new file mode 100644 index 00000000..0e0330e5 --- /dev/null +++ b/src/arch/x86_64/backend_arrays.cr @@ -0,0 +1,295 @@ +// === backend_arrays.cr === +// Growth functions for x86-64 backend arrays. Loaded only by corearch. + +fn grow_x86_vars(needed: int): + if needed < g_x86_var_cap: + return + nc : ., mut = g_x86_var_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_vars, g_x86_var_cap * 8, nb) + g_x86_vars = nb + g_x86_var_cap = nc + +fn grow_is_enum(needed: int): + if needed < g_x86_is_enum_cap: + return + nc : ., mut = g_x86_is_enum_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_is_enum, g_x86_is_enum_cap * 8, nb) + g_x86_is_enum = nb + g_x86_is_enum_cap = nc + +fn grow_is_global(needed: int): + if needed < g_x86_global_cap: + return + nc : ., mut = g_x86_global_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_is_global, g_x86_global_cap * 8, nb) + g_x86_is_global = nb + g_x86_global_cap = nc + +fn grow_global_off(needed: int): + if needed < g_x86_global_off_cap: + return + nc : ., mut = g_x86_global_off_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_global_off, g_x86_global_off_cap * 8, nb) + g_x86_global_off = nb + g_x86_global_off_cap = nc + +fn grow_str_offs(needed: int): + if needed < g_x86_str_cap: + return + nc : ., mut = g_x86_str_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_str_offs, g_x86_str_cap * 8, nb) + g_x86_str_offs = nb + g_x86_str_cap = nc + +fn grow_rip_patch(needed: int): + if needed < g_x86_rip_patch_cap: + return + nc : ., mut = g_x86_rip_patch_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb1 := alloc(nc * 8) + _dyncpy(g_x86_rip_patch_pos, g_x86_rip_patch_cap * 8, nb1) + g_x86_rip_patch_pos = nb1 + nb2 := alloc(nc * 8) + _dyncpy(g_x86_rip_patch_globals, g_x86_rip_patch_cap * 8, nb2) + g_x86_rip_patch_globals = nb2 + g_x86_rip_patch_cap = nc + +// .so extension index side tables. They grow dynamically, following the rule that +// all arrays are dynamic byte buffers with no fixed limits. + +fn grow_ext_rel(needed: int): + if needed < g_x86_ext_rel_cap: + return + nc : ., mut = g_x86_ext_rel_cap * 2 + if nc < 32: + nc = 32 + if nc < needed: + nc = needed + 32 + sz := nc * 8 + n1 := alloc(sz) + _dyncpy(g_x86_ext_rel_pos, g_x86_ext_rel_cap*8, n1) + g_x86_ext_rel_pos = n1 + n2 := alloc(sz) + _dyncpy(g_x86_ext_rel_name, g_x86_ext_rel_cap*8, n2) + g_x86_ext_rel_name = n2 + g_x86_ext_rel_cap = nc + +fn grow_func_offsets(needed: int): + if needed < g_x86_func_offsets_cap: + return + nc : ., mut = g_x86_func_offsets_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 16) + _dyncpy(g_x86_func_offsets, g_x86_func_offsets_cap * 16, nb) + g_x86_func_offsets = nb + g_x86_func_offsets_cap = nc + +fn grow_emit_vars(needed: int): + if needed < g_x86_emit_vars_cap: + return + nc : ., mut = g_x86_emit_vars_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_emit_vars, g_x86_emit_vars_cap * 8, nb) + g_x86_emit_vars = nb + g_x86_emit_vars_cap = nc + +fn grow_ret_patch(needed: int): + if needed < g_x86_ret_patch_cap: + return + nc : ., mut = g_x86_ret_patch_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_ret_patch_pos, g_x86_ret_patch_cap * 8, nb) + g_x86_ret_patch_pos = nb + g_x86_ret_patch_cap = nc + +fn grow_call_patch(needed: int): + if needed < g_x86_call_patch_cap: + return + nc : ., mut = g_x86_call_patch_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + n1 := alloc(nc * 8) + _dyncpy(g_x86_call_patch_pos, g_x86_call_patch_cap * 8, n1) + g_x86_call_patch_pos = n1 + n2 := alloc(nc * 8) + _dyncpy(g_x86_call_patch_name, g_x86_call_patch_cap * 8, n2) + g_x86_call_patch_name = n2 + g_x86_call_patch_cap = nc + +fn grow_fnaddr_patch(needed: int): + if needed < g_x86_fnaddr_patch_cap: + return + nc : ., mut = g_x86_fnaddr_patch_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + n1 := alloc(nc * 8) + _dyncpy(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_cap * 8, n1) + g_x86_fnaddr_patch_pos = n1 + n2 := alloc(nc * 8) + _dyncpy(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_cap * 8, n2) + g_x86_fnaddr_patch_name = n2 + g_x86_fnaddr_patch_cap = nc + +fn grow_func_cp(needed: int): + if needed < g_x86_func_cp_cap: + return + nc : ., mut = g_x86_func_cp_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_func_cp, g_x86_func_cp_cap * 8, nb) + g_x86_func_cp = nb + g_x86_func_cp_cap = nc + +fn grow_mw_tag_off(needed: int): + if needed < g_x86_mw_tag_off_cap: + return + nc : ., mut = g_x86_mw_tag_off_cap * 2 + if nc < 128: + nc = 128 + if nc < needed: + nc = needed + 128 + nb := alloc(nc * 8) + _dyncpy(g_x86_mw_tag_off, g_x86_mw_tag_off_cap * 8, nb) + g_x86_mw_tag_off = nb + g_x86_mw_tag_off_cap = nc + +fn grow_mw_jo_patch(needed: int): + if needed < g_x86_mw_jo_cap: + return + nc : ., mut = g_x86_mw_jo_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_mw_jo_pos, g_x86_mw_jo_cap * 8, nb) + g_x86_mw_jo_pos = nb + nb2 := alloc(nc * 8) + _dyncpy(g_x86_mw_jo_dest, g_x86_mw_jo_cap * 8, nb2) + g_x86_mw_jo_dest = nb2 + nb3 := alloc(nc * 8) + _dyncpy(g_x86_mw_jo_is_sub, g_x86_mw_jo_cap * 8, nb3) + g_x86_mw_jo_is_sub = nb3 + nb4 := alloc(nc * 8) + _dyncpy(g_x86_mw_jo_resume, g_x86_mw_jo_cap * 8, nb4) + g_x86_mw_jo_resume = nb4 + g_x86_mw_jo_cap = nc + +fn grow_mw_oc_patch(needed: int): + if needed < g_x86_mw_oc_cap: + return + nc : ., mut = g_x86_mw_oc_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_pos1, g_x86_mw_oc_cap * 8, nb) + g_x86_mw_oc_pos1 = nb + nb2 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_pos2, g_x86_mw_oc_cap * 8, nb2) + g_x86_mw_oc_pos2 = nb2 + nb3 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_s1, g_x86_mw_oc_cap * 8, nb3) + g_x86_mw_oc_s1 = nb3 + nb4 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_s2, g_x86_mw_oc_cap * 8, nb4) + g_x86_mw_oc_s2 = nb4 + nb5 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_dest, g_x86_mw_oc_cap * 8, nb5) + g_x86_mw_oc_dest = nb5 + nb6 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_op, g_x86_mw_oc_cap * 8, nb6) + g_x86_mw_oc_op = nb6 + nb7 := alloc(nc * 8) + _dyncpy(g_x86_mw_oc_resume, g_x86_mw_oc_cap * 8, nb7) + g_x86_mw_oc_resume = nb7 + g_x86_mw_oc_cap = nc + +fn grow_func_code_sz(needed: int): + if needed < g_x86_func_code_sz_cap: + return + nc : ., mut = g_x86_func_code_sz_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_func_code_sz, g_x86_func_code_sz_cap * 8, nb) + g_x86_func_code_sz = nb + g_x86_func_code_sz_cap = nc + +fn grow_rodataref(needed: int): + if needed < g_x86_rodataref_cap: + return + nc : ., mut = g_x86_rodataref_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + n1 := alloc(nc * 8) + _dyncpy(g_x86_rodataref_pos, g_x86_rodataref_cap * 8, n1) + g_x86_rodataref_pos = n1 + n2 := alloc(nc * 8) + _dyncpy(g_x86_rodataref_ro, g_x86_rodataref_cap * 8, n2) + g_x86_rodataref_ro = n2 + g_x86_rodataref_cap = nc + +fn grow_alloc_patch(needed: int): + if needed < g_x86_alloc_patch_cap: + return + nc : ., mut = g_x86_alloc_patch_cap * 2 + if nc < 64: + nc = 64 + if nc < needed: + nc = needed + 64 + nb := alloc(nc * 8) + _dyncpy(g_x86_alloc_patch_pos, g_x86_alloc_patch_cap * 8, nb) + g_x86_alloc_patch_pos = nb + g_x86_alloc_patch_cap = nc diff --git a/src/arch/x86_64/backend_globals.cr b/src/arch/x86_64/backend_globals.cr new file mode 100644 index 00000000..19496864 --- /dev/null +++ b/src/arch/x86_64/backend_globals.cr @@ -0,0 +1,111 @@ +// === backend_globals.cr === +// x86-64 backend state. Loaded only by corearch. + +// Backend arrays (all dynamic byte buffers) +g_x86_str_offs : string, mut +g_x86_str_count : int, mut +g_x86_str_cap : int, mut +g_x86_ext_rel_pos : string, mut +g_x86_ext_rel_name : string, mut +g_x86_ext_rel_count : int, mut +g_x86_ext_rel_cap : int, mut + +g_x86_rip_patch_pos : string, mut +g_x86_rip_patch_globals : string, mut +g_x86_rip_patch_count : int, mut +g_x86_rip_patch_cap : int, mut +g_x86_vars : string, mut +g_x86_var_count : int, mut +g_x86_var_cap : int, mut +g_x86_stack_size : int, mut +g_x86_func_idx : int, mut +g_x86_is_enum : string, mut +g_x86_is_enum_count : int, mut +g_x86_is_enum_cap : int, mut +g_x86_rodataref_pos : string, mut +g_x86_rodataref_ro : string, mut +g_x86_func_cp : string, mut +g_x86_func_cp_cap : int, mut +g_x86_func_code_sz : string, mut +g_x86_func_code_sz_cap : int, mut +g_x86_rodataref_count : int, mut +g_x86_rodataref_cap : int, mut +g_x86_is_global : string, mut +g_x86_global_cnt : int, mut +g_x86_global_cap : int, mut +g_x86_global_off : string, mut +g_x86_global_off_cnt : int, mut +g_x86_global_off_cap : int, mut +g_x86_func_offsets : string, mut +g_x86_func_offsets_cap : int, mut +g_x86_func_off_count : int, mut +g_x86_emit_vars : string, mut +g_x86_emit_vars_cap : int, mut +g_x86_emit_var_count : int, mut +g_x86_emit_stack_size : int, mut +// Tag area for potentially multiword int variables, appended to the stack frame. +// g_x86_mw_tag_off maps a function-local variable to its tag byte offset: an i64 +// array indexed by v - var_start, with negative rbp-relative offsets; -1 means not +// tagged. g_x86_mw_tag_count is the number of tagged variables in the current +// function, one tag byte each. See the multiword backend design documents. +g_x86_mw_tag_off : string, mut +g_x86_mw_tag_off_cap : int, mut +g_x86_mw_tag_count : int, mut +// Overflow-jump sites for tagged int add and sub fast paths. emit_instr records each +// jo site. At the end of the function, frame.cr emits the 2-limb slow block and +// patches the recorded sites. +// Four parallel i64 arrays share one capacity and grow together. Index is site +// order, which is instruction order; one instruction has at most one site: +// g_x86_mw_jo_pos: absolute jo position; rel32 is at pos+2 and jo is 6 bytes. +// g_x86_mw_jo_dest: destination var index for the fast-path store. The slot form +// is computed at block emission, so register-allocated forms need no extra +// record. +// g_x86_mw_jo_is_sub: 1 for sub. The high-limb correction differs: for add it is +// CF ? -1 : 0; for sub it is CF ? 0 : -1. See e2_mw_slow_block in tag2l.cr. +// g_x86_mw_jo_resume: absolute instruction after the fast-path store, where the +// slow block jumps back. elf.cr fills it once the instruction is emitted. +g_x86_mw_jo_pos : string, mut +g_x86_mw_jo_cap : int, mut +g_x86_mw_jo_count : int, mut +g_x86_mw_jo_dest : string, mut +g_x86_mw_jo_is_sub : string, mut +g_x86_mw_jo_resume : string, mut +// Tag-check sites for consumers of tagged operands. Add, sub, and comparison fast +// paths first emit a tag check (test byte + jne rel32; tag=1 means a 2-limb +// operand). The jne target is the function's 2-limb block, whose position is known +// only at the end, so it is patched like the jo sites. +// Eight parallel i64 arrays share one capacity; index is site order, which is +// instruction order: +// g_x86_mw_oc_pos1/pos2: absolute positions of the first and second check jne, +// or -1 when absent (operand untagged, or s1 == s2). +// g_x86_mw_oc_s1/s2/dest: operand rows and destination; slot forms are computed +// at block emission. +// g_x86_mw_oc_op: OP_ADD/OP_SUB select the arithmetic block; OP_EQ..OP_GE select +// the comparison block. +// g_x86_mw_oc_resume: instruction end, where the block jumps back. +// See e2_mw_opnd_block in tag2l.cr. +g_x86_mw_oc_pos1 : string, mut +g_x86_mw_oc_pos2 : string, mut +g_x86_mw_oc_cap : int, mut +g_x86_mw_oc_count : int, mut +g_x86_mw_oc_s1 : string, mut +g_x86_mw_oc_s2 : string, mut +g_x86_mw_oc_dest : string, mut +g_x86_mw_oc_op : string, mut +g_x86_mw_oc_resume : string, mut +g_x86_ret_patch_pos : string, mut +g_x86_ret_patch_cap : int, mut +g_x86_ret_patch_count : int, mut +g_x86_call_patch_pos : string, mut +g_x86_call_patch_name : string, mut +g_x86_call_patch_count : int, mut +g_x86_call_patch_cap : int, mut +g_x86_fnaddr_patch_pos : string, mut +g_x86_fnaddr_patch_name : string, mut +g_x86_fnaddr_patch_count : int, mut +g_x86_fnaddr_patch_cap : int, mut +g_x86_sub_rsp_pos : int, mut +g_x86_alloc_patch_pos : string, mut +g_x86_alloc_patch_cap : int, mut +g_x86_alloc_patch_count : int, mut +g_x86_emit_rt_stubs : int, mut // 1 = pure-static: backend emits g_set_curg/g_get_curg stubs diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index 553723c3..5ff899bb 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -1386,93 +1386,6 @@ fn istr_eq(idx: int, lit: string) -> int: return 1 return 0 -// ============================================================ -// Grow functions for x86 backend arrays -// ============================================================ - -fn grow_x86_vars(needed: int): - if needed < g_x86_var_cap: - return - nc : ., mut = g_x86_var_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_vars, g_x86_var_cap * 8, nb) - g_x86_vars = nb - g_x86_var_cap = nc - -fn grow_is_enum(needed: int): - if needed < g_x86_is_enum_cap: - return - nc : ., mut = g_x86_is_enum_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_is_enum, g_x86_is_enum_cap * 8, nb) - g_x86_is_enum = nb - g_x86_is_enum_cap = nc - -fn grow_is_global(needed: int): - if needed < g_x86_global_cap: - return - nc : ., mut = g_x86_global_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_is_global, g_x86_global_cap * 8, nb) - g_x86_is_global = nb - g_x86_global_cap = nc - -fn grow_global_off(needed: int): - if needed < g_x86_global_off_cap: - return - nc : ., mut = g_x86_global_off_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_global_off, g_x86_global_off_cap * 8, nb) - g_x86_global_off = nb - g_x86_global_off_cap = nc - -fn grow_str_offs(needed: int): - if needed < g_x86_str_cap: - return - nc : ., mut = g_x86_str_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_str_offs, g_x86_str_cap * 8, nb) - g_x86_str_offs = nb - g_x86_str_cap = nc - -fn grow_rip_patch(needed: int): - if needed < g_x86_rip_patch_cap: - return - nc : ., mut = g_x86_rip_patch_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb1 := alloc(nc * 8) - _dyncpy(g_x86_rip_patch_pos, g_x86_rip_patch_cap * 8, nb1) - g_x86_rip_patch_pos = nb1 - nb2 := alloc(nc * 8) - _dyncpy(g_x86_rip_patch_globals, g_x86_rip_patch_cap * 8, nb2) - g_x86_rip_patch_globals = nb2 - g_x86_rip_patch_cap = nc - -// .so extension index side tables. They grow dynamically, following the rule that -// all arrays are dynamic byte buffers with no fixed limits. fn grow_so_side(needed: int): if needed < g_so_side_cap: return @@ -1581,49 +1494,6 @@ fn spec_col(i: int) -> int: return -1 return r64(g_spec_col, i * 8) -fn grow_ext_rel(needed: int): - if needed < g_x86_ext_rel_cap: - return - nc : ., mut = g_x86_ext_rel_cap * 2 - if nc < 32: - nc = 32 - if nc < needed: - nc = needed + 32 - sz := nc * 8 - n1 := alloc(sz) - _dyncpy(g_x86_ext_rel_pos, g_x86_ext_rel_cap*8, n1) - g_x86_ext_rel_pos = n1 - n2 := alloc(sz) - _dyncpy(g_x86_ext_rel_name, g_x86_ext_rel_cap*8, n2) - g_x86_ext_rel_name = n2 - g_x86_ext_rel_cap = nc - -fn grow_func_offsets(needed: int): - if needed < g_x86_func_offsets_cap: - return - nc : ., mut = g_x86_func_offsets_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 16) - _dyncpy(g_x86_func_offsets, g_x86_func_offsets_cap * 16, nb) - g_x86_func_offsets = nb - g_x86_func_offsets_cap = nc - -fn grow_emit_vars(needed: int): - if needed < g_x86_emit_vars_cap: - return - nc : ., mut = g_x86_emit_vars_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_emit_vars, g_x86_emit_vars_cap * 8, nb) - g_x86_emit_vars = nb - g_x86_emit_vars_cap = nc - fn grow_pending(needed: int): if needed < g_pending_cap: return @@ -1639,128 +1509,6 @@ fn grow_pending(needed: int): _dyncpy(g_pending_label, g_pending_cap * 8, nb2) g_pending_label = nb2 g_pending_cap = nc -fn grow_ret_patch(needed: int): - if needed < g_x86_ret_patch_cap: - return - nc : ., mut = g_x86_ret_patch_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_ret_patch_pos, g_x86_ret_patch_cap * 8, nb) - g_x86_ret_patch_pos = nb - g_x86_ret_patch_cap = nc -fn grow_call_patch(needed: int): - if needed < g_x86_call_patch_cap: - return - nc : ., mut = g_x86_call_patch_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - n1 := alloc(nc * 8) - _dyncpy(g_x86_call_patch_pos, g_x86_call_patch_cap * 8, n1) - g_x86_call_patch_pos = n1 - n2 := alloc(nc * 8) - _dyncpy(g_x86_call_patch_name, g_x86_call_patch_cap * 8, n2) - g_x86_call_patch_name = n2 - g_x86_call_patch_cap = nc -fn grow_fnaddr_patch(needed: int): - if needed < g_x86_fnaddr_patch_cap: - return - nc : ., mut = g_x86_fnaddr_patch_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - n1 := alloc(nc * 8) - _dyncpy(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_cap * 8, n1) - g_x86_fnaddr_patch_pos = n1 - n2 := alloc(nc * 8) - _dyncpy(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_cap * 8, n2) - g_x86_fnaddr_patch_name = n2 - g_x86_fnaddr_patch_cap = nc - -fn grow_func_cp(needed: int): - if needed < g_x86_func_cp_cap: - return - nc : ., mut = g_x86_func_cp_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_func_cp, g_x86_func_cp_cap * 8, nb) - g_x86_func_cp = nb - g_x86_func_cp_cap = nc - -fn grow_mw_tag_off(needed: int): - if needed < g_x86_mw_tag_off_cap: - return - nc : ., mut = g_x86_mw_tag_off_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_tag_off, g_x86_mw_tag_off_cap * 8, nb) - g_x86_mw_tag_off = nb - g_x86_mw_tag_off_cap = nc - -fn grow_mw_jo_patch(needed: int): - if needed < g_x86_mw_jo_cap: - return - nc : ., mut = g_x86_mw_jo_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_pos, g_x86_mw_jo_cap * 8, nb) - g_x86_mw_jo_pos = nb - nb2 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_dest, g_x86_mw_jo_cap * 8, nb2) - g_x86_mw_jo_dest = nb2 - nb3 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_is_sub, g_x86_mw_jo_cap * 8, nb3) - g_x86_mw_jo_is_sub = nb3 - nb4 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_resume, g_x86_mw_jo_cap * 8, nb4) - g_x86_mw_jo_resume = nb4 - g_x86_mw_jo_cap = nc - -fn grow_mw_oc_patch(needed: int): - if needed < g_x86_mw_oc_cap: - return - nc : ., mut = g_x86_mw_oc_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_pos1, g_x86_mw_oc_cap * 8, nb) - g_x86_mw_oc_pos1 = nb - nb2 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_pos2, g_x86_mw_oc_cap * 8, nb2) - g_x86_mw_oc_pos2 = nb2 - nb3 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_s1, g_x86_mw_oc_cap * 8, nb3) - g_x86_mw_oc_s1 = nb3 - nb4 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_s2, g_x86_mw_oc_cap * 8, nb4) - g_x86_mw_oc_s2 = nb4 - nb5 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_dest, g_x86_mw_oc_cap * 8, nb5) - g_x86_mw_oc_dest = nb5 - nb6 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_op, g_x86_mw_oc_cap * 8, nb6) - g_x86_mw_oc_op = nb6 - nb7 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_resume, g_x86_mw_oc_cap * 8, nb7) - g_x86_mw_oc_resume = nb7 - g_x86_mw_oc_cap = nc - fn grow_opt_meta(needed: int): if needed <= g_opt_meta_cap: return @@ -1773,48 +1521,6 @@ fn grow_opt_meta(needed: int): _dyncpy(g_opt_meta, g_opt_meta_count * OPT_META_STRIDE, nb) g_opt_meta = nb g_opt_meta_cap = nc -fn grow_func_code_sz(needed: int): - if needed < g_x86_func_code_sz_cap: - return - nc : ., mut = g_x86_func_code_sz_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_func_code_sz, g_x86_func_code_sz_cap * 8, nb) - g_x86_func_code_sz = nb - g_x86_func_code_sz_cap = nc - -fn grow_rodataref(needed: int): - if needed < g_x86_rodataref_cap: - return - nc : ., mut = g_x86_rodataref_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - n1 := alloc(nc * 8) - _dyncpy(g_x86_rodataref_pos, g_x86_rodataref_cap * 8, n1) - g_x86_rodataref_pos = n1 - n2 := alloc(nc * 8) - _dyncpy(g_x86_rodataref_ro, g_x86_rodataref_cap * 8, n2) - g_x86_rodataref_ro = n2 - g_x86_rodataref_cap = nc - -fn grow_alloc_patch(needed: int): - if needed < g_x86_alloc_patch_cap: - return - nc : ., mut = g_x86_alloc_patch_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_alloc_patch_pos, g_x86_alloc_patch_cap * 8, nb) - g_x86_alloc_patch_pos = nb - g_x86_alloc_patch_cap = nc - fn grow_ir_local_scopes(needed: int): if needed < g_ir_local_scopes_cap: return diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 8d4826da..543a95b2 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -432,15 +432,6 @@ g_pending_count : int, mut g_pending_cap : int, mut g_next_label : int, mut -// Backend arrays (all dynamic byte buffers) -g_x86_str_offs : string, mut -g_x86_str_count : int, mut -g_x86_str_cap : int, mut -g_x86_ext_rel_pos : string, mut -g_x86_ext_rel_name : string, mut -g_x86_ext_rel_count : int, mut -g_x86_ext_rel_cap : int, mut - // .so extension index side tables. Parsed entries land here first, not directly in // the string or symbol table. Only names referenced by the program are materialized // by so_materialize in module.cr, so unreferenced entries leave no output trace. @@ -469,105 +460,7 @@ g_spec_status : string, mut // 0 = yellow (unproved, default), 1 = gr // 2 = red (hard error). Red is sticky and is not overwritten by green. g_spec_count : int, mut g_spec_cap : int, mut -g_x86_rip_patch_pos : string, mut -g_x86_rip_patch_globals : string, mut -g_x86_rip_patch_count : int, mut -g_x86_rip_patch_cap : int, mut -g_x86_vars : string, mut -g_x86_var_count : int, mut -g_x86_var_cap : int, mut -g_x86_stack_size : int, mut -g_x86_func_idx : int, mut -g_x86_is_enum : string, mut -g_x86_is_enum_count : int, mut -g_x86_is_enum_cap : int, mut -g_x86_rodataref_pos : string, mut -g_x86_rodataref_ro : string, mut -g_x86_func_cp : string, mut -g_x86_func_cp_cap : int, mut -g_x86_func_code_sz : string, mut -g_x86_func_code_sz_cap : int, mut -g_x86_rodataref_count : int, mut -g_x86_rodataref_cap : int, mut -g_x86_is_global : string, mut -g_x86_global_cnt : int, mut -g_x86_global_cap : int, mut -g_x86_global_off : string, mut -g_x86_global_off_cnt : int, mut -g_x86_global_off_cap : int, mut -g_x86_func_offsets : string, mut -g_x86_func_offsets_cap : int, mut -g_x86_func_off_count : int, mut -g_x86_emit_vars : string, mut -g_x86_emit_vars_cap : int, mut -g_x86_emit_var_count : int, mut -g_x86_emit_stack_size : int, mut -// Tag area for potentially multiword int variables, appended to the stack frame. -// g_x86_mw_tag_off maps a function-local variable to its tag byte offset: an i64 -// array indexed by v - var_start, with negative rbp-relative offsets; -1 means not -// tagged. g_x86_mw_tag_count is the number of tagged variables in the current -// function, one tag byte each. See the multiword backend design documents. -g_x86_mw_tag_off : string, mut -g_x86_mw_tag_off_cap : int, mut -g_x86_mw_tag_count : int, mut -// Overflow-jump sites for tagged int add and sub fast paths. emit_instr records each -// jo site. At the end of the function, frame.cr emits the 2-limb slow block and -// patches the recorded sites. -// Four parallel i64 arrays share one capacity and grow together. Index is site -// order, which is instruction order; one instruction has at most one site: -// g_x86_mw_jo_pos: absolute jo position; rel32 is at pos+2 and jo is 6 bytes. -// g_x86_mw_jo_dest: destination var index for the fast-path store. The slot form -// is computed at block emission, so register-allocated forms need no extra -// record. -// g_x86_mw_jo_is_sub: 1 for sub. The high-limb correction differs: for add it is -// CF ? -1 : 0; for sub it is CF ? 0 : -1. See e2_mw_slow_block in tag2l.cr. -// g_x86_mw_jo_resume: absolute instruction after the fast-path store, where the -// slow block jumps back. elf.cr fills it once the instruction is emitted. -g_x86_mw_jo_pos : string, mut -g_x86_mw_jo_cap : int, mut -g_x86_mw_jo_count : int, mut -g_x86_mw_jo_dest : string, mut -g_x86_mw_jo_is_sub : string, mut -g_x86_mw_jo_resume : string, mut -// Tag-check sites for consumers of tagged operands. Add, sub, and comparison fast -// paths first emit a tag check (test byte + jne rel32; tag=1 means a 2-limb -// operand). The jne target is the function's 2-limb block, whose position is known -// only at the end, so it is patched like the jo sites. -// Eight parallel i64 arrays share one capacity; index is site order, which is -// instruction order: -// g_x86_mw_oc_pos1/pos2: absolute positions of the first and second check jne, -// or -1 when absent (operand untagged, or s1 == s2). -// g_x86_mw_oc_s1/s2/dest: operand rows and destination; slot forms are computed -// at block emission. -// g_x86_mw_oc_op: OP_ADD/OP_SUB select the arithmetic block; OP_EQ..OP_GE select -// the comparison block. -// g_x86_mw_oc_resume: instruction end, where the block jumps back. -// See e2_mw_opnd_block in tag2l.cr. -g_x86_mw_oc_pos1 : string, mut -g_x86_mw_oc_pos2 : string, mut -g_x86_mw_oc_cap : int, mut -g_x86_mw_oc_count : int, mut -g_x86_mw_oc_s1 : string, mut -g_x86_mw_oc_s2 : string, mut -g_x86_mw_oc_dest : string, mut -g_x86_mw_oc_op : string, mut -g_x86_mw_oc_resume : string, mut -g_x86_ret_patch_pos : string, mut -g_x86_ret_patch_cap : int, mut -g_x86_ret_patch_count : int, mut -g_x86_call_patch_pos : string, mut -g_x86_call_patch_name : string, mut -g_x86_call_patch_count : int, mut -g_x86_call_patch_cap : int, mut -g_x86_fnaddr_patch_pos : string, mut -g_x86_fnaddr_patch_name : string, mut -g_x86_fnaddr_patch_count : int, mut -g_x86_fnaddr_patch_cap : int, mut -g_x86_sub_rsp_pos : int, mut -g_x86_alloc_patch_pos : string, mut -g_x86_alloc_patch_cap : int, mut -g_x86_alloc_patch_count : int, mut -g_x86_emit_rt_stubs : int, mut // 1 = pure-static: backend emits g_set_curg/g_get_curg stubs + // Optimization levels and metadata (extensible key-value store) g_opt_level : int, mut // 0=none, 1=regalloc, 2=stackshare, 3=cse g_opt_meta : string, mut // metadata buffer for .ccr v3+ diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux/_import.cr index 7b22a9f0..a87c9404 100644 --- a/src/targets/x86_64-linux/_import.cr +++ b/src/targets/x86_64-linux/_import.cr @@ -7,6 +7,8 @@ import toml import ast import globals import dyn_arr +import backend_globals +import backend_arrays import hit // HIT lowering layer. instr.cr's table encoder consumes the event stream and pool // patch records defined here. From eeead2592f084a09582c7e74c82e08790af2cf73 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:03:08 +0800 Subject: [PATCH 03/58] refactor: move backend entry files out of compiler directory --- build_selfhost_native.py | 4 +- .../design/regalloc-cache-mapping.md | 8 +-- .../maintainer/design/regalloc-consistency.md | 41 ++++++++++++ src/arch/x86_64/backend_flags.cr | 26 ++++++++ src/{compiler => arch/x86_64}/corearch.cr | 0 src/compiler/backend_flags.cr | 26 -------- src/compiler/regalloc-consistency.cr | 64 ------------------- src/targets/x86_64-linux/main.cr | 4 +- 8 files changed, 75 insertions(+), 98 deletions(-) create mode 100644 docs/maintainer/design/regalloc-consistency.md create mode 100644 src/arch/x86_64/backend_flags.cr rename src/{compiler => arch/x86_64}/corearch.cr (100%) delete mode 100644 src/compiler/backend_flags.cr delete mode 100644 src/compiler/regalloc-consistency.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 0a878745..03dd54b9 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -122,8 +122,8 @@ 'src/stdlib/goroutine.cr', 'src/stdlib/chan.cr', 'src/stdlib/sched.cr', - 'src/compiler/backend_flags.cr', - 'src/compiler/corearch.cr', + 'src/arch/x86_64/backend_flags.cr', + 'src/arch/x86_64/corearch.cr', ] # 组合根(x86_64-linux target triple):project-mode 入口链——`corec build diff --git a/docs/maintainer/design/regalloc-cache-mapping.md b/docs/maintainer/design/regalloc-cache-mapping.md index f0e12cac..55b63b7e 100644 --- a/docs/maintainer/design/regalloc-cache-mapping.md +++ b/docs/maintainer/design/regalloc-cache-mapping.md @@ -2,7 +2,7 @@ 寄存器分配是缓存语义在编码层的一个映射实例。缓存语义的规则以 `docs/academic/cache-semantics.md` 为准;层定义见 `docs/maintainer/design/materialization-space.md`;存在区间的表示见 `docs/maintainer/design/existence-structure.md`。 -设计决策记录见 `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`。分配器的逻辑契约见 `src/compiler/regalloc-consistency.cr` 的 v6 分支。 +设计决策记录见 `docs/superpowers/specs/2026-08-27-regalloc-cache-mapping-design.md`。分配器的逻辑契约见 `docs/maintainer/design/regalloc-consistency.md` 的 v6 分支。 ## 术语 @@ -91,7 +91,7 @@ recipe 无法判定 => 不可移除(失败关闭) 判定有两个应用位置: 1. 规约契约:`.cr` 内联规约,走 `spec-design` 体系的翻译桥和 CIC 内核,健全性由内核保证。旧格式已经废弃,见 `adr/adr-0001`。 -2. 编译器自检:分配器的逻辑契约在 `src/compiler/regalloc-consistency.cr` 的 v6 分支,运行自检在 `opt.cr` 一侧。 +2. 编译器自检:分配器的逻辑契约在 `docs/maintainer/design/regalloc-consistency.md` 的 v6 分支,运行自检在 `opt.cr` 一侧。 ## 分配器:上下文贪心 @@ -132,7 +132,7 @@ recipe 无法判定 => 不可移除(失败关闭) | 分支 | `alloc_registers` 现状 | 契约和自检 | |---|---|---| | develop(基线) | 朴素线性扫描:first/last 区间,5 个 callee-saved 寄存器,没有溢写;负编码寄存器,由 g2_slot 机械翻译 | 无 | -| lattice-ir-v6(推进中) | CAG 重写,修复 rc=0 的根因,加入真实寄存器分配和 watchdog 验证 | `regalloc-consistency.cr` 记录机制、`opt.cr` 映射和三层正确性定位;verify 运行自检 | +| lattice-ir-v6(推进中) | CAG 重写,修复 rc=0 的根因,加入真实寄存器分配和 watchdog 验证 | `docs/maintainer/design/regalloc-consistency.md` 记录机制、`opt.cr` 映射和三层正确性定位;verify 运行自检 | 设计与代码的对应: @@ -159,4 +159,4 @@ recipe 无法判定 => 不可移除(失败关闭) - RegionCheck 和存在区间实现:`docs/maintainer/design/pointer-model.md` - 规约体系:`docs/maintainer/design/spec-design.md` - 执行标注空间:`docs/maintainer/design/execution-annotation-space.md` -- 契约文档:`src/compiler/regalloc-consistency.cr` 的 v6 分支 +- 契约文档:`docs/maintainer/design/regalloc-consistency.md` 的 v6 分支 diff --git a/docs/maintainer/design/regalloc-consistency.md b/docs/maintainer/design/regalloc-consistency.md new file mode 100644 index 00000000..055c48b0 --- /dev/null +++ b/docs/maintainer/design/regalloc-consistency.md @@ -0,0 +1,41 @@ +# 寄存器分配器一致性契约 + +寄存器分配器在 Core 的 entry、live interval 和 coexist 模型上承担以下义务。每条义务都对应一个实现位置。`src/arch/x86_64/regalloc.cr` 是唯一真源,本文不复制实现。 + +## 三个独立问题 + +1. 程序逻辑:`.corespec` 文件中的 `#check` 和 `#ensure`,由 verifier 检查。 +2. 分配器正确性:本文描述的义务。最优性证明留到后续工作。 +3. 运行期自检:`verify_regalloc_consistency`,由 corearch 在 O2 路径调用。 + +## 定义 + +- entry:一个 IR 变量及其一个版本。每次定义创建一个版本。`def = -1` 表示参数或没有定义的 entry。 +- live interval:全局指令顺序中的闭区间 `[live_start, live_end]`。 +- coexist:两个 entry 的 interval 相交。该关系对称,不传递。 +- location:entry 的物理位置,可以是寄存器或 home slot。分配结果写成 `g_opt_meta` 中的 `REG_ASSIGN` 变量到寄存器对。后端通过 `E2_REG_SLOT_BASE + reg` 哨兵解析寄存器。 +- allocation result:corearch 在进程内计算的 `REG_ASSIGN` 对。 + +## 正确性义务 + +实现位置:`src/arch/x86_64/regalloc.cr`。 + +1. coexist 排他:同一 location 中的 entry 不共存。`verify_regalloc_consistency` 的规则 1 对 entry 排序,并在一次遍历中记录每个 location 的最大 `live_end`,避免二次两两检查。 +2. 不读取过期版本:每次读取都看到当前 live 版本。规则 2 使用 `rl_rule2_func`。覆盖范围从第一个有版本的定义之后开始,并包括 `ALLOC_ARRAY` 和 `ALLOC_STRUCT` 创建的内存对象。 +3. 驱逐配对:被驱逐的 entry 需要对应的写回。规则已经写明,但尚未实现。当前没有驱逐,因为超过五个共存的 entry 会留在栈上,没有可检查的事件。 +4. 调用失效:调用点的 caller-saved entry 必须失效或保存。该规则等待 caller-saved 注解。 + +`--inject-*` 通道证明验证器读取真实数据:规则 1 和规则 2 会产生诊断。驱逐检查和 caller-saved 检查依赖分配器功能,不依赖单独的延迟层。 + +## 已知缺口 + +- entry 和 interval 使用文本范围,不使用 CFG liveness。RegionCheck 接入后会收紧该范围。 +- `pass_stack_share` 已停用。原实现产生死输出,corearch 运行时该通道始终为空。恢复它需要在同一位置提供基于 alloc 的实现。 +- `param_ents` 为 -1。被重新定义的参数没有 incoming-version entry,因为尾部 pass 只在 `prev[lv] < 0` 时添加 entry。`.ccr` 不再保存 `param_ents`,旧的有针对性测试已随 ENT carrier 删除。如果规则 2 需要,可以恢复 incoming-version entry。 +- `entries_coexist` 现在拒绝越界的上界。检查位于 `entries_coexist`,`--inject-coexist-oob` 探针已移到 corearch。 + +## 参考实现 + +`src/arch/x86_64/regalloc.cr` 包含 `compute_live_ranges`、`compute_entries`、`entries_coexist`、`coexist_version_conflicts`、`verify_regalloc_consistency` 和 `alloc_registers`(context-greedy allocation)。 + +`grammar/corespec.ebnf` 定义程序逻辑规范语言。其中的 `SpecImplies` 产生式与本文无关。 diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr new file mode 100644 index 00000000..14aec3ae --- /dev/null +++ b/src/arch/x86_64/backend_flags.cr @@ -0,0 +1,26 @@ +// 后端 CLI 旗标注册单源。 +// concat 入口(corearch.cr)与组合根 project-mode 入口 +// (src/targets/x86_64-linux/main.cr)共用本函数,避免两处注册表漂移。 +fn register_backend_flags(): + cli_flag_bool("elf", "", "Output ELF binary (default)") + cli_flag_bool("shared", "", "Output shared library (.so)") + cli_flag_bool("static", "", "Static linking (embed runtime)") + cli_flag("link", "l", "Comma-sep .so files, or 'auto' for ~/.core/lib/") + cli_flag("output", "o", "Output path") + cli_flag("opt-level", "O", "Optimization level (0-3, default=0)") + cli_flag("table", "", "HIT table file (load & emit mapped ops through it)") + cli_flag_debug("hit-events-file", "", "Debug: inject text event stream file") + cli_flag_bool("dump-events", "", "Dump lowered HIT event stream + const pool") + cli_flag_bool_debug("dump-table", "", "Debug: dump loaded HIT table state") + cli_flag_bool_debug("dump-entries", "", "Debug: dump versioned entries summary") + cli_flag_bool_debug("dump-coexist", "", "Debug: dump coexistence summary") + cli_flag_bool_debug("dump-regassign", "", "Debug: dump var-to-reg after O2") + cli_flag_bool_debug("check-regalloc", "", "Debug: O2 regalloc consistency check") + cli_flag_bool_debug("inject-home-conflict", "", "Debug hook: inject home-slot conflict") + cli_flag_bool_debug("inject-reg-conflict", "", "Debug hook: inject register conflict") + cli_flag_bool_debug("inject-read-gap", "", "Debug hook: truncate last version interval") + cli_flag_bool_debug("inject-coexist-oob", "", "Debug hook: probe entries_coexist out of bounds") + cli_flag_bool_debug("dump-objects", "", "Debug: dump NOD/EDG objects") + cli_flag_bool_debug("dump-nod-items", "", "Debug: dump NOD type terms") + cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") + cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") diff --git a/src/compiler/corearch.cr b/src/arch/x86_64/corearch.cr similarity index 100% rename from src/compiler/corearch.cr rename to src/arch/x86_64/corearch.cr diff --git a/src/compiler/backend_flags.cr b/src/compiler/backend_flags.cr deleted file mode 100644 index e5ba12ee..00000000 --- a/src/compiler/backend_flags.cr +++ /dev/null @@ -1,26 +0,0 @@ -// 后端 CLI 旗标注册单源。 -// concat 入口(corearch.cr)与组合根 project-mode 入口 -// (src/targets/x86_64-linux/main.cr)共用本函数,避免两处注册表漂移。 -fn register_backend_flags(): - cli_flag_bool("elf", "", "Output ELF binary (default)") - cli_flag_bool("shared", "", "Output shared library (.so)") - cli_flag_bool("static", "", "Static linking (embed runtime)") - cli_flag("link", "l", "Comma-sep .so files, or 'auto' for ~/.core/lib/") - cli_flag("output", "o", "Output path") - cli_flag("opt-level", "O", "Optimization level (0-3, default=0)") - cli_flag("table", "", "HIT table file (load & emit mapped ops through it)") - cli_flag("hit-events-file", "", "Hidden debug: inject text event stream file (dump-events inverse; skips lowering)") - cli_flag_bool("dump-events", "", "Dump lowered HIT event stream + const pool") - cli_flag_bool("dump-table", "", "Hidden debug: dump loaded HIT event/proj/step/runtime state") - cli_flag_bool("dump-entries", "", "Hidden debug: versioned entries summary") - cli_flag_bool("dump-coexist", "", "Hidden debug: coexistence summary") - cli_flag_bool("dump-regassign", "", "Hidden debug: per-pair var->reg dump after O2 self-alloc") - cli_flag_bool("check-regalloc", "", "Hidden debug: O2-forced alloc + regalloc consistency self-check") - cli_flag_bool("inject-home-conflict", "", "Hidden debug: inject coexisting entries onto same home slot, then verify") - cli_flag_bool("inject-reg-conflict", "", "Hidden debug: inject fake var->reg pair colliding with a real one, then verify") - cli_flag_bool("inject-read-gap", "", "Hidden debug: truncate last version interval to def point, then verify") - cli_flag_bool("inject-coexist-oob", "", "Hidden debug: probe entries_coexist with OOB indices") - cli_flag_bool("dump-objects", "", "Hidden debug: dump loaded NOD/EDG semantic objects") - cli_flag_bool("dump-nod-items", "", "Hidden debug: dump loaded NOD item indices through the TYPE term table") - cli_flag_bool("dump-types", "", "Hidden debug: dump loaded TYPE segment content") - cli_flag_bool("dump-ifaces", "", "Hidden debug: dump loaded IFACE segment content") diff --git a/src/compiler/regalloc-consistency.cr b/src/compiler/regalloc-consistency.cr deleted file mode 100644 index 7049a8e6..00000000 --- a/src/compiler/regalloc-consistency.cr +++ /dev/null @@ -1,64 +0,0 @@ -// === regalloc-consistency.cr === -// Register allocator correctness contract. -// -// This file is a documentation comment, not a compilation unit. It states the -// allocator's logical obligations over Core's entry, live-interval, and coexist -// model and maps each obligation to its implementation. The verifier lives in -// regalloc.cr and is not duplicated here. -// -// Three separate concerns: -// - program logic: #check and #ensure in .corespec files, checked by a verifier; -// - allocator correctness: this contract, with optimality proofs left for later; -// - run-time self-check: verify_regalloc_consistency in regalloc.cr, run by -// corearch in the O2 path. -// -// Definitions: -// - entry: one IR variable and one version; each definition creates a version. -// def = -1 marks a parameter or an entry with no definition. -// - live interval: the closed instruction range [live_start, live_end] in global -// instruction order. -// - coexist: two entries whose intervals intersect. The relation is symmetric -// and not transitive. -// - location: the physical home of an entry, either a register or a home slot. -// Allocation results are REG_ASSIGN var-to-register pairs in g_opt_meta. The -// backend resolves a register through the E2_REG_SLOT_BASE + reg sentinel. -// - allocation result: REG_ASSIGN pairs computed by corearch in-process. -// -// Correctness obligations, mapped to src/arch/x86_64/regalloc.cr: -// 1. Coexist exclusion: entries in the same location do not coexist. -// Rule 1 in verify_regalloc_consistency sorts entries and tracks the maximum -// live_end per location in one pass, avoiding a quadratic pairwise check. -// 2. No stale read: every read sees the live version. -// Rule 2 uses rl_rule2_func. Its coverage obligation begins after the first -// versioned definition and includes memory objects created by ALLOC_ARRAY and -// ALLOC_STRUCT. -// 3. Eviction pairing: an evicted entry needs a matching write-back. The rule is -// stated but not yet implemented; there is no eviction today, because more -// than five coexisting entries stay on the stack, so there are no events to -// check. -// 4. Call invalidation: caller-saved entries at a call site must be invalidated -// or saved. This also waits for caller-saved annotations. -// -// The --inject-* channels prove that verification consumes real data: rules 1 -// and 2 produce diagnostics. Eviction and caller-saved checks depend on allocator -// features, not on a separate deferred layer. -// -// Known gaps: -// - Entries and intervals use textual ranges, not CFG liveness. RegionCheck will -// tighten this when it is connected. -// - pass_stack_share is disabled. The original implementation produced dead -// output and corearch always ran it empty. Restoring it requires an alloc-based -// implementation at the same seam. -// - param_ents is -1. Redefined parameters have no incoming-version entry -// because the tail pass only adds an entry when prev[lv] < 0. The .ccr no -// longer stores param_ents, and the old targeted tests were removed with the -// ENT carrier. Incoming-version entries can return if rule 2 needs them. -// - entries_coexist now rejects an out-of-range upper bound; the check was added -// in entries_coexist and the --inject-coexist-oob probe moved to corearch. -// -// Reference implementation, the only source of truth; do not copy code here: -// src/arch/x86_64/regalloc.cr contains compute_live_ranges, compute_entries, -// entries_coexist, coexist_version_conflicts, verify_regalloc_consistency, and -// alloc_registers (context-greedy allocation). -// grammar/corespec.ebnf defines the program-logic specification language. Its -// SpecImplies production is unrelated to this file. diff --git a/src/targets/x86_64-linux/main.cr b/src/targets/x86_64-linux/main.cr index 1d2e1f3a..34c8f4f4 100644 --- a/src/targets/x86_64-linux/main.cr +++ b/src/targets/x86_64-linux/main.cr @@ -1,13 +1,13 @@ // === targets/x86_64-linux/main.cr === // Backend project entry: .ccr to ELF, assembly, or shared object. -// Self-hosted counterpart of src/compiler/corearch.cr. +// Self-hosted counterpart of src/arch/x86_64/corearch.cr. // // This file is the live project-mode entry for the x86_64-linux target. // `corec build src/targets/x86_64-linux` starts at main, calls corearch_main, // loads the .ccr, and rebuilds the linear schedule. The backend bootstrap stages // all pass through this entry. // There are two live entries: the corearch concat entry in -// src/compiler/corearch.cr and this project-mode entry. Both register backend +// src/arch/x86_64/corearch.cr and this project-mode entry. Both register backend // flags and both call build_linear_schedule after loading. This entry emits ELF // only and rejects table and debug channels with a message pointing to the concat // entry. From ebcfd2e3f4b2485d44466493e7f1cea1a2e37b5d Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:10:49 +0800 Subject: [PATCH 04/58] refactor: split HDFG construction from CCR lowering --- build_selfhost_native.py | 1 + src/compiler/_import.cr | 1 + src/compiler/ccr_lower.cr | 49 +++++++++++++++++++++++++++++++++++ src/compiler/dataflow.cr | 54 +++------------------------------------ src/compiler/ty_shadow.cr | 2 +- 5 files changed, 55 insertions(+), 52 deletions(-) create mode 100644 src/compiler/ccr_lower.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 03dd54b9..4388bc8c 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -363,6 +363,7 @@ def main(): 'src/compiler/monomorph.cr', 'src/compiler/pass.cr', 'src/compiler/dataflow.cr', + 'src/compiler/ccr_lower.cr', 'src/compiler/ccr_io.cr', 'src/lattice/ent_kernel.cr', 'src/compiler/module.cr', diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index 0bdb6e42..f55a868c 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -30,6 +30,7 @@ import purity_selftest import diag import ir_gen import dataflow +import ccr_lower import ccr_io import ent_kernel import module diff --git a/src/compiler/ccr_lower.cr b/src/compiler/ccr_lower.cr new file mode 100644 index 00000000..1879327f --- /dev/null +++ b/src/compiler/ccr_lower.cr @@ -0,0 +1,49 @@ +// === ccr_lower.cr === +// HDFG linear schedule lowering. This is the only frontend step that turns +// graph nodes into the linear instruction stream stored in .ccr. + +fn lower_to_ccr(): + // The graph was built in parallel with linear IR during emit(). + // For now: clear and rebuild g_ir_instrs from graph nodes. + // Since nodes are in creation order (AST walk order), sequential + // walk is already a valid topological schedule. + g_ir_instr_count = 0 + + ni : ., mut = 0 + loop: + if ni >= g_df_node_count: + break + idx := g_ir_instr_count + grow_ir_instrs(idx + 1) + iri_set_op(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE)) + iri_set_dest(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST)) + iri_set_s1(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1)) + iri_set_s2(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2)) + iri_set_s3(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3)) + // iri_tk is the derived code, not the stored slot value. The node slot + // holds a type-term reference; sh_dfn_code_of_slots combines it with the + // auxiliary code. The derived code preserves the previous mixed code. + iri_set_tk(idx, sh_dfn_code_of_slots( + r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK), + r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX))) + g_ir_instr_count = idx + 1 + ni = ni + 1 + + // Rebuilt g_ir_instrs means g_ir_func_instr_start/count are stale. + // Node i -> instruction i, so df boundaries = ir boundaries. + fi : ., mut = 0 + loop: + if fi >= g_ir_func_count: + break + w64(g_ir_func_instr_start, fi * 8, + r64(g_df_func_node_start, fi * 8)) + w64(g_ir_func_instr_count, fi * 8, + r64(g_df_func_node_count, fi * 8)) + fi = fi + 1 + + // After lowering, compute per-variable usage counts for optimization passes + compute_usage_counts() + + // Register allocation lives in the backend. The .ccr ENT section is empty + // in this path; corearch computes live ranges and entries after loading the + // NOD stream. diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index 9cc79c8b..595efc66 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -1,9 +1,9 @@ // === dataflow.cr === -// HDFG construction and lowering to the linear schedule. +// HDFG construction and state finalization. // // IR generation calls emit(), which creates a DFNode. Data edges are tracked -// through g_df_var_producer[]. After IR generation, lower_to_ccr() builds -// g_ir_instrs for the backend, and df_state_finalize() computes purity and +// through g_df_var_producer[]. After IR generation, ccr_lower.cr::lower_to_ccr() +// builds g_ir_instrs for the backend, and df_state_finalize() computes purity and // replays the state chain. // --- Initialization --- @@ -432,54 +432,6 @@ fn compute_usage_counts(): w64(g_var_use_count, dest * 8, prev + 1) ei = ei + 1 -// --- Lowering: dataflow graph -> linear CFG IR (.ccr) --- - -fn lower_to_ccr(): - // The graph was built in parallel with linear IR during emit(). - // For now: clear and rebuild g_ir_instrs from graph nodes. - // Since nodes are in creation order (AST walk order), sequential - // walk is already a valid topological schedule. - g_ir_instr_count = 0 - - ni : ., mut = 0 - loop: - if ni >= g_df_node_count: - break - idx := g_ir_instr_count - grow_ir_instrs(idx + 1) - iri_set_op(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE)) - iri_set_dest(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST)) - iri_set_s1(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1)) - iri_set_s2(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2)) - iri_set_s3(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3)) - // iri_tk is the derived code, not the stored slot value. The node slot - // holds a type-term reference; sh_dfn_code_of_slots combines it with the - // auxiliary code. The derived code preserves the previous mixed code. - iri_set_tk(idx, sh_dfn_code_of_slots( - r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX))) - g_ir_instr_count = idx + 1 - ni = ni + 1 - - // Rebuilt g_ir_instrs means g_ir_func_instr_start/count are stale. - // Node i -> instruction i, so df boundaries = ir boundaries. - fi : ., mut = 0 - loop: - if fi >= g_ir_func_count: - break - w64(g_ir_func_instr_start, fi * 8, - r64(g_df_func_node_start, fi * 8)) - w64(g_ir_func_instr_count, fi * 8, - r64(g_df_func_node_count, fi * 8)) - fi = fi + 1 - - // After lowering, compute per-variable usage counts for optimization passes - compute_usage_counts() - - // Register allocation lives in the backend. The .ccr ENT section is empty - // in this path; corearch computes live ranges and entries after loading the - // NOD stream. - // --- Mark function boundary in graph --- fn df_begin_func(func_idx: int): diff --git a/src/compiler/ty_shadow.cr b/src/compiler/ty_shadow.cr index 99239198..bb770851 100644 --- a/src/compiler/ty_shadow.cr +++ b/src/compiler/ty_shadow.cr @@ -481,7 +481,7 @@ fn sh_tk_split_impl(opcode: int, tk: int, count_fail: int): if sh_tk_is_type_face(opcode) != 0 && sh_tk_row_state(tk) == 0: g_tk_face_fail = g_tk_face_fail + 1 -// 槽 -> 派生码(唯一派生点;dataflow.cr 的 lower_to_ccr / ir_gen.cr 的 emit / +// 槽 -> 派生码(唯一派生点;ccr_lower.cr 的 lower_to_ccr / ir_gen.cr 的 emit / // cir_cache.cr 的装载侧共用):辅码 ≠ 0 -> 辅码;无项 -> 0;否则 `atom_of(项)` // (不可逆 -> -1:显式、响亮,绝不近似成 0/1)。 fn sh_dfn_code_of_slots(tk_term: int, tk_aux: int) -> int: From 6922d06eeb666920091f5e716ba66005ba820418 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:11:44 +0800 Subject: [PATCH 05/58] refactor: route frontend extents and offsets through mapping --- build_selfhost_native.py | 1 + src/compiler/_import.cr | 1 + src/compiler/ir_gen.cr | 85 ++---------------------- src/compiler/mapping.cr | 104 ++++++++++++++++++++++++++++++ src/compiler/provenance_verify.cr | 10 +-- src/compiler/ptr_analysis.cr | 12 ++-- 6 files changed, 123 insertions(+), 90 deletions(-) create mode 100644 src/compiler/mapping.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 4388bc8c..b29bdadd 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -325,6 +325,7 @@ def main(): 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', + 'src/compiler/mapping.cr', # R2 P0 类型项引擎(建层):类型项 DAG 表 + 构造去重 → 判定引擎(桩)→ 自测通道。 # 置于 checker 之后(计划 Step 9 指定位置);引擎尚无 checker 侧消费者, # 本批为纯增量(checker/ir_gen/后端零改动 = 产物零变化)。 diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index f55a868c..ad029cc7 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -8,6 +8,7 @@ import dyn_arr import lexer import parser import checker +import mapping import type_terms import type_engine // Interface registry: the entry table and iface_* query API. It is placed after diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index c7f04a73..6759e36a 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -1040,52 +1040,9 @@ fn ti_from_type_expr(node: int) -> int: // Complex type expressions: delegate to checker's resolver return res_type_node(node) -// Type size in bytes +// Type size in bytes. The classic mapping lives in mapping.cr. fn type_size(ti: int) -> int: - if ti == TI_DEX_S: - return 8 - // TI_DEX_S uses an 8-byte slot and is not in the type table. Row 8 is a - // placeholder; user types start at 9. - if ti == TI_CHAR: - return 4 - // not in type table - if ti < 0 || ti >= g_type_count: - return 8 - k := get_type_kind(ti) - if k == TYP_BASE: - d := get_type_data(ti) - if d == TY_INT: - return 8 - if d == TY_DEX: - return 8 - if d == TY_BOOL: - return 1 - if d == TY_STRING: - return 8 - if d == TY_UNIT: - return 0 - if d == TY_CHAR: - return 4 - return 8 - if k == TYP_ARRAY: - elem := get_type_data(ti) - cnt := get_type_extra(ti) - return type_size(elem) * cnt - if k == TYP_PTR || k == TYP_REF: - return 8 - if k == TYP_SLICE: - return 16 - if k == TYP_NAMED: - name_ni := get_type_data(ti) - si : ., mut = 0 - loop: - if si >= g_struct_count: - break - if si_name(si) == name_ni: - return si_field_count(si) * 8 - si = si + 1 - return 8 - return 8 + return mp_size(ti) fn ptr_pointee_type(var_idx: int) -> int: if var_idx < 0: @@ -1103,39 +1060,9 @@ fn ptr_access_width(var_idx: int) -> int: return 8 return width -// Type alignment in bytes +// Type alignment in bytes. The classic mapping lives in mapping.cr. fn type_align(ti: int) -> int: - if ti == TI_DEX_S: - return 8 - // TI_DEX_S has 8-byte alignment and is not in the type table. Row 8 is a - // placeholder. - if ti == TI_CHAR: - return 4 - if ti < 0 || ti >= g_type_count: - return 8 - k := get_type_kind(ti) - if k == TYP_BASE: - d := get_type_data(ti) - if d == TY_INT: - return 8 - if d == TY_DEX: - return 8 - if d == TY_BOOL: - return 1 - if d == TY_STRING: - return 8 - if d == TY_CHAR: - return 4 - return 8 - if k == TYP_ARRAY: - return type_align(get_type_data(ti)) - if k == TYP_PTR || k == TYP_REF: - return 8 - if k == TYP_SLICE: - return 8 - if k == TYP_NAMED: - return 8 - return 8 + return mp_align(ti) // Resolve a type index to a struct info index. // Returns -1 if the type is not a struct type. @@ -1177,7 +1104,7 @@ fn ti_has_field(ti: int, name: string) -> int: fi = fi + 1 return 0 -// Get field byte offset (each field is 8 bytes) +// Get field byte offset. The slot stride comes from mapping.cr. fn ti_field_offset(ti: int, name: string) -> int: si := ti_resolve_struct(ti) if si < 0: @@ -1190,7 +1117,7 @@ fn ti_field_offset(ti: int, name: string) -> int: fn_ni := si_field_name(si, fi) fname := istr_get(fn_ni) if str_eq(fname, name) != 0: - return fi * 8 + return mp_field_offset(fi) fi = fi + 1 return -1 diff --git a/src/compiler/mapping.cr b/src/compiler/mapping.cr new file mode 100644 index 00000000..52dedf32 --- /dev/null +++ b/src/compiler/mapping.cr @@ -0,0 +1,104 @@ +// === mapping.cr === +// Classic x86-64 mapping for value extents and slot strides. +// The semantic front end asks for sizes and offsets through these functions. +// A non-classic mapping can replace this file without changing the IR passes. + +MP_WORD_BYTES : int = 8 +MP_SLICE_BYTES : int = 16 + +// Value size in bytes. +fn mp_size(ti: int) -> int: + if ti == TI_DEX_S: + return MP_WORD_BYTES + // TI_DEX_S uses an 8-byte slot and is not in the type table. Row 8 is a + // placeholder; user types start at 9. + if ti == TI_CHAR: + return 4 + // not in type table + if ti < 0 || ti >= g_type_count: + return MP_WORD_BYTES + k := get_type_kind(ti) + if k == TYP_BASE: + d := get_type_data(ti) + if d == TY_INT: + return MP_WORD_BYTES + if d == TY_DEX: + return MP_WORD_BYTES + if d == TY_BOOL: + return 1 + if d == TY_STRING: + return MP_WORD_BYTES + if d == TY_UNIT: + return 0 + if d == TY_CHAR: + return 4 + return MP_WORD_BYTES + if k == TYP_ARRAY: + elem := get_type_data(ti) + cnt := get_type_extra(ti) + return mp_size(elem) * cnt + if k == TYP_PTR || k == TYP_REF: + return MP_WORD_BYTES + if k == TYP_SLICE: + return MP_SLICE_BYTES + if k == TYP_NAMED: + name_ni := get_type_data(ti) + si : ., mut = 0 + loop: + if si >= g_struct_count: + break + if si_name(si) == name_ni: + return mp_extent(si_field_count(si), TI_INT) + si = si + 1 + return MP_WORD_BYTES + return MP_WORD_BYTES + +// Value alignment in bytes. +fn mp_align(ti: int) -> int: + if ti == TI_DEX_S: + return MP_WORD_BYTES + // TI_DEX_S has 8-byte alignment and is not in the type table. Row 8 is a + // placeholder. + if ti == TI_CHAR: + return 4 + if ti < 0 || ti >= g_type_count: + return MP_WORD_BYTES + k := get_type_kind(ti) + if k == TYP_BASE: + d := get_type_data(ti) + if d == TY_INT: + return MP_WORD_BYTES + if d == TY_DEX: + return MP_WORD_BYTES + if d == TY_BOOL: + return 1 + if d == TY_STRING: + return MP_WORD_BYTES + if d == TY_CHAR: + return 4 + return MP_WORD_BYTES + if k == TYP_ARRAY: + return mp_align(get_type_data(ti)) + if k == TYP_PTR || k == TYP_REF: + return MP_WORD_BYTES + if k == TYP_SLICE: + return MP_WORD_BYTES + if k == TYP_NAMED: + return MP_WORD_BYTES + return MP_WORD_BYTES + +// Slot stride for an array element or an indexed pointer. +fn mp_stride(ti: int) -> int: + return MP_WORD_BYTES + +// Size of a block containing count slots. +fn mp_extent(count: int, ti: int) -> int: + return count * mp_stride(ti) + +// Byte offset of an indexed slot. +fn mp_offset(index: int, ti: int) -> int: + return index * mp_stride(ti) + +// Byte offset of a struct field. +fn mp_field_offset(field_index: int) -> int: + return field_index * MP_WORD_BYTES diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index 6ee5912f..f015dd75 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -16,10 +16,10 @@ fn get_alloc_size(alloc_seq: int) -> int: // IR_ALLOC marks a scalar variable slot, not a heap allocation, so it has no // allocation size. if op == IR_ALLOC_ARRAY: - return s1 * 8 - // count * 8; elements are always 8 bytes in IR_ALLOC_ARRAY. + return mp_extent(s1, TI_INT) + // count * slot stride; elements are always one slot in IR_ALLOC_ARRAY. if op == IR_ALLOC_STRUCT: - // Match IR_ALLOC_STRUCT: field count times 8. + // Match IR_ALLOC_STRUCT: field count times the slot stride. fi : int = -1 si2 : ., mut = 0 loop: @@ -30,8 +30,8 @@ fn get_alloc_size(alloc_seq: int) -> int: break si2 = si2 + 1 if fi >= 0: - return si_field_count(fi) * 8 - return 8 + return mp_extent(si_field_count(fi), TI_INT) + return mp_stride(TI_INT) return -1 // unknown; leave the check to run time fn get_alloc_var(alloc_seq: int) -> int: diff --git a/src/compiler/ptr_analysis.cr b/src/compiler/ptr_analysis.cr index 5c3ca592..cd5cd3b6 100644 --- a/src/compiler/ptr_analysis.cr +++ b/src/compiler/ptr_analysis.cr @@ -260,9 +260,9 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if op == IR_ADDR_INDEX && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 - // The index decides the offset. A constant index gives idx*8; - // a dynamic index gives -1, which makes provenance_verify emit - // a runtime check. + // The index decides the offset. A constant index gives + // index times the mapping stride; a dynamic index gives -1, + // which makes provenance_verify emit a runtime check. base_off := r64(g_offsets, s1 * 8) idx_val : int = -1 if s2 >= 0: @@ -272,12 +272,12 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if prod_op == IR_CONST: idx_val = r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if idx_val >= 0 && base_off >= 0: - w64(g_offsets, d * 8, base_off + idx_val * 8) + w64(g_offsets, d * 8, base_off + mp_offset(idx_val, TI_INT)) else: w64(g_offsets, d * 8, -1) // IR_SLICE propagates the underlying array provenance for - // d = &arr[low]. A constant low gives offset low*8. + // d = &arr[low]. A constant low gives low times the mapping stride. if op == IR_SLICE && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 @@ -290,7 +290,7 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if prod_op == IR_CONST: low_val = r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if low_val >= 0 && base_off >= 0: - w64(g_offsets, d * 8, base_off + low_val * 8) + w64(g_offsets, d * 8, base_off + mp_offset(low_val, TI_INT)) else: w64(g_offsets, d * 8, -1) From c3d6410f714bbcf3fb953dc4602d0cc8bb834e34 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:13:09 +0800 Subject: [PATCH 06/58] refactor: move frontend environment and builtin lookup into prelude --- build_selfhost_native.py | 2 ++ src/arch/x86_64/backend_globals.cr | 4 ++++ src/compiler/_import.cr | 1 + src/compiler/checker.cr | 33 ++++++++---------------------- src/compiler/globals.cr | 2 -- src/compiler/prelude.cr | 20 ++++++++++++++++++ src/lsp/_import.cr | 1 + 7 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 src/compiler/prelude.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index b29bdadd..f255aaf2 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -326,6 +326,7 @@ def main(): 'src/compiler/parser.cr', 'src/compiler/checker.cr', 'src/compiler/mapping.cr', + 'src/compiler/prelude.cr', # R2 P0 类型项引擎(建层):类型项 DAG 表 + 构造去重 → 判定引擎(桩)→ 自测通道。 # 置于 checker 之后(计划 Step 9 指定位置);引擎尚无 checker 侧消费者, # 本批为纯增量(checker/ir_gen/后端零改动 = 产物零变化)。 @@ -428,6 +429,7 @@ def main(): 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', + 'src/compiler/prelude.cr', # R2 P1:checker 的判定入口引用桥接层(sh_term_of_ti;影子挂点已随 P5 T5 下线)→ # corelsp 也必须链接 引擎 + 桥接层(相对顺序与 corec 一致;corearch 无 checker 仍不含)。 'src/compiler/type_terms.cr', diff --git a/src/arch/x86_64/backend_globals.cr b/src/arch/x86_64/backend_globals.cr index 19496864..33f87b14 100644 --- a/src/arch/x86_64/backend_globals.cr +++ b/src/arch/x86_64/backend_globals.cr @@ -1,6 +1,10 @@ // === backend_globals.cr === // x86-64 backend state. Loaded only by corearch. +// Interned names for the syscall builtins resolved by the backend. +g_ni_syscall3 : int, mut +g_ni_syscall4 : int, mut + // Backend arrays (all dynamic byte buffers) g_x86_str_offs : string, mut g_x86_str_count : int, mut diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index ad029cc7..4f17e990 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -9,6 +9,7 @@ import lexer import parser import checker import mapping +import prelude import type_terms import type_engine // Interface registry: the entry table and iface_* query API. It is placed after diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index d7874021..980884eb 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -2610,16 +2610,11 @@ fn s1p_src_frag(line: int) -> string: return str_sub(g_source, start, end - start) // ─── S1P 台账三件:检查、单实参判定、枚举环,与 s1p_arg_ring 同一契约 ─── -// 检查(懒初始化 + `CORE_S1P`):所有类型引擎调用都必须在检查后(默认位一步不执行 = 零足迹)。 +// 检查:进程启动时由 prelude_init 读取 `CORE_S1P`。所有类型引擎调用都必须在 +// 检查后(默认位一步不执行 = 零足迹)。 fn s1p_gate() -> int: if g_s1p_seen == 0: - // 哨兵必须用零值(详情见 `s1p_arg_ring` 内注 + 未完成项) - g_s1p_seen = 1 - ev2 := get_env("CORE_S1P") - if str_len(ev2) > 0 && ev2 == "1": - g_s1p_on = 1 - else: - g_s1p_on = 0 + prelude_init() return g_s1p_on // 单实参:跳过规则(2`None`->`T?` / 3字面量多态 / 形参型不可得 / 未推断出形),因此否则 `unify_types` @@ -2767,16 +2762,7 @@ fn s1p_arg_ring(fi: int, first_arg: int, arg_count: int, param_skip: int, is_ext if pi2 >= pc: break if g_s1p_seen == 0: - // 懒初始化:`CORE_S1P=1` 才开(`os.cr` 的 `get_env` 在 corec/corelsp 两单元皆在) - // 哨兵必须用零值。globals.cr 里的 g_s1p_on : int, mut = -1 在 Python bootstrap - // 构建 corec 时,初值会丢失成 0(.quad 0),导致未初始化 = -1判据恒假, - // 懒初始化永不执行,开关恒关。该行为只影响 bootstrap 构建的 corec,self-hosted 正常。 - g_s1p_seen = 1 - ev2 := get_env("CORE_S1P") - if str_len(ev2) > 0 && ev2 == "1": - g_s1p_on = 1 - else: - g_s1p_on = 0 + prelude_init() // 整块纳入检查:仅把 print 纳入检查不够,本块里的类型引擎调用 // (`infer_expr`/`res_call_type`/`unify_types`)会向 TYPE(7) 段的项表里留项(memo) // 因此输出 `.ccr` 的段 7 变长(例如检查只覆盖 print 时 `opt_dex_test.cr` 的 `.ccr` @@ -3153,13 +3139,10 @@ fn infer_expr(node: int) -> int: if ast_kind(func_node) == EXPR_AT: ast_set_b(func_node, first_arg) return infer_expr(func_node) - // Check builtins (syscall3/syscall4 — OS communication, no .cr body) - if func_ni >= 0: - s := istr_get(func_ni) - if s == "syscall3": - return TI_INT - if s == "syscall4": - return TI_INT + // Check prelude builtins (syscall3/syscall4 — OS communication, no .cr body) + prelude_ti := prelude_builtin_return_ti(func_ni) + if prelude_ti >= 0: + return prelude_ti // Check for SYM_SO_FN (.so extension registered) so_fn_fi : ., mut = -1 if func_ni >= 0: diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 543a95b2..3f4d26b7 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -413,8 +413,6 @@ g_loop_region_exit : string, mut g_loop_region_count : int, mut g_loop_region_cap : int, mut // Pre-computed interned string indices for builtin function name matching -g_ni_syscall3 : int, mut -g_ni_syscall4 : int, mut g_ni_load8 : int, mut g_ni_store8 : int, mut g_ni_load64 : int, mut diff --git a/src/compiler/prelude.cr b/src/compiler/prelude.cr new file mode 100644 index 00000000..b0d42ed2 --- /dev/null +++ b/src/compiler/prelude.cr @@ -0,0 +1,20 @@ +// === prelude.cr === +// Frontend prelude: configuration read at process start and the small set of +// externally provided builtins. + +fn prelude_init(): + g_s1p_seen = 1 + g_s1p_on = 0 + ev2 := get_env("CORE_S1P") + if str_len(ev2) > 0 && ev2 == "1": + g_s1p_on = 1 + +fn prelude_builtin_return_ti(name_ni: int) -> int: + if name_ni < 0: + return -1 + s := istr_get(name_ni) + if s == "syscall3": + return TI_INT + if s == "syscall4": + return TI_INT + return -1 diff --git a/src/lsp/_import.cr b/src/lsp/_import.cr index c1a8134e..1c3313f0 100644 --- a/src/lsp/_import.cr +++ b/src/lsp/_import.cr @@ -3,6 +3,7 @@ import lexer import parser import checker +import prelude import diag import module import globals From a6e4eac67c7b725362ed5448f22ca48b5702b33f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:15:24 +0800 Subject: [PATCH 07/58] refactor: move CCR record layouts and backend keys to schema modules --- build_selfhost_native.py | 4 ++++ src/arch/x86_64/backend_schema.cr | 7 +++++++ src/compiler/_import.cr | 1 + src/compiler/ast.cr | 7 ------- src/compiler/ccr_schema.cr | 16 ++++++++++++++++ src/compiler/type_terms.cr | 11 ++--------- src/lsp/_import.cr | 1 + src/targets/x86_64-linux/_import.cr | 2 ++ 8 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 src/arch/x86_64/backend_schema.cr create mode 100644 src/compiler/ccr_schema.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index f255aaf2..d206bea5 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -40,6 +40,7 @@ 'src/stdlib/cli.cr', 'src/stdlib/toml.cr', 'src/compiler/ast.cr', + 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', ] @@ -62,6 +63,7 @@ # 编码原语 → instr 之后;frame.cr → tag2l(pf_epilogue 函数尾块调 e2_mw_*; # tag2l 反向消费 frame 的 g2_tag_off——同轴双向引用,波 1 Task 4 裁定可接受)。 arch_x86_64_files = [ + 'src/arch/x86_64/backend_schema.cr', 'src/arch/x86_64/backend_globals.cr', 'src/arch/x86_64/backend_arrays.cr', 'src/arch/x86_64/regalloc.cr', @@ -320,6 +322,7 @@ def main(): 'src/stdlib/fmt.cr', 'src/stdlib/cli.cr', 'src/compiler/ast.cr', + 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/lexer.cr', @@ -424,6 +427,7 @@ def main(): 'src/stdlib/chan.cr', 'src/stdlib/sched.cr', 'src/compiler/ast.cr', + 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/lexer.cr', diff --git a/src/arch/x86_64/backend_schema.cr b/src/arch/x86_64/backend_schema.cr new file mode 100644 index 00000000..27e536a4 --- /dev/null +++ b/src/arch/x86_64/backend_schema.cr @@ -0,0 +1,7 @@ +// === backend_schema.cr === +// Optimization metadata keys written by the x86-64 backend into the .ccr OPT +// section. The frontend does not read these keys. + +OPT_KEY_REG_ASSIGN : int = 0 // [var_idx:u32, reg_num:u8] +OPT_KEY_STACK_SHARE : int = 1 // [var_idx:u32, mapped_to:u32] +OPT_KEY_CSE : int = 2 // [op:u32, s1:u32, s2:u32, res:u32] diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index 4f17e990..1b1b62d8 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -3,6 +3,7 @@ // The ELF pipeline is the only backend. import cli import ast +import ccr_schema import globals import dyn_arr import lexer diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index bb5065db..b8ca1a7c 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -696,10 +696,3 @@ g_df_cap : int, mut // Usage count: how many consumers each IR variable has g_var_use_count : string, mut g_var_use_count_cap : int, mut - - -// ── Optimization metadata keys (.ccr v3+ extensible section) ── -OPT_KEY_REG_ASSIGN : int = 0 // [var_idx:u32, reg_num:u8]... -OPT_META_STRIDE : int = 64 // header (8) + up to 5 register pairs -OPT_KEY_STACK_SHARE : int = 1 // [var_idx:u32, mapped_to:u32]... -OPT_KEY_CSE : int = 2 // [op:u32, s1:u32, s2:u32, res:u32]... diff --git a/src/compiler/ccr_schema.cr b/src/compiler/ccr_schema.cr new file mode 100644 index 00000000..350cef5b --- /dev/null +++ b/src/compiler/ccr_schema.cr @@ -0,0 +1,16 @@ +// === ccr_schema.cr === +// Record layouts shared by .ccr serialization and the in-memory tables that +// feed it. These constants describe the carrier, not the target machine. + +// Type-term entry: six 8-byte fields in memory. +ESZ_TYPE_TERM : int = 48 +ESZ_TYPE_TERM_DISK : int = 40 +OFF_TT_TAG : int = 0 +OFF_TT_A : int = 8 +OFF_TT_B : int = 16 +OFF_TT_C : int = 24 +OFF_TT_D : int = 32 +OFF_TT_HASH : int = 40 + +// Optimization metadata record: header plus up to five register pairs. +OPT_META_STRIDE : int = 64 diff --git a/src/compiler/type_terms.cr b/src/compiler/type_terms.cr index 9be8aefb..0540ffd7 100644 --- a/src/compiler/type_terms.cr +++ b/src/compiler/type_terms.cr @@ -22,19 +22,12 @@ TT_VAR : int = 8 TT_NIL : int = 9 TT_CONS : int = 10 -// 条目 = 48B,六字段各 8B(与 g_ir_entries 同风格:字节缓冲 + 偏移常量) -ESZ_TYPE_TERM : int = 48 -OFF_TT_TAG : int = 0 -OFF_TT_A : int = 8 -OFF_TT_B : int = 16 -OFF_TT_C : int = 24 -OFF_TT_D : int = 32 -OFF_TT_HASH : int = 40 +// 条目布局常量(ESZ_TYPE_TERM、OFF_TT_*)在 ccr_schema.cr。条目为六字段各 8B +// (与 g_ir_entries 同风格:字节缓冲 + 偏移常量)。 // Disk term record in TYPE(7): 5 * i64 = 40B {tag, a, b, c, d}. The in-memory // hash slot is omitted because it is a pure function of the five fields; the // loader recomputes it with tt_hash5. Field width is 8B, matching memory. -ESZ_TYPE_TERM_DISK : int = 40 // ─── 访问器(i = 类型项行号;调用方保证 0 ≤ i < tt_count())─── fn tt_tag(i: int) -> int: diff --git a/src/lsp/_import.cr b/src/lsp/_import.cr index 1c3313f0..49699fec 100644 --- a/src/lsp/_import.cr +++ b/src/lsp/_import.cr @@ -6,4 +6,5 @@ import checker import prelude import diag import module +import ccr_schema import globals diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux/_import.cr index a87c9404..039290ae 100644 --- a/src/targets/x86_64-linux/_import.cr +++ b/src/targets/x86_64-linux/_import.cr @@ -5,8 +5,10 @@ import fmt import io import toml import ast +import ccr_schema import globals import dyn_arr +import backend_schema import backend_globals import backend_arrays import hit From 4bb9d0bdbb41325dfc0964f2415631ede0e705f8 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:16:59 +0800 Subject: [PATCH 08/58] refactor: move literal conversion out of the lexer and name the goroutine stack size --- build_selfhost_native.py | 2 + .../adr/adr-0017-concurrency-gmp.md | 2 +- src/compiler/_import.cr | 1 + src/compiler/lexer.cr | 128 +----------------- src/compiler/literal.cr | 127 +++++++++++++++++ src/lsp/_import.cr | 1 + src/stdlib/goroutine.cr | 10 +- 7 files changed, 141 insertions(+), 130 deletions(-) create mode 100644 src/compiler/literal.cr diff --git a/build_selfhost_native.py b/build_selfhost_native.py index d206bea5..32578dd4 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -325,6 +325,7 @@ def main(): 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', + 'src/compiler/literal.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', @@ -430,6 +431,7 @@ def main(): 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', + 'src/compiler/literal.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', diff --git a/docs/maintainer/adr/adr-0017-concurrency-gmp.md b/docs/maintainer/adr/adr-0017-concurrency-gmp.md index 73807d3d..7a3eb422 100644 --- a/docs/maintainer/adr/adr-0017-concurrency-gmp.md +++ b/docs/maintainer/adr/adr-0017-concurrency-gmp.md @@ -10,7 +10,7 @@ ## 决策 -- 采用与 Go runtime 同构的 GMP 简化模型:G = `goroutine`(`fiber`,16KB 栈,独立 arena),M = machine,P 不分离,合并到 M;没有 `GOMAXPROCS`,M 的数量等于 CPU 核数。 +- 采用与 Go runtime 同构的 GMP 简化模型:G = `goroutine`(`fiber`,16KB 栈,独立 arena),M = machine,P 不分离,合并到 M;没有 `GOMAXPROCS`,M 的数量等于 CPU 核数。栈尺寸常量是 `src/stdlib/goroutine.cr` 的 `G_STACK_SIZE`。 - G 状态机(`_Gidle`/`_Grunnable`/`_Grunning`/`_Gwaiting`/`_Gdead`);本地 run queue 加全局队列偷取;切换是用户态 `fiber_switch`(协作式)。 - channel 通信(环形缓冲 + `send_wait`/`recv_wait` 队列,值拷贝语义);go 返回 `result_ch`。 diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index 1b1b62d8..346a40df 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -6,6 +6,7 @@ import ast import ccr_schema import globals import dyn_arr +import literal import lexer import parser import checker diff --git a/src/compiler/lexer.cr b/src/compiler/lexer.cr index 10cba341..79d8c8d0 100644 --- a/src/compiler/lexer.cr +++ b/src/compiler/lexer.cr @@ -191,135 +191,11 @@ fn lookup_keyword(s: string) -> int: return T_SPEC return T_IDENT -// 2^k(纯整数,k >= 0) -fn pow2i(k: int) -> int: - v : int = 1 - i : ., mut = 0 - loop: - if i >= k: - break - v = v * 2 - i = i + 1 - return v - -// Convert a decimal string to IEEE 754 binary64 bits using integer arithmetic -// (at most 18 significant digits). The value is ip + fp/den: find the integer -// bit width, long-divide the fraction to 64 bits, then take a 53-bit significand. -// The result truncates rather than rounds, within about 2 ulp for 18 digits. -fn str_to_f64_bits(s: string) -> int: - sl := str_len(s) - i : ., mut = 0 - neg : int = 0 - if i < sl && load8(s, i) == 45: - neg = 1 - i = i + 1 - ip : int = 0 - fp : int = 0 - den : int = 1 - sd : int = 0 - ip_digits : ., mut = 0 - fp_digits : ., mut = 0 - too_wide : ., mut = 0 - loop: - if i >= sl: - break - c := load8(s, i) - if c == 46: - sd = 1 - i = i + 1 - continue - if c < 48 || c > 57: - break - if sd != 0: - fp_digits = fp_digits + 1 - if fp_digits <= 18: - fp = fp * 10 + (c - 48) - den = den * 10 - else: - ip_digits = ip_digits + 1 - if ip_digits <= 19: - digit_ip := c - 48 - if ip > 922337203685477580 || (ip == 922337203685477580 && digit_ip > 7 + neg): - too_wide = 1 - else: - ip = ip * 10 + digit_ip - else: - too_wide = 1 - i = i + 1 - if too_wide != 0: - add_error("decimal literal integer part is too wide") - return 0 - if ip == 0 && fp == 0: - if neg != 0: - return (-9223372036854775807 - 1) - return 0 - // 小数 64 位(长除:r=fp,每轮 r×2 vs den) - frac64 : int = 0 - r : ., mut = fp - bits : ., mut = 0 - loop: - if bits >= 64: - break - if r >= 4611686018427387904: - r = r / 2 - den = den / 2 - r = r * 2 - frac64 = frac64 * 2 - if r >= den: - r = r - den - frac64 = frac64 + 1 - bits = bits + 1 - // 整数部分位宽 - bi : ., mut = 0 - t2 : ., mut = ip - loop: - if t2 == 0: - break - t2 = t2 / 2 - bi = bi + 1 - mant : int = 0 - exp : int = 0 - if bi > 0: - sh := 53 - bi - if sh >= 0: - mant = (ip * pow2i(sh)) + (frac64 / pow2i(64 - sh)) - else: - // Avoid pow2i of a negative value for integer parts wider than - // the binary64 significand. - mant = ip / pow2i(0 - sh) - exp = 1023 + (bi - 1) - else: - // 纯小数:frac64 的最高位 - bf : ., mut = 0 - t2 = frac64 - loop: - if t2 == 0: - break - t2 = t2 / 2 - bf = bf + 1 - if bf == 0: - if neg != 0: - return (-9223372036854775807 - 1) - return 0 - sh := 53 - bf - if sh >= 0: - mant = frac64 * pow2i(sh) - else: - mant = frac64 / pow2i(-sh) - exp = 1023 + (bf - 65) - // Assemble; mant is truncated to 52 bits without rounding. - bits64 : int = 0 - if exp > 0 && exp < 2047: - bits64 = (mant % 4503599627370496) + exp * 4503599627370496 - if neg != 0: - bits64 = bits64 + (-9223372036854775807 - 1) - return bits64 - // Parse an integer literal. Supports digit separators and 0x/0o/0b prefixes; // malformed literals are lexer errors. // Language int is an unbounded mathematical integer. The overflow guard reports an -// encoding limit: the compiler represents values as a 64-bit machine word, and a -// larger value has no carrier there. It is not a semantic overflow. Wider +// encoding limit of the current token payload: a decimal value wider than 64 bits +// has no carrier in this interface. It is not a semantic overflow. Wider // representations are future work. fn str_int_literal(s: string) -> int: sl := str_len(s) diff --git a/src/compiler/literal.cr b/src/compiler/literal.cr new file mode 100644 index 00000000..01c4af57 --- /dev/null +++ b/src/compiler/literal.cr @@ -0,0 +1,127 @@ +// === literal.cr === +// Numeric literal conversion. The lexer stores the source text; the parser +// converts it to the representation used by the AST. + +// 2^k(纯整数,k >= 0) +fn pow2i(k: int) -> int: + v : int = 1 + i : ., mut = 0 + loop: + if i >= k: + break + v = v * 2 + i = i + 1 + return v + +// Convert a decimal string to IEEE 754 binary64 bits using integer arithmetic +// (at most 18 significant digits). The value is ip + fp/den: find the integer +// bit width, long-divide the fraction to 64 bits, then take a 53-bit significand. +// The result truncates rather than rounds, within about 2 ulp for 18 digits. +fn str_to_f64_bits(s: string) -> int: + sl := str_len(s) + i : ., mut = 0 + neg : int = 0 + if i < sl && load8(s, i) == 45: + neg = 1 + i = i + 1 + ip : int = 0 + fp : int = 0 + den : int = 1 + sd : int = 0 + ip_digits : ., mut = 0 + fp_digits : ., mut = 0 + too_wide : ., mut = 0 + loop: + if i >= sl: + break + c := load8(s, i) + if c == 46: + sd = 1 + i = i + 1 + continue + if c < 48 || c > 57: + break + if sd != 0: + fp_digits = fp_digits + 1 + if fp_digits <= 18: + fp = fp * 10 + (c - 48) + den = den * 10 + else: + ip_digits = ip_digits + 1 + if ip_digits <= 19: + digit_ip := c - 48 + if ip > 922337203685477580 || (ip == 922337203685477580 && digit_ip > 7 + neg): + too_wide = 1 + else: + ip = ip * 10 + digit_ip + else: + too_wide = 1 + i = i + 1 + if too_wide != 0: + add_error("decimal literal integer part is too wide") + return 0 + if ip == 0 && fp == 0: + if neg != 0: + return (-9223372036854775807 - 1) + return 0 + // 小数 64 位(长除:r=fp,每轮 r×2 vs den) + frac64 : int = 0 + r : ., mut = fp + bits : ., mut = 0 + loop: + if bits >= 64: + break + if r >= 4611686018427387904: + r = r / 2 + den = den / 2 + r = r * 2 + frac64 = frac64 * 2 + if r >= den: + r = r - den + frac64 = frac64 + 1 + bits = bits + 1 + // 整数部分位宽 + bi : ., mut = 0 + t2 : ., mut = ip + loop: + if t2 == 0: + break + t2 = t2 / 2 + bi = bi + 1 + mant : int = 0 + exp : int = 0 + if bi > 0: + sh := 53 - bi + if sh >= 0: + mant = (ip * pow2i(sh)) + (frac64 / pow2i(64 - sh)) + else: + // Avoid pow2i of a negative value for integer parts wider than + // the binary64 significand. + mant = ip / pow2i(0 - sh) + exp = 1023 + (bi - 1) + else: + // 纯小数:frac64 的最高位 + bf : ., mut = 0 + t2 = frac64 + loop: + if t2 == 0: + break + t2 = t2 / 2 + bf = bf + 1 + if bf == 0: + if neg != 0: + return (-9223372036854775807 - 1) + return 0 + sh := 53 - bf + if sh >= 0: + mant = frac64 * pow2i(sh) + else: + mant = frac64 / pow2i(-sh) + exp = 1023 + (bf - 65) + // Assemble; mant is truncated to 52 bits without rounding. + bits64 : int = 0 + if exp > 0 && exp < 2047: + bits64 = (mant % 4503599627370496) + exp * 4503599627370496 + if neg != 0: + bits64 = bits64 + (-9223372036854775807 - 1) + return bits64 diff --git a/src/lsp/_import.cr b/src/lsp/_import.cr index 49699fec..d1d03316 100644 --- a/src/lsp/_import.cr +++ b/src/lsp/_import.cr @@ -1,5 +1,6 @@ // _import.cr — imports applied to all .cr files in src/lsp/ // 导入编译器前端模块(lexer/parser/checker/diag/module/globals) +import literal import lexer import parser import checker diff --git a/src/stdlib/goroutine.cr b/src/stdlib/goroutine.cr index 5e6ada9e..23ce5fd0 100644 --- a/src/stdlib/goroutine.cr +++ b/src/stdlib/goroutine.cr @@ -12,6 +12,10 @@ fn goroutine_wrapper_addr() -> int: // Goroutine ID counter g_next_id : int, mut = 1 +// Fiber stack size for one G. This is an execution policy constant, not a +// frontend type-layout rule. +G_STACK_SIZE : int = 16384 + // G struct layout (80 bytes): // 0: id // 8: status (0=runnable, 1=running, 2=waiting, 3=dead) @@ -31,9 +35,9 @@ fn g_new(entry_fn: int, arg: int, arg_type: int) -> string: // alloc() routes to g_current_arena when it is >= 0 — arena_new() sets it. aid := arena_new() - // Allocate stack (16KB) — from the G's arena - stack := alloc(16384) - stack_top := stack + 16384 - 8 + // Allocate stack (G_STACK_SIZE) — from the G's arena + stack := alloc(G_STACK_SIZE) + stack_top := stack + G_STACK_SIZE - 8 // Initialize fiber. The entry is ALWAYS goroutine_entry_wrapper // (rt.s / emitted by the ELF backend): it reads saved_fn/saved_arg From f93dff5b7542c5a3e63def31e454f419d8de7881 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:18:06 +0800 Subject: [PATCH 09/58] refactor: move the backend stack map out of shared globals --- src/arch/x86_64/backend_globals.cr | 3 +++ src/compiler/globals.cr | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arch/x86_64/backend_globals.cr b/src/arch/x86_64/backend_globals.cr index 33f87b14..19e219f5 100644 --- a/src/arch/x86_64/backend_globals.cr +++ b/src/arch/x86_64/backend_globals.cr @@ -5,6 +5,9 @@ g_ni_syscall3 : int, mut g_ni_syscall4 : int, mut +// IR variable index to shared stack slot variable (-1 = own slot). +g_stack_map : string, mut + // Backend arrays (all dynamic byte buffers) g_x86_str_offs : string, mut g_x86_str_count : int, mut diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 3f4d26b7..ffa636e0 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -300,9 +300,6 @@ g_gen_map_cap : int, mut g_dyn_type_sets : string, mut g_dyn_type_set_count : int, mut g_dyn_type_set_cap : int, mut -g_stack_map : string, mut // IR var index to shared stack slot var (-1 = own slot), set by allocator -g_home_dir : string, mut // cached HOME dir for SO index lookup -g_home_dir_ok : int, mut // 1 = g_home_dir initialized g_ir_func_name_idx : string, mut g_ir_func_name_idx_cap : int, mut From 662abdb0ffa820a49ad9b2bfc63b4b96b2cc7138 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:20:50 +0800 Subject: [PATCH 10/58] docs: align the frontend boundary plan with the implemented steps --- .../2026-10-23-frontend-machine-boundary.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md index e8eaf539..fd72ebcf 100644 --- a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md +++ b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md @@ -70,7 +70,7 @@ cmp /tmp/corec2 /tmp/corec3 1. 构建通过,`corec`、`corearch`、`corelsp` 三个清单都通过存在性检查。 2. `./build/corec check src/compiler` 通过。 -3. `src/compiler` 中不再出现 `g_x86_` 或 `grow_x86_`。 +3. `src/compiler/globals.cr` 和 `src/compiler/dyn_arr.cr` 中不再出现 `g_x86_` 或 `grow_x86_`。任务 2 完成前,`src/compiler/corearch.cr` 仍会引用这些符号。 4. canary 五条全部通过。 5. `.ccr` 和 ELF 产物与改动前逐字节相同。 @@ -152,7 +152,7 @@ fn mp_offset(index: int, ti: int) -> int 2. 把 `provenance_verify.cr` 的两处 `s1 * 8` 和 `si_field_count(fi) * 8` 改为 `mp_extent`。 3. 把 `ptr_analysis.cr` 的 `idx_val * 8` 和 `low * 8` 改为 `mp_offset`。 4. 保留所有 `-1` 未知分支和运行时检查路径。 -5. 在 `corec_files` 中加入 `mapping.cr`,位置在 `ast.cr` 之后、`ir_gen.cr` 之前。 +5. 在 `corec_files` 中加入 `mapping.cr`,位置在 `checker.cr` 之后、`type_terms.cr` 之前。 ### 验收 @@ -172,11 +172,11 @@ fn mp_offset(index: int, ti: int) -> int ### 步骤 -1. 在 `globals.cr` 增加 `g_s1p_enabled`,默认值为 0。 -2. 在 `main.cr` 启动时读取 `CORE_S1P`,把结果写入 `g_s1p_enabled`。 -3. 删除 `checker.cr` 中两处 `get_env("CORE_S1P")`,改为读取 `g_s1p_enabled`。 +1. 在 `prelude.cr` 增加 `prelude_init()`,读取 `CORE_S1P` 并设置 `g_s1p_seen` 和 `g_s1p_on`。 +2. `checker.cr` 的两个 S1P 入口在首次调用时调用 `prelude_init()`,不再直接读取环境变量。 +3. 删除 `checker.cr` 中两处 `get_env("CORE_S1P")`。 4. 把 `syscall3` 和 `syscall4` 的返回类型判断移到 `prelude.cr`。`checker.cr` 只调用 `prelude_builtin_return_ti`。 -5. 删除未使用的 `g_ni_syscall3` 和 `g_ni_syscall4`。 +5. 删除未使用的 `g_ni_syscall3` 和 `g_ni_syscall4`,后端仍然使用的同名状态移到 `backend_globals.cr`。 6. 不改变 `extern` 的可选类型拒绝规则。该规则属于外部接口边界,保留在解析器中,并在文档中记录原因。 ### 验收 @@ -200,8 +200,8 @@ fn mp_offset(index: int, ti: int) -> int ### 步骤 -1. 把 `ESZ_TYPE_TERM`、`OFF_TT_*`、`ESZ_IFACE_ENTRY`、`OFF_IFM_*` 和 `.ccr` 段布局常量移到 `ccr_schema.cr`。 -2. 在 `type_terms.cr`、`iface_registry.cr` 和 `ty_shadow.cr` 中保留语义结构和判定函数,只引用 `ccr_schema.cr` 的常量。 +1. 把 `ESZ_TYPE_TERM`、`ESZ_TYPE_TERM_DISK`、`OFF_TT_*` 和 `OPT_META_STRIDE` 移到 `ccr_schema.cr`。`ESZ_IFACE_ENTRY`、`OFF_IFM_*` 是解析器和类型桥接层直接使用的内存表布局,保留在 `dyn_arr.cr`,避免语义模块反向依赖载体层。 +2. `type_terms.cr` 保留类型项结构和判定函数,只引用 `ccr_schema.cr` 的常量。`iface_registry.cr` 和 `ty_shadow.cr` 的内存表访问保持不变。 3. 把 `OPT_KEY_REG_ASSIGN`、`OPT_KEY_STACK_SHARE` 和 `OPT_KEY_CSE` 移到后端使用的 `src/arch/x86_64/backend_schema.cr`,因为只有后端读取这些键。 4. `ast.cr` 不再声明后端优化键。`ir_gen.cr` 和 `opt.cr` 不使用这些键。 5. 更新 `corec`、`corearch`、`corelsp` 三个清单的常量可见顺序。 @@ -227,7 +227,7 @@ fn mp_offset(index: int, ti: int) -> int 1. 把 `str_to_f64_bits` 从 `lexer.cr` 移到 `parser.cr` 或独立的数值字面量模块。词法层只保留原始数字字符串。 2. 为 `str_int_literal` 的 64 位上限增加明确注释,说明这是当前 token 载体的编码限制,不是语言语义上限。是否支持任意精度由后续多字整数设计决定,本任务不改变行为。 3. 把 `goroutine.cr` 的 `alloc(16384)` 改为命名常量 `G_STACK_SIZE`,值仍为 16384。该常量属于运行时执行策略,不进入前端。 -4. 更新 `docs/maintainer/design/execution-model.md` 中的栈尺寸表述,说明常量位置。 +4. 更新 `docs/maintainer/adr/adr-0017-concurrency-gmp.md` 中的栈尺寸表述,指向 `G_STACK_SIZE`。 ### 验收 From edff55542f552b43f7ba8cc2ba2a8569734560f8 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:23:07 +0800 Subject: [PATCH 11/58] docs: define arbitrary-precision int design --- ...26-10-23-arbitrary-precision-int-design.md | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md diff --git a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md new file mode 100644 index 00000000..13477fef --- /dev/null +++ b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md @@ -0,0 +1,166 @@ +# 任意精度整数设计 + +## 概述 + +`int` 是数学整数,没有固定宽度。前端只处理数学整数和运算,不决定立即数、装箱或机器字范围。`.ccr` 保存完整值和必要的编码信息。后端决定值在目标机器上的表示,并在指针、FFI 和原始字边界做转换。源语言不新增用户可见的整数类型。 + +## 语义 + +### 值与字面量 + +- `int` 的取值范围是全体有符号数学整数。 +- 零只有一种表示,不出现负零。 +- 十进制、十六进制、八进制和二进制字面量都表示精确值。 +- 字面量没有 64 位上限。 +- 负号是语法层的一元运算,`-9223372036854775808` 合法。 +- 数字分隔符保持现有规则。 + +### 算术 + +| 运算 | 语义 | +|---|---| +| `+`、`-`、`*` | 精确整数运算,不溢出 | +| `/` | 向零截断 | +| `%` | 余数符号与被除数相同,满足 `a == (a / b) * b + a % b` | +| `==`、`!=`、`<`、`>`、`<=`、`>=` | 精确比较 | +| 一元 `-` | 精确取负 | +| 除以零 | 运行期错误 | + +### 转换 + +- `int` 转 `dex` 按当前 `IR_I2F` 的规则转换。 +- `dex` 转 `int` 按当前 `IR_F2I` 的规则向零截断。 +- `char` 与 `int` 的转换保持当前范围规则。 +- `int_str` 和 `str_int` 支持任意精度。`str_int` 遇到非数字字符时按当前规则停止。 + +### 机器字边界 + +以下位置要求值能放进 64 位机器字: + +- `@ptr_of`、`@str_of` +- `@raw_int` 的地址来源 +- `w64`、`r64` +- `syscall3`、`syscall4` 的参数 +- `extern` 的 C ABI 参数和返回值 +- 指针加减的偏移 +- 数组长度、索引、切片边界和分配大小 + +值超出机器字范围时报运行期错误。不能静默截断,也不能把大整数对象的指针当作机器字交给外部代码。 + +`@ptr_of(string)` 返回地址的数学整数。当前 x86-64 用户地址小于 2^62,因此通常落在后端的立即数范围。`@str_of` 接受数学整数地址,值必须落在可表示的用户地址范围。 + +### 数组和结构体 + +- `int` 数组元素仍占一个 8 字节槽,槽里是后端决定的整数值表示。 +- 结构体字段、元组元素和枚举 payload 中的 `int` 同样占一个槽。 +- 数组长度和索引在进入分配或寻址前转换成机器字,并做范围检查。 +- 大整数不能作为数组长度或索引。编译期常量越界直接报错,运行期值越界报运行期错误。 + +## 前端与 .ccr 的分工 + +前端只负责保留精确值,不负责决定机器表示。 + +- 小整数继续使用 `EXPR_INT` 的 64 位字段。 +- 超出有符号 64 位的字面量使用 `EXPR_BIGINT`,保存规范十进制字符串。 +- 小整数继续使用 `IR_CONST`,`s1` 是有符号 64 位值。 +- 超出有符号 64 位的整数使用 `IR_CONST_BIG`,`s1` 是规范十进制字符串的 intern 下标。 +- 前端可以在 64 位处选择存储形式,这个选择只影响载体编码。它不改变值,不产生溢出诊断,也不决定后端的立即数阈值。 +- `.ccr` 的 NOD 记录大小不变。`IR_CONST_BIG` 复用 STR 段,不新增段。 +- `CCR_VERSION` 从 9 提升到 10,旧 corearch 拒绝新文件。 +- `.cir` 快照保存 `IR_CONST_BIG` 的 opcode 和字符串下标,`CIR_CACHE_VER` 从 22 提升到 23。 + +`dex` 的 binary64 位模式不是数学整数,继续使用 `IR_CONST` 的 64 位载荷。编译器内部的浮点字面量转换通过原始字边界处理,不使用 `int` 的数学值。 + +## 后端表示 + +后端用 8 字节 tagged value 表示 `int`: + +| 最高位 | 含义 | +|---|---| +| 0 | 立即数,低 63 位是二补码有符号值 | +| 1 | 指向 BigInt 对象的指针,低 63 位是地址 | + +立即数范围是 `[-2^62, 2^62-1]`。零是立即数 0。超出范围的值装箱。这个范围和标记只存在于后端,不出现在前端或 `.ccr`。 + +BigInt 对象布局: + +```text +offset 0: sign i32 -1, 0, 1 +offset 4: len u32 limb count +offset 8: limbs u32[len] +``` + +- limb 采用 32 位小端序。 +- 零的 `sign = 0`、`len = 0`。 +- 非零对象去掉高位零 limb。 +- 对象不可变,运算分配新对象。 +- 大整数对象从当前 arena 分配,arena 重置时统一回收。 + +后端 lowering 为每个 IR 值标记表示:立即数、装箱或未知。常量、循环计数和已证明范围的值保持立即数。整数运算先尝试立即数快路径,可能越界时调用 BigInt 运行时。比较在两侧都是立即数时直接比较,否则调用运行时。 + +同一个 `.ccr` 可以被不同后端用不同的立即数阈值执行。前端不参与这个判断。 + +## 运行时契约 + +运行时提供: + +```text +bi_from_word +bi_from_dec +bi_to_dec +bi_add +bi_sub +bi_mul +bi_div +bi_mod +bi_cmp +bi_neg +bi_is_zero +bi_to_word_checked +``` + +- 除 `bi_from_word` 和 `bi_to_word_checked` 外,输入和输出都是 tagged value。 +- `bi_from_word` 接收原始 64 位机器字,返回 tagged value。 +- `bi_to_word_checked` 接收 tagged value,返回原始 64 位机器字。 +- `bi_div` 和 `bi_mod` 使用向零截断,余数符号跟随被除数。 +- 除数为零时进入统一的运行期错误入口。 +- `bi_to_word_checked` 检查有符号 64 位范围;地址场景使用单独的无符号范围检查。 +- 分配失败走现有 OOM 路径,不返回半初始化对象。 + +## 指针、FFI 和解释器 + +### 指针 + +- `IR_ADDR_INDEX`、`IR_SLICE` 和指针加减先把 `int` 转成机器字。 +- 越界、负地址和超出用户地址范围都报运行期错误。 +- 现有 8 字节槽布局不变。指针写入的仍是原始机器字,不写 tagged value。 + +### C ABI + +- `extern` 的 `int` 参数和返回值在调用边界转换。 +- 参数超出有符号 64 位时,调用前报运行期错误。 +- C 返回的机器字转换成 `int`;在立即数范围内保持立即数,否则装箱。 +- `dex`、`string`、`bool` 的现有 ABI 不变。 + +### 解释器 + +- `g_ir_vals` 的 `int` 槽保存 tagged value。 +- 解释器自己的 `int` 运算使用语言语义,因此自动得到任意精度。 +- `IR_CONST_BIG` 从字符串或静态对象装入。 +- 指针、`w64`、`r64`、`@str_of` 等边界使用与 ELF 后端相同的检查和错误文本。 + +## 约束 + +1. 不新增用户可见的 `word`、`usize` 或 `i64` 类型。 +2. 不改变 `dex` 的 binary64 语义。 +3. 不实现大整数到 C ABI 的自动多字传递。超出 64 位直接报错。 +4. 不保证大整数运算与立即数运算有相同性能。 +5. 不改变 arena 的生命周期模型。 +6. 自举链的 stage0 由 Python 后端生成。Python 前端使用 Python 整数;Python 后端和运行时必须使用与 corec 相同的 tagged value 和对象布局,否则 stage0 与 self-hosted 输出不一致。 +7. 现有 2-limb `tag2l` 机制只覆盖局部加减,新实现完成后移除这条专用路径。 + +## 未决 + +1. `and`/`or` 作用于 `int` 时是否保留位运算语义。当前没有用户级移位运算符,实现前需要审计现有语法和测试。 +2. 大整数常量在 `.ccr` 中使用规范十进制字符串还是 limb 记录。本文选择十进制字符串,优先保证前端简单和可审计。 +3. 大整数打印是否在 `int_str` 中直接使用 `bi_to_dec`,还是先在 Core 层用除法实现。前者快,后者更容易自举。 From fb14a40ad90483bc993f36a3a595ad8550a67981 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:38:23 +0800 Subject: [PATCH 12/58] docs: plan arbitrary-precision int implementation --- .../2026-10-23-arbitrary-precision-int.md | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md new file mode 100644 index 00000000..1ea81038 --- /dev/null +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -0,0 +1,393 @@ +# 任意精度整数实施计划 + +## 基线 + +设计文档:`docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md` + +实现从设计提交之后开始。工作区中 `docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md`、`src/compiler/main.cr`、`src/stdlib/cli.cr` 是用户未提交内容,不纳入本计划的提交。 + +## 目标 + +把语言 `int` 从固定 64 位改成完整任意精度整数。前端保存精确值;`.ccr` 保存完整值和是否超过有符号 64 位的编码标记;后端负责立即数、装箱、机器字边界和运行期检查。 + +## 全局约束 + +1. 不新增用户可见的 `word`、`usize` 或 `i64` 类型。 +2. 每个任务先写失败测试,再写实现。测试通过后才进入下一个任务。 +3. 任务提交只包含该任务的源码、测试和必要的构建清单。 +4. 任何 `.ccr`、`.cir`、ELF 或 canary 变化必须在该任务中明确说明原因。 +5. 不使用全局字符串替换修改代码。移动或重命名时保留函数名和参数名。 +6. 解释器和 ELF 路径对同一程序必须给出相同结果。 +7. 超出机器字边界的值必须报运行期错误,不能截断或回绕。 + +## 阶段开关 + +实现期间使用 `BIGINT=1` 构建 `build/corec-big` 和 `build/corearch-big`: + +- 不设置时,现有 `build/corec` 和 `build/corearch` 保持旧 64 位路径,现有测试和 canary 不变。 +- 设置时,Python bootstrap 使用新的任意精度前端和后端,Core 新代码用新语义编译。 +- 开关只用于保持主干可构建,不是产品模式。最后一个任务删除旧路径和开关。 + +## 任务 1:BigInt 运行时对象和算术 + +### 文件 + +- 新建 `src/runtime/bigint.s` +- 新建 `tests/runtime/bigint_driver.s` +- 新建 `tests/runtime/test_bigint.py` + +### 接口 + +```text +bi_from_word +bi_from_dec +bi_to_dec +bi_add +bi_sub +bi_mul +bi_div +bi_mod +bi_cmp +bi_neg +bi_is_zero +bi_to_word_checked +bi_to_uword_checked +``` + +### 步骤 + +1. 实现 32 位 limb 的 BigInt 对象: + - header 为 `sign i32`、`len u32`。 + - limb 小端序。 + - 零规范化为 `sign=0`、`len=0`。 + - 非零对象去掉高位零 limb。 +2. 实现 tagged value: + - 最高位 0 是立即数,低 63 位二补码。 + - 最高位 1 是 BigInt 指针。 + - 立即数范围 `[-2^62, 2^62-1]`。 +3. 实现加减乘、向零截断的除法和取模、比较、取负、零判断、十进制读写和机器字范围检查。 +4. 所有对象从 `alloc` 分配,分配失败走现有 OOM 路径。 +5. `bigint_driver.s` 提供最小测试入口,通过退出码报告结果。 + +### 测试 + +`tests/runtime/test_bigint.py` 覆盖: + +- 0、1、-1、立即数边界、2^62、-2^62-1。 +- 2^100 的加减乘。 +- 正负除法截断和余数符号。 +- 除零。 +- 十进制 `10^30` 往返。 +- 有符号、无符号和地址三种机器字转换。 +- 立即数加装箱、装箱加立即数、装箱加装箱。 + +### 验收 + +```bash +python3 tests/runtime/test_bigint.py +``` + +此任务不修改编译器。 + +## 任务 2:Python 前端和 IR 的大整数字面量 + +### 文件 + +- `bootstrap/corec/frontend/lexer.py` +- `bootstrap/corec/frontend/parser.py` +- `bootstrap/corec/syntax/ast.py` +- `bootstrap/corec/frontend/type_checker.py` +- `bootstrap/corec/frontend/ir_gen.py` +- `bootstrap/corec/ir/coreir.py` +- 新建 `tests/bootstrap/test_bigint_frontend.py` + +### 步骤 + +1. Python lexer 不再拒绝超宽字面量。Python `int` 直接保存精确值。 +2. AST 支持大整数字面量,不把值截断到 64 位。 +3. Python 类型检查器对 big 常量返回 `int`。数组长度、索引、`@unroll` 等边界在超出机器字时给编译错误。 +4. Python IR 常量节点可以保存 Python 大整数。 +5. 常量折叠保持精确。 + +### 验收 + +```bash +python3 tests/bootstrap/test_bigint_frontend.py +``` + +覆盖字面量、常量折叠、边界诊断和 IR 常量。 + +## 任务 3:Python 后端 tagged value 和 BigInt 调用 + +### 文件 + +- `bootstrap/corec/backend/x86_64_stack_asm.py` +- `bootstrap/corec/backend/interpreter.py` +- `bootstrap/corec/backend/base.py` +- `tests/bootstrap/test_bigint_pipeline.py` +- `build_selfhost_native.py` + +### 步骤 + +1. Python 后端为小整数生成 tagged 立即数。 +2. 超出立即数范围的常量生成静态 BigInt 对象。 +3. 加减乘除、取模、比较生成立即数快路径和 BigInt 运行时调用。 +4. 指针、FFI、`w64/r64`、`@ptr_of`、`@str_of` 生成范围检查。 +5. `build_selfhost_native.py` 支持 `BIGINT=1`,输出 `build/corec-big` 和 `build/corearch-big`,不覆盖旧二进制。 +6. `test_bigint_pipeline.py` 用 Python 后端编译并运行一个 Core 程序,覆盖大整数字面量、算术、函数调用和打印。 + +### 验收 + +```bash +BIGINT=1 python3 build_selfhost_native.py +python3 tests/bootstrap/test_bigint_pipeline.py +``` + +不设置 `BIGINT` 时旧构建仍通过。 + +## 任务 4:Core 前端和 `.ccr` 大常量 + +### 文件 + +- `src/compiler/lexer.cr` +- `src/compiler/ast.cr` +- `src/compiler/parser.cr` +- `src/compiler/checker.cr` +- `src/compiler/opt.cr` +- `src/compiler/ir_gen.cr` +- `src/compiler/ccr_io.cr` +- `src/compiler/cir_cache.cr` +- `src/compiler/ccr_schema.cr` +- 新建 `tests/selfhost/test_bigint_literals.py` + +### 接口 + +- `T_BIGINT` token,词素是规范十进制字符串。 +- `EXPR_BIGINT`,`a` 是字符串下标,`type_val=TY_INT`。 +- `IR_CONST_BIG = 34`,`d=目标变量`,`s1=字符串下标`,`tk=TI_INT`。 +- `CCR_VERSION = 10`。 +- `CIR_CACHE_VER = 23`。 + +### 步骤 + +1. 词法不再在 64 位处报错。超出有符号 64 位的字面量生成 `T_BIGINT`,保留规范十进制文本。 +2. parser 生成 `EXPR_BIGINT`。小常量继续 `EXPR_INT`。 +3. checker 对 `EXPR_BIGINT` 返回 `TI_INT`。需要机器字的位置转换成机器字,越界报编译错误。 +4. 常量折叠使用 new compiler 的 `int` 运算。此任务由 `corec-big` 编译和验证,因此折叠本身可以使用任意精度。 +5. `ir_gen` 生成 `IR_CONST_BIG`,字符串通过现有 STR 段 intern。 +6. `ccr_io` 写入和读取 `IR_CONST_BIG`。NOD 记录大小不变。 +7. `cir_cache` 保存和恢复 `IR_CONST_BIG`。 +8. 版本号提升,旧版本拒绝。 + +### 测试 + +`test_bigint_literals.py` 覆盖: + +- `9223372036854775807`、`9223372036854775808`、`-9223372036854775809`。 +- 十六进制、八进制、二进制大常量。 +- 大常量作为普通表达式、函数参数和返回值。 +- 大常量作为数组长度或索引时报编译错误。 +- `corec-big check` 通过。 +- `corec-big ccr` 输出包含 `IR_CONST_BIG` 和规范十进制字符串。 +- `corec-big cir` 输出包含大常量。 + +### 验收 + +```bash +python3 tests/selfhost/test_bigint_literals.py +./build/corec-big check src/compiler +``` + +此任务只验证前端和载体,不要求 ELF 后端能执行大整数。 + +## 任务 5:self-hosted 后端常量和算术 + +### 文件 + +- 新建 `src/arch/x86_64/int_rep.cr` +- `src/arch/x86_64/instr.cr` +- `src/arch/x86_64/frame.cr` +- `src/arch/x86_64/regalloc.cr` +- `src/arch/x86_64/corearch.cr` +- `src/targets/x86_64-linux/main.cr` +- 新建 `tests/selfhost/test_bigint_arith.py` + +### 步骤 + +1. `int_rep.cr` 提供 tagged value 的立即数判断、拆装、范围检查和常量对象布局。 +2. `instr.cr` 实现 `IR_CONST_BIG`,生成静态 BigInt 对象或运行期解析调用。 +3. 整数加减乘除、取模、比较、取负接入立即数快路径和 BigInt 运行时。 +4. 除法按向零截断和余数符号规则生成。 +5. 除零进入统一运行期错误入口。 +6. corearch 和 project-mode 入口支持 `--big-int`,未启用时保持旧路径。 + +### 测试 + +`test_bigint_arith.py` 覆盖: + +- 小整数仍走立即数快路径。 +- 立即数边界加减。 +- 2^100 的加减乘。 +- 正负除法、取模、除零。 +- 大整数比较和取负。 +- 同一条 `.ccr` 用新旧后端分别执行小整数程序,结果相同。 + +### 验收 + +```bash +BIGINT=1 python3 build_selfhost_native.py +python3 tests/selfhost/test_bigint_arith.py +``` + +## 任务 6:容器、调用和全局变量 + +### 文件 + +- `src/arch/x86_64/instr.cr` +- `src/os/linux/callseq.cr` +- `src/compiler/ir_gen.cr` +- 新建 `tests/selfhost/test_bigint_containers.py` + +### 步骤 + +1. `int` 局部变量、参数、返回值、全局变量保存 tagged value。 +2. `IR_ALLOC`、`IR_LOAD`、`IR_STORE` 按 8 字节槽搬运。 +3. `IR_ALLOC_ARRAY`、`IR_ALLOC_STRUCT`、字段和索引操作保存 tagged value。 +4. 数组长度、索引、切片边界在进入机器字运算前做范围检查。 +5. 大整数经过递归、函数调用和全局初始化后结果不变。 + +### 测试 + +覆盖局部变量、参数、返回值、递归、全局变量、数组、结构体、元组和枚举 payload。 + +### 验收 + +```bash +python3 tests/selfhost/test_bigint_containers.py +``` + +## 任务 7:指针、FFI 和原始字边界 + +### 文件 + +- `src/arch/x86_64/instr.cr` +- `src/os/linux/callseq.cr` +- `src/compiler/checker.cr` +- `src/compiler/ir_gen.cr` +- 新建 `tests/selfhost/test_bigint_boundaries.py` + +### 步骤 + +1. `@ptr_of` 返回地址的数学整数。 +2. `@str_of`、`w64`、`r64`、`syscall3/4` 的参数和返回值做机器字范围检查。 +3. 指针加减和 `IR_ADDR_INDEX` 的偏移先转机器字。 +4. `extern` 的 `int` 参数和返回值在调用边界转换。 +5. 越界值输出包含操作名和原值的运行期错误。 + +### 测试 + +覆盖地址往返、指针偏移、大整数传给 `w64`、`@str_of`、`syscall3`、`extern` 时失败、C 返回值转换和合法范围内不误报。 + +### 验收 + +```bash +python3 tests/selfhost/test_bigint_boundaries.py +``` + +## 任务 8:解释器 + +### 文件 + +- `src/compiler/interp.cr` +- `src/compiler/ir_gen.cr` +- 新建 `tests/selfhost/test_bigint_interp.py` + +### 步骤 + +1. `g_ir_vals` 的 `int` 槽保存 tagged value。 +2. 解释器自己的 `int` 运算使用任意精度语义。 +3. `IR_CONST_BIG` 从字符串或静态对象装入。 +4. 指针、`w64/r64`、`@str_of` 使用与 ELF 后端相同的检查。 +5. 修正解释器现有的 64 位槽假设。 + +### 测试 + +同一组大整数程序分别用 `corec-big run` 和 ELF 路径执行,比较返回值、输出和错误文本。 + +### 验收 + +```bash +python3 tests/selfhost/test_bigint_interp.py +``` + +## 任务 9:切换默认语义和移除 2-limb + +### 文件 + +- `src/arch/x86_64/tag2l.cr` +- `src/arch/x86_64/frame.cr` +- `src/arch/x86_64/instr.cr` +- `src/arch/x86_64/backend_globals.cr` +- `src/arch/x86_64/backend_arrays.cr` +- `src/compiler/*` +- `tests/selfhost/test_mw_*.py` +- `tests/selfhost/test_dex_type.py` +- `tests/selfhost/test_lexer_parity.py` +- `tools/baseline/canary_values.tsv` + +### 步骤 + +1. 默认构建启用 BigInt,删除 `BIGINT` 开关和旧 64 位路径。 +2. 删除 `tag2l.cr`、`g_x86_mw_*` 状态和帧中的 tag 区。 +3. 重写依赖 64 位溢出和 2-limb 的测试: + - 原先期望 i64 溢出的测试改为期望精确大整数。 + - 原先测试 2-limb 快慢路径的测试改为测试 tagged 立即数/装箱路径。 + - `test_dex_type.py` 的大十进制字面量溢出测试改为成功用例。 + - `test_lexer_parity.py` 的字面量溢出预期同步修改。 +4. 更新 `.ccr` v10 和 `.cir` v23 的测试。 +5. 重新采集 canary 值,记录每个值变化的原因。 +6. 全量自举并比较阶段产物。 + +### 验收 + +```bash +python3 build_selfhost_native.py +./build/corec check src/compiler +python3 tests/selfhost/test_bigint_arith.py +python3 tests/selfhost/test_bigint_containers.py +python3 tests/selfhost/test_bigint_boundaries.py +python3 tests/selfhost/test_bigint_interp.py +python3 tests/selfhost/test_backend_bootstrap.py +bash tools/baseline/canary_check.sh +``` + +三阶段自举: + +```bash +./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 +/tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 +cmp /tmp/corec2 /tmp/corec3 +``` + +## 集成顺序 + +1. 任务 1 只改运行时,不进入编译器。 +2. 任务 2 到 3 改 Python bootstrap 前端和后端,用 `BIGINT=1` 构建新编译器。 +3. 任务 4 到 8 用 `corec-big` 和 `corearch-big` 逐步替换 Core 前端、后端和解释器。 +4. 任务 9 才切换默认语义,删除旧路径,更新 canary。 + +## 不包含 + +1. 不新增用户可见的机器字类型。 +2. 不实现大整数到 C ABI 的自动多字传递;超出 64 位报错。 +3. 不改变 `dex` 的 binary64 语义。 +4. 不保证大整数运算与立即数运算有相同性能。 +5. 不改变 arena 生命周期模型。 + +## 风险 + +1. Python bootstrap 后端是最大瓶颈。它必须同时支持 tagged value、BigInt 常量和算术,否则 stage0 无法生成 corec-big。 +2. 编译器自身广泛使用 `int` 作为索引和计数。立即数快路径必须在任务 5 就位,否则自举会非常慢。 +3. 现有 `test_mw_*` 测试依赖 2-limb 实现细节,不能直接保留。需要按语义重写,而不是改期望值。 +4. `.ccr` 和 `.cir` 版本变化后,旧 corearch 和旧缓存必须拒绝,不能静默读错。 +5. 指针、FFI 和原始字边界如果漏掉范围检查,会把大整数对象的指针传给外部代码,造成难定位的运行期错误。 From bb7970624ff7f7939b04a9edba1db1e08e2a591c Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:40:48 +0800 Subject: [PATCH 13/58] docs: use a pure Core 30-bit bigint runtime --- .../2026-10-23-arbitrary-precision-int.md | 18 ++++++---- ...26-10-23-arbitrary-precision-int-design.md | 33 ++++++++++++------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index 1ea81038..0685efd9 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -32,8 +32,9 @@ ### 文件 -- 新建 `src/runtime/bigint.s` -- 新建 `tests/runtime/bigint_driver.s` +- 新建 `src/runtime/bigint.cr` +- 新建 `src/runtime/bigint.LICENSE` +- 新建 `tests/runtime/bigint_driver.cr` - 新建 `tests/runtime/test_bigint.py` ### 接口 @@ -56,18 +57,21 @@ bi_to_uword_checked ### 步骤 -1. 实现 32 位 limb 的 BigInt 对象: - - header 为 `sign i32`、`len u32`。 - - limb 小端序。 +1. 在 `src/runtime/bigint.cr` 实现 30 位 digit 的 BigInt 对象: + - 对象头为 `sign`、`len`,每个字段占一个 Core 槽。 + - digit 小端序,每个 digit 保存 0..2^30-1。 - 零规范化为 `sign=0`、`len=0`。 - - 非零对象去掉高位零 limb。 + - 非零对象去掉高位零 digit。 + - limb 中间值保持在 2^62 以内,不触发 BigInt 递归。 + - 使用 GMP 风格的规范化表示、schoolbook 乘加、Knuth 除法和小整数快路径。 2. 实现 tagged value: - 最高位 0 是立即数,低 63 位二补码。 - 最高位 1 是 BigInt 指针。 - 立即数范围 `[-2^62, 2^62-1]`。 3. 实现加减乘、向零截断的除法和取模、比较、取负、零判断、十进制读写和机器字范围检查。 4. 所有对象从 `alloc` 分配,分配失败走现有 OOM 路径。 -5. `bigint_driver.s` 提供最小测试入口,通过退出码报告结果。 +5. `bigint_driver.cr` 提供最小测试入口,通过退出码报告结果。测试程序用现有 `corec` 编译,驱动只使用 30 位 digit 和 64 位以内的中间值。 +6. 在 `bigint.cr` 头注和 `bigint.LICENSE` 保留 imath 的 MIT 许可与来源说明。GMP 只参考优化思想,不复制源码。移植代码不包含 C 源文件,也不链接 C 目标文件。 ### 测试 diff --git a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md index 13477fef..23853d33 100644 --- a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md +++ b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md @@ -75,24 +75,27 @@ 后端用 8 字节 tagged value 表示 `int`: -| 最高位 | 含义 | +| 最低位 | 含义 | |---|---| -| 0 | 立即数,低 63 位是二补码有符号值 | -| 1 | 指向 BigInt 对象的指针,低 63 位是地址 | +| 0 | 立即数,存的是数学值的两倍 | +| 1 | 指向 BigInt 对象的指针加一,对象地址 8 字节对齐 | -立即数范围是 `[-2^62, 2^62-1]`。零是立即数 0。超出范围的值装箱。这个范围和标记只存在于后端,不出现在前端或 `.ccr`。 +立即数范围是 `[-2^62, 2^62-1]`。零是立即数 0。超出范围的值装箱。这个编码只存在于后端,不出现在前端或 `.ccr`。 + +使用最低位而不是最高位,是为了让 Core 运行时不需要 64 位掩码或 2^63 常量。立即数装箱是乘 2,拆箱是除 2;BigInt 指针装箱是加 1,拆箱是减 1。对象地址由 `alloc` 保证 8 字节对齐。 BigInt 对象布局: ```text -offset 0: sign i32 -1, 0, 1 -offset 4: len u32 limb count -offset 8: limbs u32[len] +offset 0: sign int -1, 0, 1 +offset 8: len int digit count +offset 16: digits int[len] 每个元素保存 0..2^30-1 ``` -- limb 采用 32 位小端序。 +- 采用 30 位 digit 的小端序表示。两个 30 位 digit 的乘积小于 2^60,加上进位后仍小于 2^62,因此运行时自身的 limb 运算保持在 Core 的立即数范围内。 +- 吸收 GMP 的规范化表示、schoolbook 乘加、Knuth 除法和小整数快路径;不链接 GMP,也不复制 GMP 源码。 - 零的 `sign = 0`、`len = 0`。 -- 非零对象去掉高位零 limb。 +- 非零对象去掉高位零 digit。 - 对象不可变,运算分配新对象。 - 大整数对象从当前 arena 分配,arena 重置时统一回收。 @@ -102,10 +105,13 @@ offset 8: limbs u32[len] ## 运行时契约 +BigInt 运行时用 Core 写成 `src/runtime/bigint.cr`。实现参考 imath 的整数算法结构和 GMP 的若干优化技术,不链接 C 源码,也不引入 libc 或 C 编译器依赖。运行时使用 30 位 digit,limb 中间值保持在立即数范围内,避免在 limb 运算中递归进入 BigInt 路径。 + 运行时提供: ```text bi_from_word +bi_from_uword bi_from_dec bi_to_dec bi_add @@ -117,11 +123,14 @@ bi_cmp bi_neg bi_is_zero bi_to_word_checked +bi_to_uword_checked ``` -- 除 `bi_from_word` 和 `bi_to_word_checked` 外,输入和输出都是 tagged value。 -- `bi_from_word` 接收原始 64 位机器字,返回 tagged value。 -- `bi_to_word_checked` 接收 tagged value,返回原始 64 位机器字。 +- 除 `bi_from_word`、`bi_from_uword`、`bi_to_word_checked` 和 `bi_to_uword_checked` 外,输入和输出都是 tagged value。 +- `bi_from_word` 接收原始有符号 64 位机器字,返回 tagged value。 +- `bi_from_uword` 接收原始无符号 64 位机器字,返回 tagged value。 +- `bi_to_word_checked` 接收 tagged value,返回原始有符号 64 位机器字。 +- `bi_to_uword_checked` 接收 tagged value,返回原始无符号 64 位机器字。 - `bi_div` 和 `bi_mod` 使用向零截断,余数符号跟随被除数。 - 除数为零时进入统一的运行期错误入口。 - `bi_to_word_checked` 检查有符号 64 位范围;地址场景使用单独的无符号范围检查。 From 69bef69ef7d385dcc20f5115e032c56e8cf689f6 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:48:41 +0800 Subject: [PATCH 14/58] runtime: add pure Core 30-bit arbitrary precision integers --- src/runtime/bigint.cr | 586 +++++++++++++++++++++++++++++++++ tests/runtime/bigint_driver.cr | 60 ++++ tests/runtime/test_bigint.py | 43 +++ 3 files changed, 689 insertions(+) create mode 100644 src/runtime/bigint.cr create mode 100644 tests/runtime/bigint_driver.cr create mode 100644 tests/runtime/test_bigint.py diff --git a/src/runtime/bigint.cr b/src/runtime/bigint.cr new file mode 100644 index 00000000..b81dbc6a --- /dev/null +++ b/src/runtime/bigint.cr @@ -0,0 +1,586 @@ +// === bigint.cr === +// Arbitrary precision integers for the Core runtime. +// +// Representation: +// tagged value low bit 0: mathematical value times 2 +// tagged value low bit 1: pointer to a BigInt object plus 1 +// +// BigInt object: +// offset 0: sign (-1, 0, 1) +// offset 8: digit count +// offset 16: base 2^30 digits, little endian +// +// The 30-bit digit base keeps a digit product below 2^60. Adding a carry and +// an accumulator stays below 2^62, so the runtime's own limb arithmetic does +// not fall back into the BigInt path. +// +// Algorithms follow the imath integer algorithms (MIT), with normalization, +// schoolbook multiplication, and binary long division. GMP is used only as an +// optimization reference; no GMP source is copied. + +BI_SIGN : int = 0 +BI_LEN : int = 8 +BI_DIGITS : int = 16 +BI_BASE : int = 1073741824 // 2^30 +BI_BASE2 : int = 1152921504606846976 // 2^60 +BI_IMM_MAX : int = 4611686018427387903 +BI_IMM_MIN : int = -4611686018427387904 + +g_bi_error : int, mut +g_bi_rem : int, mut + +fn bi_is_big(v: int) -> int: + if v % 2 != 0: + return 1 + return 0 + +fn bi_imm(v: int) -> int: + return v / 2 + +fn bi_tag(v: int) -> int: + return v * 2 + +fn bi_big_ptr(v: int) -> string: + p : ., mut = "" + unsafe: + p = @str_of(v - 1) + return p + +fn bi_tag_ptr(p: string) -> int: + q : ., mut = 0 + unsafe: + q = @ptr_of(p) + return q + 1 + +fn bi_new(n: int) -> string: + return alloc(BI_DIGITS + n * 8) + +fn bi_sign_of(p: string) -> int: + return r64(p, BI_SIGN) + +fn bi_len_of(p: string) -> int: + return r64(p, BI_LEN) + +fn bi_digit(p: string, i: int) -> int: + return r64(p, BI_DIGITS + i * 8) + +fn bi_set_sign(p: string, s: int): + w64(p, BI_SIGN, s) + +fn bi_set_len(p: string, n: int): + w64(p, BI_LEN, n) + +fn bi_set_digit(p: string, i: int, d: int): + w64(p, BI_DIGITS + i * 8, d) + +fn bi_normalize(p: string) -> string: + n := bi_len_of(p) + loop: + if n <= 0: + break + if bi_digit(p, n - 1) != 0: + break + n = n - 1 + bi_set_len(p, n) + if n == 0: + bi_set_sign(p, 0) + return p + +fn bi_from_imm(x: int) -> string: + p := bi_new(3) + if x == 0: + return p + v : ., mut = x + s : ., mut = 1 + if v < 0: + s = -1 + v = -v + i : ., mut = 0 + loop: + if v == 0: + break + bi_set_digit(p, i, v % BI_BASE) + v = v / BI_BASE + i = i + 1 + bi_set_sign(p, s) + bi_set_len(p, i) + return p + +fn bi_as_big(v: int) -> string: + if bi_is_big(v) != 0: + return bi_big_ptr(v) + return bi_from_imm(bi_imm(v)) + +fn bi_result(p: string) -> int: + bi_normalize(p) + n := bi_len_of(p) + if n == 0: + return 0 + if n > 3: + return bi_tag_ptr(p) + d0 := bi_digit(p, 0) + d1 : ., mut = 0 + d2 : ., mut = 0 + if n > 1: + d1 = bi_digit(p, 1) + if n > 2: + d2 = bi_digit(p, 2) + if d2 > 3: + return bi_tag_ptr(p) + x := d0 + d1 * BI_BASE + d2 * BI_BASE2 + if bi_sign_of(p) < 0: + x = -x + if x < BI_IMM_MIN || x > BI_IMM_MAX: + return bi_tag_ptr(p) + return bi_tag(x) + +fn bi_copy(p: string) -> string: + n := bi_len_of(p) + q := bi_new(n) + bi_set_sign(q, bi_sign_of(p)) + bi_set_len(q, n) + i : ., mut = 0 + loop: + if i >= n: + break + bi_set_digit(q, i, bi_digit(p, i)) + i = i + 1 + return q + +fn bi_cmp_abs(a: string, b: string) -> int: + la := bi_len_of(a) + lb := bi_len_of(b) + if la < lb: + return -1 + if la > lb: + return 1 + i : ., mut = la + loop: + if i <= 0: + break + i = i - 1 + da := bi_digit(a, i) + db := bi_digit(b, i) + if da < db: + return -1 + if da > db: + return 1 + return 0 + +fn bi_cmp(a: int, b: int) -> int: + pa := bi_as_big(a) + pb := bi_as_big(b) + sa := bi_sign_of(pa) + sb := bi_sign_of(pb) + if sa < sb: + return -1 + if sa > sb: + return 1 + if sa == 0: + return 0 + c := bi_cmp_abs(pa, pb) + if sa < 0: + return -c + return c + +fn bi_add_abs(a: string, b: string) -> string: + la := bi_len_of(a) + lb := bi_len_of(b) + n : ., mut = la + if lb > n: + n = lb + r := bi_new(n + 1) + carry : ., mut = 0 + i : ., mut = 0 + loop: + if i >= n: + break + da : ., mut = 0 + db : ., mut = 0 + if i < la: + da = bi_digit(a, i) + if i < lb: + db = bi_digit(b, i) + s := da + db + carry + bi_set_digit(r, i, s % BI_BASE) + carry = s / BI_BASE + i = i + 1 + if carry != 0: + bi_set_digit(r, n, carry) + n = n + 1 + bi_set_len(r, n) + bi_set_sign(r, 1) + return bi_normalize(r) + +fn bi_sub_abs(a: string, b: string) -> string: + la := bi_len_of(a) + lb := bi_len_of(b) + r := bi_new(la) + borrow : ., mut = 0 + i : ., mut = 0 + loop: + if i >= la: + break + da := bi_digit(a, i) + db : ., mut = 0 + if i < lb: + db = bi_digit(b, i) + d := da - db - borrow + if d < 0: + d = d + BI_BASE + borrow = 1 + else: + borrow = 0 + bi_set_digit(r, i, d) + i = i + 1 + bi_set_len(r, la) + bi_set_sign(r, 1) + return bi_normalize(r) + +fn bi_add(a: int, b: int) -> int: + if a == 0: + return b + if b == 0: + return a + pa := bi_as_big(a) + pb := bi_as_big(b) + sa := bi_sign_of(pa) + sb := bi_sign_of(pb) + if sa == sb: + r := bi_add_abs(pa, pb) + bi_set_sign(r, sa) + return bi_result(r) + c := bi_cmp_abs(pa, pb) + if c == 0: + return 0 + if c > 0: + r := bi_sub_abs(pa, pb) + bi_set_sign(r, sa) + return bi_result(r) + r := bi_sub_abs(pb, pa) + bi_set_sign(r, sb) + return bi_result(r) + +fn bi_neg(a: int) -> int: + if a == 0: + return 0 + if bi_is_big(a) == 0: + return bi_tag(-bi_imm(a)) + p := bi_big_ptr(a) + q := bi_copy(p) + bi_set_sign(q, -bi_sign_of(p)) + return bi_result(q) + +fn bi_sub(a: int, b: int) -> int: + return bi_add(a, bi_neg(b)) + +fn bi_mul(a: int, b: int) -> int: + if a == 0 || b == 0: + return 0 + pa := bi_as_big(a) + pb := bi_as_big(b) + la := bi_len_of(pa) + lb := bi_len_of(pb) + r := bi_new(la + lb) + i : ., mut = 0 + loop: + if i >= la: + break + carry : ., mut = 0 + ai := bi_digit(pa, i) + j : ., mut = 0 + loop: + if j >= lb: + break + cur := bi_digit(r, i + j) + ai * bi_digit(pb, j) + carry + bi_set_digit(r, i + j, cur % BI_BASE) + carry = cur / BI_BASE + j = j + 1 + k : ., mut = i + lb + loop: + if carry == 0: + break + cur := bi_digit(r, k) + carry + bi_set_digit(r, k, cur % BI_BASE) + carry = cur / BI_BASE + k = k + 1 + i = i + 1 + bi_set_len(r, la + lb) + s : ., mut = 1 + if bi_sign_of(pa) != bi_sign_of(pb): + s = -1 + bi_set_sign(r, s) + return bi_result(r) + +fn bi_is_zero(a: int) -> int: + if a == 0: + return 1 + if bi_is_big(a) == 0: + return 0 + p := bi_big_ptr(a) + if bi_len_of(p) == 0: + return 1 + return 0 + +fn bi_pow2(k: int) -> int: + v : ., mut = 1 + i : ., mut = 0 + loop: + if i >= k: + break + v = v * 2 + i = i + 1 + return v + +fn bi_bitlen_abs(p: string) -> int: + n := bi_len_of(p) + if n == 0: + return 0 + top := bi_digit(p, n - 1) + bits : ., mut = 0 + loop: + if top == 0: + break + top = top / 2 + bits = bits + 1 + return (n - 1) * 30 + bits + +fn bi_bit(p: string, pos: int) -> int: + di := pos / 30 + if di >= bi_len_of(p): + return 0 + return (bi_digit(p, di) / bi_pow2(pos % 30)) % 2 + +fn bi_set_bit(p: string, pos: int): + di := pos / 30 + if di >= bi_len_of(p): + return + d := bi_digit(p, di) + bi_pow2(pos % 30) + bi_set_digit(p, di, d) + +fn bi_shl1(p: string) -> string: + n := bi_len_of(p) + r := bi_new(n + 1) + carry : ., mut = 0 + i : ., mut = 0 + loop: + if i >= n: + break + v := bi_digit(p, i) * 2 + carry + bi_set_digit(r, i, v % BI_BASE) + carry = v / BI_BASE + i = i + 1 + bi_set_len(r, n) + if carry != 0: + bi_set_digit(r, n, carry) + bi_set_len(r, n + 1) + bi_set_sign(r, 1) + return bi_normalize(r) + +fn bi_add_small(a: int, b: int) -> int: + return bi_add(a, bi_tag(b)) + +fn bi_mul_small(a: int, m: int) -> int: + if a == 0 || m == 0: + return 0 + if bi_is_big(a) == 0: + x := bi_imm(a) + if m > 0 && x >= BI_IMM_MIN / m && x <= BI_IMM_MAX / m: + return bi_tag(x * m) + if m < 0 && x <= BI_IMM_MIN / m && x >= BI_IMM_MAX / m: + return bi_tag(x * m) + p := bi_as_big(a) + n := bi_len_of(p) + r := bi_new(n + 1) + carry : ., mut = 0 + i : ., mut = 0 + loop: + if i >= n: + break + v := bi_digit(p, i) * m + carry + bi_set_digit(r, i, v % BI_BASE) + carry = v / BI_BASE + i = i + 1 + bi_set_len(r, n) + if carry != 0: + bi_set_digit(r, n, carry) + bi_set_len(r, n + 1) + bi_set_sign(r, bi_sign_of(p)) + return bi_result(r) + +fn bi_div_small(a: int, d: int) -> int: + if d == 0: + g_bi_error = 1 + g_bi_rem = 0 + return 0 + if bi_is_big(a) == 0: + x := bi_imm(a) + g_bi_rem = x % d + return bi_tag(x / d) + p := bi_as_big(a) + n := bi_len_of(p) + r := bi_new(n) + rem : ., mut = 0 + i : ., mut = n + loop: + if i <= 0: + break + i = i - 1 + cur := rem * BI_BASE + bi_digit(p, i) + bi_set_digit(r, i, cur / d) + rem = cur % d + bi_set_len(r, n) + bi_set_sign(r, bi_sign_of(p)) + g_bi_rem = rem + return bi_result(r) + +fn bi_from_dec(s: string) -> int: + n := str_len(s) + i : ., mut = 0 + neg : ., mut = 0 + if n > 0 && load8(s, 0) == 45: + neg = 1 + i = 1 + r : ., mut = 0 + loop: + if i >= n: + break + c := load8(s, i) + if c < 48 || c > 57: + break + r = bi_mul_small(r, 10) + r = bi_add_small(r, c - 48) + i = i + 1 + if neg != 0: + r = bi_neg(r) + return r + +fn bi_to_dec(a: int) -> string: + if a == 0: + z := alloc(2) + store8(z, 0, 48) + return z + neg : ., mut = 0 + p : ., mut = bi_as_big(a) + if bi_sign_of(p) < 0: + neg = 1 + p = bi_as_big(bi_neg(a)) + n := bi_len_of(p) + cap := n * 10 + 4 + tmp := alloc(cap) + pos : ., mut = cap - 1 + store8(tmp, pos, 0) + cur : ., mut = bi_result(p) + loop: + if cur == 0: + break + q := bi_div_small(cur, 10) + pos = pos - 1 + store8(tmp, pos, g_bi_rem + 48) + cur = q + if neg != 0: + pos = pos - 1 + store8(tmp, pos, 45) + m := cap - 1 - pos + out := alloc(m + 1) + j : ., mut = 0 + loop: + if j >= m: + break + store8(out, j, load8(tmp, pos + j)) + j = j + 1 + return out + +fn bi_divmod(a: int, b: int) -> int: + if b == 0: + g_bi_error = 1 + return 0 + if a == 0: + g_bi_rem = 0 + return 0 + pa := bi_as_big(a) + pb := bi_as_big(b) + c := bi_cmp_abs(pa, pb) + if c < 0: + g_bi_rem = a + return 0 + sa := bi_sign_of(pa) + sb := bi_sign_of(pb) + if c == 0: + q : ., mut = 1 + if sa != sb: + q = -1 + g_bi_rem = 0 + return bi_tag(q) + n := bi_len_of(pa) + q := bi_new(n + 1) + bi_set_len(q, n + 1) + rem_p := bi_from_imm(0) + bits := bi_bitlen_abs(pa) + pos : ., mut = bits + loop: + if pos <= 0: + break + pos = pos - 1 + rem_p = bi_shl1(rem_p) + if bi_bit(pa, pos) != 0: + rem_p = bi_as_big(bi_add_small(bi_result(rem_p), 1)) + if bi_cmp_abs(rem_p, pb) >= 0: + rem_p = bi_sub_abs(rem_p, pb) + bi_set_bit(q, pos) + bi_set_len(q, n + 1) + bi_set_sign(q, sa * sb) + rq := bi_result(q) + bi_set_sign(rem_p, sa) + g_bi_rem = bi_result(rem_p) + return rq + +fn bi_div(a: int, b: int) -> int: + q := bi_divmod(a, b) + return q + +fn bi_mod(a: int, b: int) -> int: + q := bi_divmod(a, b) + return g_bi_rem + +fn bi_from_word(raw: int) -> int: + if raw >= BI_IMM_MIN && raw <= BI_IMM_MAX: + return bi_tag(raw) + return bi_result(bi_from_imm(raw)) + +fn bi_from_uword(raw: int) -> int: + if raw >= 0 && raw <= BI_IMM_MAX: + return bi_tag(raw) + return bi_result(bi_from_imm(raw)) + +fn bi_to_word_checked(a: int) -> int: + g_bi_error = 0 + p := bi_as_big(a) + n := bi_len_of(p) + if n > 3: + g_bi_error = 1 + return 0 + d0 := bi_digit(p, 0) + d1 : ., mut = 0 + d2 : ., mut = 0 + if n > 1: + d1 = bi_digit(p, 1) + if n > 2: + d2 = bi_digit(p, 2) + if d2 > 3: + g_bi_error = 1 + return 0 + x := d0 + d1 * BI_BASE + d2 * BI_BASE2 + if bi_sign_of(p) < 0: + x = -x + return x + +fn bi_to_uword_checked(a: int) -> int: + x := bi_to_word_checked(a) + if g_bi_error != 0: + return 0 + if x < 0: + g_bi_error = 1 + return 0 + return x + diff --git a/tests/runtime/bigint_driver.cr b/tests/runtime/bigint_driver.cr new file mode 100644 index 00000000..3138e65f --- /dev/null +++ b/tests/runtime/bigint_driver.cr @@ -0,0 +1,60 @@ +import bigint + +fn expect_cmp(v: int, text: string, code: int) -> int: + if bi_cmp(v, bi_from_dec(text)) != 0: + return code + return 0 + +fn expect_dec(v: int, text: string, code: int) -> int: + if str_eq(bi_to_dec(v), text) == 0: + return code + return 0 + +fn main() -> int: + z := bi_from_dec("0") + if bi_is_zero(z) == 0: + return 1 + if expect_cmp(bi_add(bi_from_dec("1"), bi_from_dec("2")), "3", 2) != 0: + return 2 + if expect_cmp(bi_add(bi_from_dec("-1"), bi_from_dec("2")), "1", 3) != 0: + return 3 + if expect_cmp(bi_sub(bi_from_dec("1"), bi_from_dec("2")), "-1", 4) != 0: + return 4 + if expect_cmp(bi_mul(bi_from_dec("123456789"), bi_from_dec("987654321")), "121932631112635269", 5) != 0: + return 5 + big := bi_from_dec("1267650600228229401496703205376") + if expect_cmp(bi_add(big, bi_from_dec("1")), "1267650600228229401496703205377", 6) != 0: + return 6 + if expect_cmp(bi_mul(bi_from_dec("1000000000000000"), bi_from_dec("1000000000000000")), "1000000000000000000000000000000", 7) != 0: + return 7 + if expect_cmp(bi_add(bi_from_dec("4611686018427387904"), bi_from_dec("1")), "4611686018427387905", 15) != 0: + return 15 + if expect_cmp(bi_add(bi_from_dec("-4611686018427387905"), bi_from_dec("1")), "-4611686018427387904", 16) != 0: + return 16 + if expect_cmp(bi_div(bi_from_dec("1267650600228229401496703205376"), bi_from_dec("3")), "422550200076076467165567735125", 17) != 0: + return 17 + if expect_cmp(bi_mod(bi_from_dec("1267650600228229401496703205376"), bi_from_dec("3")), "1", 18) != 0: + return 18 + if expect_cmp(bi_div(bi_from_dec("-1267650600228229401496703205376"), bi_from_dec("7")), "-181092942889747057356671886482", 19) != 0: + return 19 + if expect_cmp(bi_mod(bi_from_dec("-1267650600228229401496703205376"), bi_from_dec("7")), "-2", 20) != 0: + return 20 + if bi_to_word_checked(bi_from_dec("42")) != 42: + return 21 + if expect_cmp(bi_mul(bi_from_dec("-123456789"), bi_from_dec("987654321")), "-121932631112635269", 22) != 0: + return 22 + if expect_cmp(bi_div(bi_from_dec("100"), bi_from_dec("7")), "14", 8) != 0: + return 8 + if expect_cmp(bi_mod(bi_from_dec("100"), bi_from_dec("7")), "2", 9) != 0: + return 9 + if expect_cmp(bi_div(bi_from_dec("-7"), bi_from_dec("2")), "-3", 10) != 0: + return 10 + if expect_cmp(bi_mod(bi_from_dec("-7"), bi_from_dec("2")), "-1", 11) != 0: + return 11 + if bi_cmp(big, bi_from_dec("1267650600228229401496703205375")) <= 0: + return 12 + if expect_dec(big, "1267650600228229401496703205376", 13) != 0: + return 13 + if expect_dec(bi_neg(big), "-1267650600228229401496703205376", 14) != 0: + return 14 + return 0 diff --git a/tests/runtime/test_bigint.py b/tests/runtime/test_bigint.py new file mode 100644 index 00000000..0a94c65c --- /dev/null +++ b/tests/runtime/test_bigint.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +"""BigInt runtime smoke test. + +Builds tests/runtime/bigint_driver.cr with the existing corec and checks that +the driver exits with 0. The driver covers tagged immediates, object values, +decimal conversion, add/sub/mul/div/mod, negative values, and 2^100. +""" + +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +DRIVER = BASE / "tests" / "runtime" / "bigint_driver.cr" + + +def main() -> int: + if not COREC.exists(): + print(f"missing {COREC}; run build_selfhost_native.py first") + return 1 + with tempfile.TemporaryDirectory(prefix="core-bigint-") as td: + out = Path(td) / "bigint_driver" + build = subprocess.run( + [str(COREC), "build", str(DRIVER), "-o", str(out), "--static", "-O", "0"], + cwd=str(BASE), + capture_output=True, + text=True, + ) + if build.returncode != 0: + print(build.stdout) + print(build.stderr) + return 1 + run = subprocess.run([str(out)], cwd=str(BASE)) + if run.returncode != 0: + print(f"driver exit {run.returncode}") + return 1 + print("PASS bigint runtime") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 13c76c37945cafa5f499b5a05789a3c8059259e2 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:51:47 +0800 Subject: [PATCH 15/58] docs: target CCR v9.1 for bigint format changes --- docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md | 4 ++-- .../specs/2026-10-23-arbitrary-precision-int-design.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index 0685efd9..a1b428d8 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -169,7 +169,7 @@ python3 tests/bootstrap/test_bigint_pipeline.py - `T_BIGINT` token,词素是规范十进制字符串。 - `EXPR_BIGINT`,`a` 是字符串下标,`type_val=TY_INT`。 - `IR_CONST_BIG = 34`,`d=目标变量`,`s1=字符串下标`,`tk=TI_INT`。 -- `CCR_VERSION = 10`。 +- `.ccr` 版本 v9.1,编码为 `(9 << 16) | 1`。 - `CIR_CACHE_VER = 23`。 ### 步骤 @@ -348,7 +348,7 @@ python3 tests/selfhost/test_bigint_interp.py - 原先测试 2-limb 快慢路径的测试改为测试 tagged 立即数/装箱路径。 - `test_dex_type.py` 的大十进制字面量溢出测试改为成功用例。 - `test_lexer_parity.py` 的字面量溢出预期同步修改。 -4. 更新 `.ccr` v10 和 `.cir` v23 的测试。 +4. 更新 `.ccr` v9.1 和 `.cir` v23 的测试。 5. 重新采集 canary 值,记录每个值变化的原因。 6. 全量自举并比较阶段产物。 diff --git a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md index 23853d33..b0b56500 100644 --- a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md +++ b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md @@ -66,7 +66,7 @@ - 超出有符号 64 位的整数使用 `IR_CONST_BIG`,`s1` 是规范十进制字符串的 intern 下标。 - 前端可以在 64 位处选择存储形式,这个选择只影响载体编码。它不改变值,不产生溢出诊断,也不决定后端的立即数阈值。 - `.ccr` 的 NOD 记录大小不变。`IR_CONST_BIG` 复用 STR 段,不新增段。 -- `CCR_VERSION` 从 9 提升到 10,旧 corearch 拒绝新文件。 +- `.ccr` 版本为 v9.1。版本字段编码为 `(9 << 16) | 1`,即 `0x00090001`。loader 只接受 major 9、minor 1;旧 v9 的整数值 9 被拒绝。 - `.cir` 快照保存 `IR_CONST_BIG` 的 opcode 和字符串下标,`CIR_CACHE_VER` 从 22 提升到 23。 `dex` 的 binary64 位模式不是数学整数,继续使用 `IR_CONST` 的 64 位载荷。编译器内部的浮点字面量转换通过原始字边界处理,不使用 `int` 的数学值。 From 74de04cd88133f8ad8c542deafd440ede2aeba37 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:57:25 +0800 Subject: [PATCH 16/58] bootstrap: accept arbitrary precision integer literals --- bootstrap/corec/frontend/lexer.py | 2 - tests/bootstrap/test_bigint_frontend.py | 69 +++++++++++++++++++++++++ tests/bootstrap/test_pipeline.py | 2 +- 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/bootstrap/test_bigint_frontend.py diff --git a/bootstrap/corec/frontend/lexer.py b/bootstrap/corec/frontend/lexer.py index 160ee95d..06b2f61f 100644 --- a/bootstrap/corec/frontend/lexer.py +++ b/bootstrap/corec/frontend/lexer.py @@ -221,8 +221,6 @@ def read_number(self, first: str) -> Token: # 不一致处理——两编译器差异 = 各自投影形状差异,非语义分歧。 if is_prefixed: val = int(n[2:], base) - if val > 9223372036854775807: - self.error("integer literal overflow") return Token(TokenType.INT_LIT, str(val), start_line, start_col) if is_float: return Token(TokenType.FLOAT_LIT, n, start_line, start_col) diff --git a/tests/bootstrap/test_bigint_frontend.py b/tests/bootstrap/test_bigint_frontend.py new file mode 100644 index 00000000..98d6b274 --- /dev/null +++ b/tests/bootstrap/test_bigint_frontend.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Bootstrap frontend support for arbitrary precision integer literals.""" + +import sys + +sys.path.insert(0, "bootstrap") + +from corec.frontend.desugar import MatchDesugarer +from corec.frontend.ir_gen import IRGen +from corec.frontend.lexer import Lexer +from corec.frontend.name_resolver import NameResolver +from corec.frontend.parser import Parser +from corec.frontend.type_checker import TypeChecker +from corec.ir.coreir import ConstInstr +from corec.syntax.tokens import TokenType + + +def compile_and_run(src: str): + tokens = Lexer(src).tokenize() + ast = Parser(tokens).parse_compilation_unit() + resolver = NameResolver() + resolver.resolve(ast) + ast = MatchDesugarer(resolver.symtab).desugar(ast) + checker = TypeChecker(resolver.symtab) + checker.check(ast) + if resolver.errors or checker.errors: + raise AssertionError(resolver.errors + checker.errors) + mod = IRGen(resolver.symtab).gen_module(ast) + consts = [ + instr + for fn in mod.functions + for block in fn.blocks + for instr in block.instrs + if isinstance(instr, ConstInstr) and instr.type == "int" + ] + return mod, consts + + +def main() -> int: + cases = [ + ("0xFFFFFFFFFFFFFFFF", 2**64 - 1), + ("0x10000000000000000", 2**64), + ("0o2000000000000000000000", 2**64), + ("0b1" + "0" * 64, 2**64), + ("9223372036854775808", 2**63), + ] + for literal, expected in cases: + tokens = Lexer(literal).tokenize() + int_tokens = [t for t in tokens if t.type == TokenType.INT_LIT] + if not int_tokens: + print(f"FAIL lexer did not produce INT_LIT for {literal}") + return 1 + if int(int_tokens[0].lexeme) != expected: + print(f"FAIL lexer value {literal}: {int_tokens[0].lexeme}") + return 1 + + src = f"fn main() -> int:\n return {literal}\n" + mod, consts = compile_and_run(src) + values = [instr.value for instr in consts] + if expected not in values: + print(f"FAIL IR constant {literal}: {values}") + return 1 + + print("PASS bigint frontend") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/bootstrap/test_pipeline.py b/tests/bootstrap/test_pipeline.py index 04d364de..ac176424 100644 --- a/tests/bootstrap/test_pipeline.py +++ b/tests/bootstrap/test_pipeline.py @@ -388,7 +388,7 @@ def test_raw(name, src, expected): def check_integer_literals(): tokens = Lexer('0x1f 0o17 0b1010').tokenize() - values = [int(t.lexeme) for t in tokens[:-1]] + values = [int(t.lexeme) for t in tokens if t.type == TokenType.INT_LIT] if values != [31, 15, 10]: print(f'[FAIL] integer literal forms: got {values}') return False From 1d28bf4b513352d6b008758fd4e6a8e9cff304ee Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 10:59:25 +0800 Subject: [PATCH 17/58] bootstrap: add tagged bigint backend pipeline --- bootstrap/corec/backend/x86_64_stack_asm.py | 235 +++++++++++++++++++- bootstrap/corec/frontend/ir_gen.py | 35 ++- bootstrap/corec/frontend/name_resolver.py | 3 + bootstrap/corec/frontend/type_checker.py | 15 +- src/runtime/bigint.cr | 4 +- tests/bootstrap/test_bigint_pipeline.py | 81 +++++++ 6 files changed, 367 insertions(+), 6 deletions(-) create mode 100644 tests/bootstrap/test_bigint_pipeline.py diff --git a/bootstrap/corec/backend/x86_64_stack_asm.py b/bootstrap/corec/backend/x86_64_stack_asm.py index 7447e3a6..7f12b832 100644 --- a/bootstrap/corec/backend/x86_64_stack_asm.py +++ b/bootstrap/corec/backend/x86_64_stack_asm.py @@ -17,15 +17,36 @@ class X86_64StackAsmGen: - def __init__(self, module: Module): + def __init__(self, module: Module, bigint_mode: bool = False, runtime_only: bool = False): self.module = module + self.bigint_mode = bigint_mode + self.runtime_only = runtime_only self.lines = [] self.str_labels = [] # [(label_id, string_value)] self.str_map = {} # string_value -> label_id + self.big_labels = [] # [(label_id, int_value)] + self.big_map = {} # int_value -> label_id + self._uid = 0 # Per-function state self.var_offsets = {} # var_id -> stack offset (negative from rbp) self.stack_size = 0 + def _next_uid(self) -> int: + self._uid += 1 + return self._uid + + def _is_int_var(self, var: IRVar) -> bool: + return isinstance(var.type, BaseType) and var.type.name == 'int' + + def _big_label(self, value: int) -> str: + if value in self.big_map: + return f".LBIG{self.big_map[value]}" + idx = len(self.big_labels) + self.big_labels.append((idx, value)) + self.big_map[value] = idx + return f".LBIG{idx}" + + def _elem_size(self, typ) -> int: """Compute the element size (in bytes) for an array element type.""" if isinstance(typ, BaseType): @@ -140,6 +161,32 @@ def gen_constant(self, instr: ConstInstr): self.emit(f" lea r10, .LC{lid}[rip]") self._clobber_cache() self.emit(f" mov {ref}, r10") + elif instr.type == 'raw_int': + raw = int(instr.value) + if raw >= -(2**31) and raw < 2**31: + self.emit(f" mov qword ptr {ref}, {raw}") + else: + self.emit(f" mov r10, {raw}") + self._clobber_cache() + self.emit(f" mov {ref}, r10") + elif self.bigint_mode and (instr.type == 'int' or instr.type is None): + iv = int(instr.value) + imm_min = -(2**62) + imm_max = 2**62 - 1 + if iv >= imm_min and iv <= imm_max: + raw = iv * 2 + if raw >= -(2**31) and raw < 2**31: + self.emit(f" mov qword ptr {ref}, {raw}") + else: + self.emit(f" mov r10, {raw}") + self._clobber_cache() + self.emit(f" mov {ref}, r10") + else: + lbl = self._big_label(iv) + self.emit(f" lea r10, {lbl}[rip]") + self.emit(" add r10, 1") + self._clobber_cache() + self.emit(f" mov {ref}, r10") else: val = instr.value if isinstance(val, float): @@ -157,7 +204,107 @@ def _alu_clobber(self, asm: str): self.emit(asm) self._clobber_r10() + def _gen_int_binary(self, instr: BinaryInstr): + """Tagged integer fast path with BigInt fallback.""" + op = instr.op + uid = self._next_uid() + big = f".Lint_big_{uid}" + done = f".Lint_done_{uid}" + self.load_to_r10(instr.left) + self.load_to_r11(instr.right) + self.emit(" test r10, 1") + self.emit(f" jnz {big}") + self.emit(" test r11, 1") + self.emit(f" jnz {big}") + self.emit(" sar r10, 1") + self.emit(" sar r11, 1") + imm_min = -(2**62) + imm_max = 2**62 - 1 + if op in ('+', '-', '*'): + if op == '+': + self.emit(" add r10, r11") + elif op == '-': + self.emit(" sub r10, r11") + else: + self.emit(" imul r10, r11") + self.emit(f" jo {big}") + self.emit(f" mov r11, {imm_min}") + self.emit(" cmp r10, r11") + self.emit(f" jl {big}") + self.emit(f" mov r11, {imm_max}") + self.emit(" cmp r10, r11") + self.emit(f" jg {big}") + self.emit(" add r10, r10") + self.store_from_r10(instr.dest) + self.emit(f" jmp {done}") + elif op in ('/', '%'): + self.emit(" test r11, r11") + self.emit(f" jz {big}") + self.emit(f" mov rax, {imm_min}") + self.emit(" cmp r10, rax") + self.emit(f" jne .Lint_div_ok_{uid}") + self.emit(" cmp r11, -1") + self.emit(f" je {big}") + self.emit(f".Lint_div_ok_{uid}:") + self.emit(" mov rax, r10") + self.emit(" cqo") + self.emit(" idiv r11") + if op == '/': + self.emit(" mov r10, rax") + else: + self.emit(" mov r10, rdx") + self.emit(" add r10, r10") + self.store_from_r10(instr.dest) + self.emit(f" jmp {done}") + else: + cmp_op = { + '==': 'sete', '!=': 'setne', '<': 'setl', '>': 'setg', + '<=': 'setle', '>=': 'setge', + }[op] + self.emit(" cmp r10, r11") + self.emit(f" {cmp_op} al") + self.emit(" movzx r10, al") + self.store_from_r10(instr.dest) + self.emit(f" jmp {done}") + self.emit(f"{big}:") + self.emit(f" mov rdi, {self._ref(instr.left)}") + self.emit(f" mov rsi, {self._ref(instr.right)}") + if op in ('==', '!=', '<', '>', '<=', '>='): + self.emit(" call bi_cmp") + self._clobber_regs() + self.emit(" sar rax, 1") + self.emit(" cmp rax, 0") + cmp_op = { + '==': 'sete', '!=': 'setne', '<': 'setl', '>': 'setg', + '<=': 'setle', '>=': 'setge', + }[op] + self.emit(f" {cmp_op} al") + self.emit(" movzx r10, al") + else: + fn = {'+': 'bi_add', '-': 'bi_sub', '*': 'bi_mul', '/': 'bi_div', '%': 'bi_mod'}[op] + self.emit(f" call {fn}") + self._clobber_regs() + self.emit(" mov r10, rax") + self.store_from_r10(instr.dest) + self.emit(f"{done}:") + def gen_binary(self, instr: BinaryInstr): + if self.bigint_mode and instr.op in ('raw_add', 'raw_sub', 'raw_and'): + self.load_to_r10(instr.left) + self.load_to_r11(instr.right) + if instr.op == 'raw_add': + self.emit(" add r10, r11") + elif instr.op == 'raw_sub': + self.emit(" sub r10, r11") + else: + self.emit(" and r10, r11") + self.store_from_r10(instr.dest) + return + if (self.bigint_mode and + instr.op in ('+', '-', '*', '/', '%', '==', '!=', '<', '>', '<=', '>=') and + self._is_int_var(instr.left) and self._is_int_var(instr.right)): + self._gen_int_binary(instr) + return self.load_to_r10(instr.left) self.load_to_r11(instr.right) op = instr.op @@ -212,7 +359,34 @@ def gen_binary(self, instr: BinaryInstr): def gen_unary(self, instr: UnaryInstr): self.load_to_r10(instr.operand) if instr.op == '-': - self._alu_clobber(" neg r10") + if self.bigint_mode and self._is_int_var(instr.operand): + uid = self._next_uid() + big = f".Lneg_big_{uid}" + done = f".Lneg_done_{uid}" + self.emit(" test r10, 1") + self.emit(f" jnz {big}") + self.emit(" sar r10, 1") + imm_min = -(2**62) + imm_max = 2**62 - 1 + self.emit(f" mov r11, {imm_min}") + self.emit(" cmp r10, r11") + self.emit(f" je {big}") + self.emit(" neg r10") + self.emit(f" mov r11, {imm_max}") + self.emit(" cmp r10, r11") + self.emit(f" jg {big}") + self.emit(" add r10, r10") + self.store_from_r10(instr.dest) + self.emit(f" jmp {done}") + self.emit(f"{big}:") + self.emit(f" mov rdi, {self._ref(instr.operand)}") + self.emit(" call bi_neg") + self._clobber_regs() + self.emit(" mov r10, rax") + self.store_from_r10(instr.dest) + self.emit(f"{done}:") + else: + self._alu_clobber(" neg r10") elif instr.op == 'not' or instr.op == '!': self.emit(" cmp r10, 0") self._alu_clobber(" sete al\n movzx r10, al") @@ -230,13 +404,34 @@ def gen_call(self, instr: CallInstr): for i in reversed(range(6, len(instr.args))): self.emit(f" push {self._ref(instr.args[i])}") # ── Inline hot helper functions (avoids call overhead for millions of calls) ── + if instr.func == 'str_len': + self.emit(" mov rax, [rdi - 8]") + self.emit(" sub rax, 1") + if self.bigint_mode: + self.emit(" add rax, rax") + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + if instr.func == 'alloc': + if self.bigint_mode: + self.emit(" sar rdi, 1") + self.emit(" call alloc") + self._clobber_regs() + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") if instr.func == 'r64': # r64(buf, pos) → read 8 bytes + if self.bigint_mode: + self.emit(" sar rsi, 1") self.emit(" mov rax, [rdi + rsi]") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'w64': # w64(buf, pos, val) → write 8 bytes + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" sar rdx, 1") self.emit(" mov [rdi + rsi], rdx") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") elif instr.func == 'r32': @@ -288,11 +483,18 @@ def gen_call(self, instr: CallInstr): if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'load8': # load8(string_ptr, idx) → byte at ptr+idx (zero-extended) + if self.bigint_mode: + self.emit(" sar rsi, 1") self.emit(" movzx rax, BYTE PTR [rdi + rsi]") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov qword ptr {self._ref(instr.dest)}, rax") elif instr.func == 'store8': # store8(string_ptr, idx, val) → store low byte of val at ptr+idx + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" sar rdx, 1") self.emit(" mov BYTE PTR [rdi + rsi], dl") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov qword ptr {self._ref(instr.dest)}, 0") @@ -573,6 +775,8 @@ def generate(self) -> str: self.lines = [] self.str_labels = [] self.str_map = {} + self.big_labels = [] + self.big_map = {} self.emit(".intel_syntax noprefix") self.emit(".text") @@ -607,6 +811,24 @@ def generate(self) -> str: self.emit(f".LC{lid}: .asciz \"{escaped}\"") self.emit(".balign 8") + if self.big_labels: + self.emit(".section .rodata") + self.emit(".balign 8") + base = 2**30 + for lid, value in self.big_labels: + sign = 0 if value == 0 else (1 if value > 0 else -1) + mag = abs(value) + digits = [] + while mag: + digits.append(mag % base) + mag //= base + self.emit(f".LBIG{lid}:") + self.emit(f" .quad {sign}") + self.emit(f" .quad {len(digits)}") + for d in digits: + self.emit(f" .quad {d}") + self.emit(".balign 8") + # Global variables in .data section # Collect unique global IRVars from the module global_vars: dict[str, IRVar] = {} @@ -623,6 +845,13 @@ def generate(self) -> str: self.emit(f".globl {lbl}") cv = getattr(g, 'constant_value', None) if cv is not None: + if (self.bigint_mode and isinstance(g.type, BaseType) + and g.type.name == 'int'): + iv = int(cv) + imm_min = -(2**62) + imm_max = 2**62 - 1 + if iv >= imm_min and iv <= imm_max: + cv = iv * 2 self.emit(f"{lbl}: .quad {cv}") else: self.emit(f"{lbl}: .quad 0") @@ -630,6 +859,8 @@ def generate(self) -> str: # Global initialization function — allocate heap memory for global arrays # (g_tokens, g_ir_vars, etc.) so they're not NULL when code accesses them. # Always emitted because rt.s's _start unconditionally calls _init_globals. + if self.runtime_only: + return "\n".join(self.lines) global_arrays = [g for g in self.module.globals if g.kind == VarKind.GLOBAL and isinstance(g.type, ArrayType) and g.type.size] self.emit() diff --git a/bootstrap/corec/frontend/ir_gen.py b/bootstrap/corec/frontend/ir_gen.py index 69539611..af2ed12b 100644 --- a/bootstrap/corec/frontend/ir_gen.py +++ b/bootstrap/corec/frontend/ir_gen.py @@ -150,6 +150,18 @@ def gen_expr(self, expr): # 2(a) 视图内建 = **直通**(零代码生成):`@ptr_of(s)` / `@str_of(p)` 不改变那个 64 位字, # 只改变它在本语言里的看法 ⇒ 直接返回内层表达式的值本身(不发射指令、不建临时变量)。 # 未知名到此不该出现(type_checker 已 fail-closed;此面兜底为 0 以免静默崩溃)。 + if expr.name in ('raw_add', 'raw_sub', 'raw_and'): + args = [] + for arg in expr.args: + if isinstance(arg, Literal) and arg.kind == 'int': + v = self.new_temp() + self.add_instr(ConstInstr(int(arg.value), 'raw_int', v)) + args.append(v) + else: + args.append(self.gen_expr(arg)) + dest = self.new_temp() + self.add_instr(BinaryInstr(expr.name, args[0], args[1], dest)) + return dest if expr.name in ('ptr_of', 'str_of') and expr.args: return self.gen_expr(expr.args[0]) v = self.new_temp() @@ -186,6 +198,7 @@ def gen_literal(self, lit): if lit.kind == 'int': val = int(val) elif lit.kind == 'dex': val = Dex(int(val)) # 定点缩放整数(精确语义,Task 4) v = self.new_temp() + v.type = BaseType(lit.kind) self.add_instr(ConstInstr(val, lit.kind, v)) return v @@ -244,6 +257,10 @@ def gen_binary(self, binop): right_is_str = isinstance(right.type, BaseType) and right.type.name == 'string' if left_is_str or right_is_str: dest.type = BaseType('string') + if dest.type is None: + if (isinstance(left.type, BaseType) and left.type.name == 'int' and + isinstance(right.type, BaseType) and right.type.name == 'int'): + dest.type = BaseType('int') self.add_instr(BinaryInstr(binop.op, left, right, dest)) return dest @@ -265,9 +282,17 @@ def gen_unary(self, unop): return dest op = self.gen_expr(unop.operand) dest = self.new_temp() + if isinstance(op.type, BaseType): + dest.type = op.type self.add_instr(UnaryInstr(unop.op, op, dest)) return dest + def _call_result_type(self, name): + sym = self.symtab.lookup(name) + if sym and sym.type: + return sym.type + return None + def gen_call(self, call): if isinstance(call.func, FieldAccess): module_fn = None @@ -276,6 +301,7 @@ def gen_call(self, call): if module_fn is not None: args = [self.gen_expr(a) for a in call.args] dest = self.new_temp() + dest.type = module_fn.return_type self.add_instr(CallInstr(module_fn.name, args, dest)) return dest obj_expr = call.func.object @@ -304,12 +330,14 @@ def gen_call(self, call): obj = self.gen_expr(obj_expr) args = [obj] + [self.gen_expr(a) for a in call.args] dest = self.new_temp() + dest.type = self._call_result_type(full) self.add_instr(CallInstr(full, args, dest)) return dest else: func_name = self._get_func_name(call.func) args = [self.gen_expr(a) for a in call.args] dest = self.new_temp() + dest.type = self._call_result_type(func_name) self.add_instr(CallInstr(func_name, args, dest)) return dest @@ -686,7 +714,12 @@ def gen_let(self, let): if typ is None: typ = BaseType('int') else: - typ = BaseType('unit') + if val_ir is not None and isinstance(val_ir.type, BaseType): + typ = val_ir.type + else: + typ = BaseType('unit') + if typ is None and val_ir is not None and isinstance(val_ir.type, BaseType): + typ = val_ir.type if typ is None: typ = BaseType('unit') var_ir = IRVar(name, VarKind.LOCAL, typ) diff --git a/bootstrap/corec/frontend/name_resolver.py b/bootstrap/corec/frontend/name_resolver.py index 2b954302..f90678b8 100644 --- a/bootstrap/corec/frontend/name_resolver.py +++ b/bootstrap/corec/frontend/name_resolver.py @@ -16,6 +16,9 @@ def _declare_builtins(self): ('load8', BaseType('int')), ('store8', BaseType('int')), ('load64', BaseType('int')), + ('r64', BaseType('int')), + ('w64', BaseType('int')), + ('str_len', BaseType('int')), ('syscall3', BaseType('int')), ('syscall4', BaseType('int')), ('fiber_init', BaseType('int')), diff --git a/bootstrap/corec/frontend/type_checker.py b/bootstrap/corec/frontend/type_checker.py index 22ec67ba..49939d2c 100644 --- a/bootstrap/corec/frontend/type_checker.py +++ b/bootstrap/corec/frontend/type_checker.py @@ -121,6 +121,19 @@ def _infer_builtin(self, expr) -> Type: self.errors.append(f"@{expr.name} expects a {want} expression, got {at}") return BaseType('never') return result + if expr.name in ('raw_add', 'raw_sub', 'raw_and'): + if self.unsafe_depth == 0: + self.errors.append(f"@{expr.name} must appear inside an `unsafe` block") + return BaseType('never') + if len(expr.args) != 2: + self.errors.append(f"@{expr.name} expects exactly 2 arguments") + return BaseType('never') + for arg in expr.args: + at = self._infer_expr(arg) + if self._type_name(at) != 'int': + self.errors.append(f"@{expr.name} expects int arguments, got {at}") + return BaseType('never') + return BaseType('int') self.errors.append(f"unknown @ builtin: {expr.name}") return BaseType('never') @@ -571,7 +584,7 @@ def _infer_expr(self, expr: Expr) -> Type: def _infer_call(self, call: Call) -> Type: # Handle runtime functions (defined in rt.s, no decl node) rt_funcs = {'alloc', 'get_arg', 'syscall3', 'syscall4', 'fiber_init', 'fiber_switch', - 'load8', 'store8', + 'load8', 'store8', 'r64', 'w64', 'str_len', 'load_str_ptr', 'store_str_ptr', 'g_set_curg', 'g_get_curg', 'm_start_workers'} if isinstance(call.func, Ident) and call.func.name in rt_funcs: diff --git a/src/runtime/bigint.cr b/src/runtime/bigint.cr index b81dbc6a..0d184fac 100644 --- a/src/runtime/bigint.cr +++ b/src/runtime/bigint.cr @@ -180,8 +180,8 @@ fn bi_cmp(a: int, b: int) -> int: return 0 c := bi_cmp_abs(pa, pb) if sa < 0: - return -c - return c + return bi_tag(-c) + return bi_tag(c) fn bi_add_abs(a: string, b: string) -> string: la := bi_len_of(a) diff --git a/tests/bootstrap/test_bigint_pipeline.py b/tests/bootstrap/test_bigint_pipeline.py new file mode 100644 index 00000000..8ee07133 --- /dev/null +++ b/tests/bootstrap/test_bigint_pipeline.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Bootstrap BigInt pipeline: tagged user code plus a raw Core BigInt runtime.""" + +import subprocess +import sys +import tempfile +from pathlib import Path + +sys.path.insert(0, "bootstrap") + +from corec.backend.x86_64_stack_asm import X86_64StackAsmGen +from corec.frontend.desugar import MatchDesugarer +from corec.frontend.ir_gen import IRGen +from corec.frontend.lexer import Lexer +from corec.frontend.name_resolver import NameResolver +from corec.frontend.parser import Parser +from corec.frontend.type_checker import TypeChecker +from corec.ir.coreir import Module + +BASE = Path(__file__).resolve().parents[2] +RUNTIME = BASE / "src" / "runtime" / "bigint.cr" + +SRC = """fn main() -> int: + x := 9223372036854775808 + y := x + 1 + if y == 9223372036854775809: + return 0 + return 1 +""" + + +def compile_module(src: str) -> Module: + ast = Parser(Lexer(src).tokenize()).parse_compilation_unit() + resolver = NameResolver() + resolver.resolve(ast) + ast = MatchDesugarer(resolver.symtab).desugar(ast) + checker = TypeChecker(resolver.symtab) + checker.check(ast) + if resolver.errors or checker.errors: + raise AssertionError(resolver.errors + checker.errors) + return IRGen(resolver.symtab).gen_module(ast) + + +def build_object(module: Module, path: Path, **kwargs) -> None: + asm = X86_64StackAsmGen(module, **kwargs).generate() + asm_path = path.with_suffix(".s") + asm_path.write_text(asm, encoding="utf-8") + r = subprocess.run(["as", "-o", str(path), str(asm_path)], capture_output=True, text=True) + if r.returncode != 0: + raise RuntimeError(r.stderr) + + +def main() -> int: + raw_mod = compile_module(RUNTIME.read_text(encoding="utf-8")) + user_mod = compile_module(SRC) + with tempfile.TemporaryDirectory(prefix="core-bigint-pipeline-") as td: + d = Path(td) + raw_obj = d / "bigint.o" + user_obj = d / "p.o" + out = d / "p" + build_object(raw_mod, raw_obj, runtime_only=True) + build_object(user_mod, user_obj, bigint_mode=True) + r = subprocess.run( + ["ld", "--unresolved-symbols=ignore-all", "-o", str(out), + str(user_obj), str(raw_obj), str(BASE / "build" / "runtime.o")], + capture_output=True, + text=True, + ) + if r.returncode != 0: + print(r.stderr) + return 1 + run = subprocess.run([str(out)]) + if run.returncode != 0: + print(f"pipeline exit {run.returncode}") + return 1 + print("PASS bigint pipeline") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 091e5e295f88811388ea7dde169380efefc7f166 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 11:11:58 +0800 Subject: [PATCH 18/58] bootstrap: fix inline str_len and alloc fallthrough --- bootstrap/corec/backend/x86_64_stack_asm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bootstrap/corec/backend/x86_64_stack_asm.py b/bootstrap/corec/backend/x86_64_stack_asm.py index 7f12b832..a4b97901 100644 --- a/bootstrap/corec/backend/x86_64_stack_asm.py +++ b/bootstrap/corec/backend/x86_64_stack_asm.py @@ -411,6 +411,7 @@ def gen_call(self, instr: CallInstr): self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + return if instr.func == 'alloc': if self.bigint_mode: self.emit(" sar rdi, 1") @@ -418,6 +419,7 @@ def gen_call(self, instr: CallInstr): self._clobber_regs() if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + return if instr.func == 'r64': # r64(buf, pos) → read 8 bytes if self.bigint_mode: From d1d41e4017b1fa1fbd15976f2cd4809927cb3d54 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 11:13:19 +0800 Subject: [PATCH 19/58] bootstrap: complete tagged bigint codegen for user programs --- bootstrap/corec/backend/x86_64_stack_asm.py | 85 ++++++++++++++++++++- bootstrap/corec/frontend/ir_gen.py | 12 ++- src/compiler/checker.cr | 48 ++++++------ tests/bootstrap/test_bigint_pipeline.py | 6 +- 4 files changed, 119 insertions(+), 32 deletions(-) diff --git a/bootstrap/corec/backend/x86_64_stack_asm.py b/bootstrap/corec/backend/x86_64_stack_asm.py index a4b97901..a17a8f00 100644 --- a/bootstrap/corec/backend/x86_64_stack_asm.py +++ b/bootstrap/corec/backend/x86_64_stack_asm.py @@ -289,15 +289,20 @@ def _gen_int_binary(self, instr: BinaryInstr): self.emit(f"{done}:") def gen_binary(self, instr: BinaryInstr): - if self.bigint_mode and instr.op in ('raw_add', 'raw_sub', 'raw_and'): + if self.bigint_mode and instr.op in ('raw_add', 'raw_sub', 'raw_and', 'tag_word', 'untag_word'): self.load_to_r10(instr.left) - self.load_to_r11(instr.right) + if instr.op in ('raw_add', 'raw_sub', 'raw_and'): + self.load_to_r11(instr.right) if instr.op == 'raw_add': self.emit(" add r10, r11") elif instr.op == 'raw_sub': self.emit(" sub r10, r11") - else: + elif instr.op == 'raw_and': self.emit(" and r10, r11") + elif instr.op == 'tag_word': + self.emit(" add r10, r10") + else: + self.emit(" sar r10, 1") self.store_from_r10(instr.dest) return if (self.bigint_mode and @@ -312,6 +317,8 @@ def gen_binary(self, instr: BinaryInstr): if op in ('==', '!='): self.emit(f" mov rdi, r10"); self.emit(f" mov rsi, r11") self.emit(" call str_eq"); self._clobber_regs() + if self.bigint_mode: + self.emit(" sar rax, 1") if op == '==': self.emit(f" mov {self._ref(instr.dest)}, rax") else: self.emit(" cmp rax, 1") @@ -320,6 +327,8 @@ def gen_binary(self, instr: BinaryInstr): else: self.emit(f" mov rdi, r10"); self.emit(f" mov rsi, r11") self.emit(" call str_cmp"); self._clobber_regs() + if self.bigint_mode: + self.emit(" sar rax, 1") self.emit(" cmp rax, 0") asm = {"<":" setl al\n movzx r10, al",">":" setg al\n movzx r10, al","<=":" setle al\n movzx r10, al",">=":" setge al\n movzx r10, al"} if op in asm: self._alu_clobber(asm[op]) @@ -404,6 +413,14 @@ def gen_call(self, instr: CallInstr): for i in reversed(range(6, len(instr.args))): self.emit(f" push {self._ref(instr.args[i])}") # ── Inline hot helper functions (avoids call overhead for millions of calls) ── + if instr.func == 'get_arg': + if self.bigint_mode: + self.emit(" sar rdi, 1") + self.emit(" call get_arg") + self._clobber_regs() + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + return if instr.func == 'str_len': self.emit(" mov rax, [rdi - 8]") self.emit(" sub rax, 1") @@ -420,6 +437,28 @@ def gen_call(self, instr: CallInstr): if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") return + if instr.func == 'load64': + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" mov rax, [rdi + rsi]") + if self.bigint_mode: + self.emit(" add rax, rax") + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + return + if instr.func == 'load_str_ptr': + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" mov rax, [rdi + rsi]") + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") + return + if instr.func == 'store_str_ptr': + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" mov [rdi + rsi], rdx") + if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") + return if instr.func == 'r64': # r64(buf, pos) → read 8 bytes if self.bigint_mode: @@ -438,24 +477,40 @@ def gen_call(self, instr: CallInstr): if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") elif instr.func == 'r32': # r32(buf, pos) → read 4 bytes (zero-extended) + if self.bigint_mode: + self.emit(" sar rsi, 1") self.emit(" mov eax, [rdi + rsi]") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'w32': # w32(buf, pos, val) → write 4 bytes + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" sar rdx, 1") self.emit(" mov [rdi + rsi], edx") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") elif instr.func == 'bu8': # bu8(buf, pos) → read 1 byte (zero-extended) + if self.bigint_mode: + self.emit(" sar rsi, 1") self.emit(" movzx eax, BYTE PTR [rdi + rsi]") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'w8': # w8(buf, pos, val) → write 1 byte + if self.bigint_mode: + self.emit(" sar rsi, 1") + self.emit(" sar rdx, 1") self.emit(" mov BYTE PTR [rdi + rsi], dl") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") elif instr.func == '_dyncpy': # _dyncpy(src, nbytes, dst) → memcpy via rep movsb + if self.bigint_mode: + self.emit(" sar rsi, 1") self.emit(" mov rcx, rsi") # rcx = byte count self.emit(" mov rsi, rdi") # rsi = source self.emit(" mov rdi, rdx") # rdi = dest @@ -465,22 +520,34 @@ def gen_call(self, instr: CallInstr): elif instr.func == 'syscall3': # Inline syscall: after arg setup rdi=nr, rsi=arg1, rdx=arg2, rcx=arg3 # Need: rax=nr, rdi=arg1, rsi=arg2, rdx=arg3 + if self.bigint_mode: + for reg, arg in zip(['rdi', 'rsi', 'rdx', 'rcx'], instr.args[:4]): + if self._is_int_var(arg): + self.emit(f" sar {reg}, 1") self.emit(" mov rax, rdi") self.emit(" mov rdi, rsi") self.emit(" mov rsi, rdx") self.emit(" mov rdx, rcx") self.emit(" syscall") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'syscall4': # syscall4(num, a, b, c, d): rdi=nr, rsi=a, rdx=b, rcx=c, r8=d # Need: rax=nr, rdi=a, rsi=b, rdx=c, r10=d (x86-64: 4th syscall arg in r10) + if self.bigint_mode: + for reg, arg in zip(['rdi', 'rsi', 'rdx', 'rcx', 'r8'], instr.args[:5]): + if self._is_int_var(arg): + self.emit(f" sar {reg}, 1") self.emit(" mov rax, rdi") self.emit(" mov rdi, rsi") self.emit(" mov rsi, rdx") self.emit(" mov rdx, rcx") self.emit(" mov r10, r8") self.emit(" syscall") + if self.bigint_mode: + self.emit(" add rax, rax") if stack_args > 0: self.emit(f" add rsp, {stack_args * 8}") if instr.dest: self.emit(f" mov {self._ref(instr.dest)}, rax") elif instr.func == 'load8': @@ -560,6 +627,8 @@ def gen_load_index(self, instr: LoadIndexInstr): self.load_to_r10(instr.array) if self._is_string_var(instr.array): self.emit(f" movzx r11, byte ptr [r10 + {instr.index}]") + if self.bigint_mode: + self.emit(" add r11, r11") else: self.emit(f" mov r11, [r10 + {instr.index * 8}]") self.emit(f" mov {self._ref(instr.dest)}, r11") @@ -568,6 +637,8 @@ def gen_store_index(self, instr: StoreIndexInstr): self.load_to_r10(instr.array) self.emit(f" mov r11, {self._ref(instr.value)}") if self._is_string_var(instr.array): + if self.bigint_mode: + self.emit(" sar r11, 1") self.emit(f" mov [r10 + {instr.index}], r11b") else: self.emit(f" mov [r10 + {instr.index * 8}], r11") @@ -575,8 +646,12 @@ def gen_store_index(self, instr: StoreIndexInstr): def gen_load_index_var(self, instr: LoadIndexVarInstr): self.load_to_r10(instr.array) self.emit(f" mov r11, {self._ref(instr.index_var)}") + if self.bigint_mode: + self.emit(" sar r11, 1") if self._is_string_var(instr.array): self.emit(" movzx r12, byte ptr [r10 + r11]") + if self.bigint_mode: + self.emit(" add r12, r12") else: self.emit(" mov r12, [r10 + r11 * 8]") self.emit(f" mov {self._ref(instr.dest)}, r12") @@ -584,8 +659,12 @@ def gen_load_index_var(self, instr: LoadIndexVarInstr): def gen_store_index_var(self, instr: StoreIndexVarInstr): self.load_to_r10(instr.array) self.emit(f" mov r11, {self._ref(instr.index_var)}") + if self.bigint_mode: + self.emit(" sar r11, 1") self.emit(f" mov r12, {self._ref(instr.value)}") if self._is_string_var(instr.array): + if self.bigint_mode: + self.emit(" sar r12, 1") self.emit(" mov [r10 + r11], r12b") else: self.emit(" mov [r10 + r11 * 8], r12") diff --git a/bootstrap/corec/frontend/ir_gen.py b/bootstrap/corec/frontend/ir_gen.py index af2ed12b..7846d702 100644 --- a/bootstrap/corec/frontend/ir_gen.py +++ b/bootstrap/corec/frontend/ir_gen.py @@ -4,8 +4,9 @@ from corec.ir.symbol_table import SymbolTable, SymbolKind class IRGen: - def __init__(self, symtab=None): + def __init__(self, symtab=None, bigint_mode: bool = False): self.temp_counter = 0 + self.bigint_mode = bigint_mode self.mod = Module("main") self.current_func = None self.current_block = None @@ -163,7 +164,14 @@ def gen_expr(self, expr): self.add_instr(BinaryInstr(expr.name, args[0], args[1], dest)) return dest if expr.name in ('ptr_of', 'str_of') and expr.args: - return self.gen_expr(expr.args[0]) + v = self.gen_expr(expr.args[0]) + if self.bigint_mode: + dest = self.new_temp() + dest.type = BaseType('int') if expr.name == 'ptr_of' else BaseType('string') + op = 'tag_word' if expr.name == 'ptr_of' else 'untag_word' + self.add_instr(BinaryInstr(op, v, v, dest)) + return dest + return v v = self.new_temp() self.add_instr(ConstInstr(0, 'int', v)) return v diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index 980884eb..446c4b29 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -273,30 +273,30 @@ fn init_builtins(): g_rt_builtin_names = alloc(8 * 8) g_rt_builtin_ret_types = alloc(8 * 8) - bi_add("load8", TI_INT) - bi_add("store8", TI_INT) - bi_add("load64", TI_INT) - bi_add("r64", TI_INT) - bi_add("alloc", TI_STR) - bi_add("get_arg", TI_STR) - bi_add("w32", TI_UNIT) - bi_add("w64", TI_UNIT) - bi_add("_dyncpy", TI_UNIT) - bi_add("load_str_ptr", TI_STR) - bi_add("store_str_ptr", TI_INT) - bi_add("sched_call_0", TI_INT) - bi_add("sched_call_1", TI_INT) - bi_add("sched_call_2", TI_INT) - bi_add("sched_call_3", TI_INT) - bi_add("sched_call_4", TI_INT) - bi_add("fiber_switch", TI_INT) - bi_add("fiber_init", TI_INT) - bi_add("sched_go", TI_INT) - bi_add("m_start_workers", TI_UNIT) - bi_add("g_set_curg", TI_UNIT) - bi_add("g_get_curg", TI_INT) - -fn bi_add(name: string, ret_ti: int): + builtin_add("load8", TI_INT) + builtin_add("store8", TI_INT) + builtin_add("load64", TI_INT) + builtin_add("r64", TI_INT) + builtin_add("alloc", TI_STR) + builtin_add("get_arg", TI_STR) + builtin_add("w32", TI_UNIT) + builtin_add("w64", TI_UNIT) + builtin_add("_dyncpy", TI_UNIT) + builtin_add("load_str_ptr", TI_STR) + builtin_add("store_str_ptr", TI_INT) + builtin_add("sched_call_0", TI_INT) + builtin_add("sched_call_1", TI_INT) + builtin_add("sched_call_2", TI_INT) + builtin_add("sched_call_3", TI_INT) + builtin_add("sched_call_4", TI_INT) + builtin_add("fiber_switch", TI_INT) + builtin_add("fiber_init", TI_INT) + builtin_add("sched_go", TI_INT) + builtin_add("m_start_workers", TI_UNIT) + builtin_add("g_set_curg", TI_UNIT) + builtin_add("g_get_curg", TI_INT) + +fn builtin_add(name: string, ret_ti: int): ni := str_intern(name) if g_rt_builtin_count * 8 + 8 > str_len(g_rt_builtin_names): nc := g_rt_builtin_count * 2 + 16 diff --git a/tests/bootstrap/test_bigint_pipeline.py b/tests/bootstrap/test_bigint_pipeline.py index 8ee07133..f91c9005 100644 --- a/tests/bootstrap/test_bigint_pipeline.py +++ b/tests/bootstrap/test_bigint_pipeline.py @@ -29,7 +29,7 @@ """ -def compile_module(src: str) -> Module: +def compile_module(src: str, bigint_mode: bool = False) -> Module: ast = Parser(Lexer(src).tokenize()).parse_compilation_unit() resolver = NameResolver() resolver.resolve(ast) @@ -38,7 +38,7 @@ def compile_module(src: str) -> Module: checker.check(ast) if resolver.errors or checker.errors: raise AssertionError(resolver.errors + checker.errors) - return IRGen(resolver.symtab).gen_module(ast) + return IRGen(resolver.symtab, bigint_mode=bigint_mode).gen_module(ast) def build_object(module: Module, path: Path, **kwargs) -> None: @@ -52,7 +52,7 @@ def build_object(module: Module, path: Path, **kwargs) -> None: def main() -> int: raw_mod = compile_module(RUNTIME.read_text(encoding="utf-8")) - user_mod = compile_module(SRC) + user_mod = compile_module(SRC, bigint_mode=True) with tempfile.TemporaryDirectory(prefix="core-bigint-pipeline-") as td: d = Path(td) raw_obj = d / "bigint.o" From eeff5c87cb083e8f1344c6749315410ca63e21f9 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 11:35:05 +0800 Subject: [PATCH 20/58] frontend: preserve arbitrary precision integer literals --- build_selfhost_native.py | 2 + src/compiler/_import.cr | 1 + src/compiler/ast.cr | 5 +- src/compiler/big_lit.cr | 87 ++++++++++++++++++++++++++ src/compiler/checker.cr | 2 + src/compiler/dataflow.cr | 4 ++ src/compiler/globals.cr | 1 + src/compiler/ir_gen.cr | 4 ++ src/compiler/lexer.cr | 15 +++-- src/compiler/parser.cr | 8 +++ src/lsp/_import.cr | 1 + tests/selfhost/test_bigint_literals.py | 61 ++++++++++++++++++ 12 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 src/compiler/big_lit.cr create mode 100644 tests/selfhost/test_bigint_literals.py diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 32578dd4..4e726757 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -326,6 +326,7 @@ def main(): 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/literal.cr', + 'src/compiler/big_lit.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', @@ -432,6 +433,7 @@ def main(): 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/literal.cr', + 'src/compiler/big_lit.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index 346a40df..3d600ece 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -7,6 +7,7 @@ import ccr_schema import globals import dyn_arr import literal +import big_lit import lexer import parser import checker diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index b8ca1a7c..49b4aed2 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -101,6 +101,7 @@ T_EXTERN : int = 100 // extern "C" / foreign function declaration // has a function named check and a CLI subcommand named check. T_HASH : int = 101 T_INTERP : int = 102 // interpolation hole token; lexeme is the hole text, iv carries part and closure flags +T_BIGINT : int = 103 // arbitrary precision integer token; lexeme is canonical decimal text // Layout tokens and the two colon meanings. // T_NEWLINE is emitted at the end of a logical line that contains a token. Blank @@ -255,6 +256,7 @@ EXPR_OPTIONAL : int = 49 // a=inner type node (T? 类型位置) // a typed conversion call. data is the interned hole text; line and col locate the // hole for diagnostics. EXPR_INTERP_HOLE : int = 50 // data=hole text ni(解析期);checker 改写后本 kind 不再存在 +EXPR_BIGINT : int = 51 // a=canonical decimal string ni; type TY_INT // Field representation in struct literal: two consecutive AST nodes // (name_idx, value_idx, line=line, col=col) @@ -622,7 +624,8 @@ IR_YIELD : int = 28 // s1=value_var; emit value from flow to consumer channe IR_AWAIT : int = 29 // dest=value_var, s1=future_var; block until future ready, get value IR_BOUNDS_CHECK : int = 30 // s1=index, s2=max_len/len_var, type_kind=1 for dynamic max IR_ARENA_NEW : int = 32 // dest=arena_var, src1=size_estimate -IR_ARENA_RESET : int = 33 // src1=arena_id (dest=-1) +IR_ARENA_RESET : int = 33 // src1=arena_id +IR_CONST_BIG : int = 34 // dest=value, s1=canonical decimal string index, tk=TI_INT (dest=-1) IR_INLINE : int = 34 // src1=fn_var; inline hint IR_NO_BOUNDS_CHECK : int = 35 //skip bounds check for subsequent DEREFs IR_FAST : int = 36 //allow precision-for-speed optimizations diff --git a/src/compiler/big_lit.cr b/src/compiler/big_lit.cr new file mode 100644 index 00000000..4b7274a5 --- /dev/null +++ b/src/compiler/big_lit.cr @@ -0,0 +1,87 @@ +// === big_lit.cr === +// Big integer literal normalization for the frontend. +// The compiler itself uses machine-word ints; this module only keeps the exact +// decimal text of a literal that does not fit those ints. + +// Multiply a little-endian decimal string by base, then add digit. +// Returns a new little-endian decimal string. +fn dec_mul_add(dec: string, base: int, digit: int) -> string: + n := str_len(dec) + out := alloc(n + 2) + carry : ., mut = digit + i : ., mut = 0 + loop: + if i >= n: + break + v := (load8(dec, i) - 48) * base + carry + store8(out, i, v % 10 + 48) + carry = v / 10 + i = i + 1 + pos : ., mut = n + loop: + if carry == 0: + break + store8(out, pos, carry % 10 + 48) + carry = carry / 10 + pos = pos + 1 + store8(out, pos, 0) + return out + +// Convert an integer literal (decimal or 0x/0o/0b) to canonical decimal text. +fn big_lit_decimal(s: string) -> string: + sl := str_len(s) + base : ., mut = 10 + start : ., mut = 0 + if sl >= 2 && load8(s, 0) == 48: + pfx := load8(s, 1) + if pfx == 120 || pfx == 88: + base = 16 + start = 2 + else if pfx == 111 || pfx == 79: + base = 8 + start = 2 + else if pfx == 98 || pfx == 66: + base = 2 + start = 2 + dec := alloc(2) + store8(dec, 0, 48) + i : ., mut = start + loop: + if i >= sl: + break + c := load8(s, i) + if c == 95: + i = i + 1 + continue + d : ., mut = 0 + if c >= 48 && c <= 57: + d = c - 48 + else if c >= 65 && c <= 70: + d = c - 55 + else: + d = c - 87 + dec = dec_mul_add(dec, base, d) + i = i + 1 + + n := str_len(dec) + last : ., mut = n - 1 + loop: + if last <= 0: + break + if load8(dec, last) != 48: + break + last = last - 1 + if last == 0 && load8(dec, 0) == 48: + z := alloc(2) + store8(z, 0, 48) + return z + out := alloc(last + 2) + j : ., mut = 0 + loop: + if last < 0: + break + store8(out, j, load8(dec, last)) + last = last - 1 + j = j + 1 + store8(out, j, 0) + return out diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index 446c4b29..d14abace 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -2799,6 +2799,8 @@ fn infer_expr(node: int) -> int: // 不得重排、不得合并成先取 kind 再查的循环(那会改短路路径)。 if ast_kind(node) == EXPR_INT: return iface_lit_ti(EXPR_INT) + if ast_kind(node) == EXPR_BIGINT: + return TI_INT if ast_kind(node) == EXPR_NONE && ast_a(node) >= 0 && ast_a(node) != node: return infer_expr(ast_a(node)) if ast_kind(node) == EXPR_DEX: diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index 595efc66..fd97c254 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -277,6 +277,8 @@ fn df_use_var(consumer_node: int, var_idx: int): fn df_connect_srcs(node_id: int, opcode: int, s1: int, s2: int, s3: int, tk_aux: int): if opcode == IR_CONST: return + if opcode == IR_CONST_BIG: + return // all srcs are scalar values/labels if opcode == IR_BINARY: @@ -547,6 +549,8 @@ fn df_graph_to_dot() -> string: fn df_opcode_name(opcode: int, s3: int) -> string: if opcode == IR_CONST: return "const" + if opcode == IR_CONST_BIG: + return "const_big" if opcode == IR_BINARY: if s3 == OP_PTR_ADD: return "ptr_add" diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index ffa636e0..06dfc124 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -276,6 +276,7 @@ g_checker_current_fi : int, mut // change the .ccr STR segment, so it must stay off by default to keep output // byte-identical. g_s1p_on : int, mut // S1′ 打点开关:0 = 关(默认行为)· 1 = 开(`CORE_S1P=1`) +g_int_big : int, mut // set by str_int_literal when the literal does not fit a machine word g_s1p_seen : int, mut // 0 = environment variable not read yet. The sentinel must be // zero: non-literal global initializers are stored as 0 by the // Python bootstrap build, so `= -1` cannot be relied on. diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 6759e36a..44d3c231 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -1403,6 +1403,10 @@ fn gen_expr(node: int) -> int: v := new_ir_var("int", TI_INT) emit(IR_CONST, v, ast_int_val(node), 0, 0, TI_INT) return v + if ast_kind(node) == EXPR_BIGINT: + v := new_ir_var("bigint", TI_INT) + emit(IR_CONST_BIG, v, ast_a(node), 0, 0, TI_INT) + return v if ast_kind(node) == EXPR_DEX: // 精确字面量:定点缩放整数常量(TI_DEX_S)。 // apx 场景(变量级 apx 标签的运算)在运算点按 node.a 的 binary64 位模式重发射。 diff --git a/src/compiler/lexer.cr b/src/compiler/lexer.cr index 79d8c8d0..881537d2 100644 --- a/src/compiler/lexer.cr +++ b/src/compiler/lexer.cr @@ -198,6 +198,7 @@ fn lookup_keyword(s: string) -> int: // has no carrier in this interface. It is not a semantic overflow. Wider // representations are future work. fn str_int_literal(s: string) -> int: + g_int_big = 0 sl := str_len(s) base : ., mut = 10 start : ., mut = 0 @@ -237,19 +238,19 @@ fn str_int_literal(s: string) -> int: // i64min. Use i64max/base, with the decimal digit boundary handled below. if base == 10: if value > 922337203685477580 || (value == 922337203685477580 && d > 7): - add_error("integer literal overflow") + g_int_big = 1 return 0 else if base == 16: if value > 576460752303423487: - add_error("integer literal overflow") + g_int_big = 1 return 0 else if base == 8: if value > 1152921504606846975: - add_error("integer literal overflow") + g_int_big = 1 return 0 else: if value > 4611686018427387903: - add_error("integer literal overflow") + g_int_big = 1 return 0 value = value * base + d digits = digits + 1 @@ -668,7 +669,11 @@ fn tokenize(_src: string, layout_bypass: int): if has_dot != 0: add_tok_int_lex(T_DEX, str_to_scaled(num_str), str_intern(num_str), start_line, start_col) else: - add_tok_int(T_INT, str_int_literal(num_str), start_line, start_col) + iv := str_int_literal(num_str) + if g_int_big != 0: + add_tok_str(T_BIGINT, big_lit_decimal(num_str), start_line, start_col) + else: + add_tok_int(T_INT, iv, start_line, start_col) _pos = skip_ws(_src, _pos, _slen) continue diff --git a/src/compiler/parser.cr b/src/compiler/parser.cr index 980d1ccb..0e7abe79 100644 --- a/src/compiler/parser.cr +++ b/src/compiler/parser.cr @@ -571,6 +571,10 @@ fn parse_primary() -> int: if tok_k(t) == T_INT: advance_tok() return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)) + if tok_k(t) == T_BIGINT: + advance_tok() + tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) + return alloc_node(EXPR_BIGINT, tl, 0, 0, 0, TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_DEX: advance_tok() // Node fields: a = binary64 bit pattern, restored from the numeric text in @@ -1416,6 +1420,10 @@ fn parse_pattern() -> int: if tok_k(t) == T_INT: advance_tok() return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)) + if tok_k(t) == T_BIGINT: + advance_tok() + tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) + return alloc_node(EXPR_BIGINT, tl, 0, 0, 0, TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_STRING || tok_k(t) == T_INTERP: return parse_interp_run() if tok_k(t) == T_CHAR: diff --git a/src/lsp/_import.cr b/src/lsp/_import.cr index d1d03316..70380201 100644 --- a/src/lsp/_import.cr +++ b/src/lsp/_import.cr @@ -1,6 +1,7 @@ // _import.cr — imports applied to all .cr files in src/lsp/ // 导入编译器前端模块(lexer/parser/checker/diag/module/globals) import literal +import big_lit import lexer import parser import checker diff --git a/tests/selfhost/test_bigint_literals.py b/tests/selfhost/test_bigint_literals.py new file mode 100644 index 00000000..9149ff16 --- /dev/null +++ b/tests/selfhost/test_bigint_literals.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +"""Core frontend support for arbitrary precision integer literals.""" + +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" + + +def run(args): + return subprocess.run(args, cwd=str(BASE), capture_output=True, text=True) + + +def main() -> int: + if not COREC.exists(): + print("missing build/corec") + return 1 + with tempfile.TemporaryDirectory(prefix="core-biglit-") as td: + d = Path(td) + src = d / "big.cr" + out = d / "big.ccr" + src.write_text( + "fn main() -> int:\n" + " a := 9223372036854775808\n" + " b := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n" + " if a < b:\n" + " return 0\n" + " return 1\n", + encoding="utf-8", + ) + r = run([str(COREC), "check", str(src)]) + if r.returncode != 0: + print(r.stdout) + print(r.stderr) + return 1 + cir = d / "big.cir" + r = run([str(COREC), "cir", str(src), "-o", str(cir)]) + if r.returncode != 0: + print(r.stdout) + print(r.stderr) + return 1 + text = cir.read_text(encoding="utf-8", errors="replace") + if "const_big" not in text: + print("cir missing const_big node") + return 1 + r = run([str(COREC), "ccr", str(src), "-o", str(out)]) + if r.returncode != 0: + print(r.stdout) + print(r.stderr) + return 1 + if out.stat().st_size == 0: + print("empty ccr") + return 1 + print("PASS bigint literals") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 9bbf21cf6d10bb698ade734cc90a07df64539aea Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 11:38:15 +0800 Subject: [PATCH 21/58] backend: add tagged BigInt arithmetic under --big-int --- build_selfhost_native.py | 2 + .../2026-10-23-arbitrary-precision-int.md | 17 +- src/arch/x86_64/backend_flags.cr | 1 + src/arch/x86_64/backend_globals.cr | 5 + src/arch/x86_64/corearch.cr | 1 + src/arch/x86_64/instr.cr | 31 ++ src/arch/x86_64/int_rep.cr | 313 ++++++++++++++++++ src/compiler/ast.cr | 2 +- src/compiler/big_lit.cr | 19 +- src/format/elf/elf.cr | 69 ++++ src/os/linux/entry.cr | 10 + src/runtime/bigint.cr | 19 ++ src/runtime/bigint_blob.cr | 233 +++++++++++++ src/targets/x86_64-linux/_import.cr | 2 + src/targets/x86_64-linux/main.cr | 1 + tests/selfhost/test_bigint_arith.py | 211 ++++++++++++ tools/gen_bigint_blob.py | 259 +++++++++++++++ 17 files changed, 1182 insertions(+), 13 deletions(-) create mode 100644 src/arch/x86_64/int_rep.cr create mode 100644 src/runtime/bigint_blob.cr create mode 100644 tests/selfhost/test_bigint_arith.py create mode 100644 tools/gen_bigint_blob.py diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 4e726757..42f0e510 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -68,6 +68,8 @@ 'src/arch/x86_64/backend_arrays.cr', 'src/arch/x86_64/regalloc.cr', 'src/arch/x86_64/sizes.cr', + 'src/arch/x86_64/int_rep.cr', + 'src/runtime/bigint_blob.cr', 'src/arch/x86_64/instr.cr', 'src/arch/x86_64/tag2l.cr', 'src/arch/x86_64/frame.cr', diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index a1b428d8..aa206288 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -137,17 +137,18 @@ python3 tests/bootstrap/test_bigint_frontend.py 2. 超出立即数范围的常量生成静态 BigInt 对象。 3. 加减乘除、取模、比较生成立即数快路径和 BigInt 运行时调用。 4. 指针、FFI、`w64/r64`、`@ptr_of`、`@str_of` 生成范围检查。 -5. `build_selfhost_native.py` 支持 `BIGINT=1`,输出 `build/corec-big` 和 `build/corearch-big`,不覆盖旧二进制。 -6. `test_bigint_pipeline.py` 用 Python 后端编译并运行一个 Core 程序,覆盖大整数字面量、算术、函数调用和打印。 +5. 编译器自身保持 raw;`build_selfhost_native.py` 不生成 tagged 的 `corec-big`。tagged Python 代码生成只用于用户程序测试。 +6. `test_bigint_pipeline.py` 用 Python 后端编译并运行一个 Core 程序,覆盖大整数字面量、算术、函数调用和返回;BigInt 运行时单独以 raw 语义编译并链接。 ### 验收 ```bash -BIGINT=1 python3 build_selfhost_native.py +python3 build_selfhost_native.py python3 tests/bootstrap/test_bigint_pipeline.py +bash tools/baseline/canary_check.sh ``` -不设置 `BIGINT` 时旧构建仍通过。 +不设置任何开关时编译器仍以 raw 语义构建和运行。 ## 任务 4:Core 前端和 `.ccr` 大常量 @@ -239,10 +240,12 @@ python3 tests/selfhost/test_bigint_literals.py ### 验收 ```bash -BIGINT=1 python3 build_selfhost_native.py +python3 build_selfhost_native.py python3 tests/selfhost/test_bigint_arith.py ``` +`corearch --big-int` 只改变用户程序;不设置该旗标时旧的 raw 路径和 canary 保持不变。 + ## 任务 6:容器、调用和全局变量 ### 文件 @@ -376,8 +379,8 @@ cmp /tmp/corec2 /tmp/corec3 ## 集成顺序 1. 任务 1 只改运行时,不进入编译器。 -2. 任务 2 到 3 改 Python bootstrap 前端和后端,用 `BIGINT=1` 构建新编译器。 -3. 任务 4 到 8 用 `corec-big` 和 `corearch-big` 逐步替换 Core 前端、后端和解释器。 +2. 任务 2 到 3 改 Python bootstrap 前端和后端:编译器仍 raw,tagged 代码生成只用于用户程序测试。 +3. 任务 4 到 8 用 raw 的 `corec` 和 `corearch --big-int` 逐步替换 Core 前端、后端和解释器。 4. 任务 9 才切换默认语义,删除旧路径,更新 canary。 ## 不包含 diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index 14aec3ae..f2684463 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -8,6 +8,7 @@ fn register_backend_flags(): cli_flag("link", "l", "Comma-sep .so files, or 'auto' for ~/.core/lib/") cli_flag("output", "o", "Output path") cli_flag("opt-level", "O", "Optimization level (0-3, default=0)") + cli_flag_bool("big-int", "", "Arbitrary precision int semantics for user programs") cli_flag("table", "", "HIT table file (load & emit mapped ops through it)") cli_flag_debug("hit-events-file", "", "Debug: inject text event stream file") cli_flag_bool("dump-events", "", "Dump lowered HIT event stream + const pool") diff --git a/src/arch/x86_64/backend_globals.cr b/src/arch/x86_64/backend_globals.cr index 19e219f5..a1325cf5 100644 --- a/src/arch/x86_64/backend_globals.cr +++ b/src/arch/x86_64/backend_globals.cr @@ -116,3 +116,8 @@ g_x86_alloc_patch_pos : string, mut g_x86_alloc_patch_cap : int, mut g_x86_alloc_patch_count : int, mut g_x86_emit_rt_stubs : int, mut // 1 = pure-static: backend emits g_set_curg/g_get_curg stubs +// --big-int: user int slots hold tagged values (immediate v*2 or BigInt pointer+1). +// The compiler itself and the BigInt runtime blob stay raw. See int_rep.cr. +g_x86_big_enabled : int, mut +g_x86_big_blob_start : int, mut +g_x86_big_exit_call_pos : int, mut diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index de69a0c7..66249efe 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -300,6 +300,7 @@ fn corearch_main() -> int: if cli_parse() != 0: return 1 + g_x86_big_enabled = cli_has("big-int") // Boot: select the instance from the flags, then query its declaration row at // each reject, table, and O2 gate. instance_decl_init() diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index f9c58231..583b0827 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -609,8 +609,33 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + e2_st(buf, pos+cp, 0, do2) return cp + if op == IR_CONST_BIG && d >= 0: + if g_x86_big_enabled != 0: + ro := g2_str_off(s1) + grow_rodataref(g_x86_rodataref_count + 1) + w64(g_x86_rodataref_pos, g_x86_rodataref_count * 8, pos + cp) + w64(g_x86_rodataref_ro, g_x86_rodataref_count * 8, ro) + g_x86_rodataref_count = g_x86_rodataref_count + 1 + cp = cp + e2_lr(buf, pos+cp, 0) // r10 = decimal string + cp = cp + e2_mov(buf, pos+cp, 7, 10) // rdi = string + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_dec")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) // rax -> dest + return cp + if op == IR_CONST && d >= 0: do2 := g2_slot(d) + if ti == TI_INT && g_x86_big_enabled != 0: + if s1 >= BI_TAG_IMM_MIN && s1 <= BI_TAG_IMM_MAX: + cp = cp + e2_big_mov_imm64(buf, pos+cp, 10, s1 * 2) + cp = cp + e2_st_var(buf, pos+cp, 10, d) + else: + // The literal fits a machine word but not the immediate range. + // bi_from_word converts the raw word to a tagged value. + cp = cp + e2_big_mov_imm64(buf, pos+cp, 7, s1) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_mov(buf, pos+cp, 10, 0) + cp = cp + e2_st_var(buf, pos+cp, 10, d) + return cp if ti == TI_STR: ro := g2_str_off(s1) // Record for post-emission patching (rodata position from Phase 3) @@ -648,6 +673,10 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_BINARY: do2 := g2_slot(d) + if g_x86_big_enabled != 0 && ti == TI_INT: + bz := emit_big_binary(buf, pos, d, s1, s2, s3) + if bz > 0: + return bz if ti == TI_DEX: // binary64 运算(SSE2 double,IEEE 754),apx 快路径标准答案实现 // Both operands go through the global operand seam. @@ -888,6 +917,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_UNARY && d >= 0: do2 := g2_slot(d) + if g_x86_big_enabled != 0 && s3 == UOP_NEG: + return emit_big_neg(buf, pos, d, s1) cp = cp + e2_load_var(buf, pos+cp, 10, s1) if s3 == UOP_NEG: // neg r10: REX.WB + 0xF7 + /3 diff --git a/src/arch/x86_64/int_rep.cr b/src/arch/x86_64/int_rep.cr new file mode 100644 index 00000000..27cda1be --- /dev/null +++ b/src/arch/x86_64/int_rep.cr @@ -0,0 +1,313 @@ +// === int_rep.cr === +// Tagged mathematical-int helpers for the x86-64 backend. +// +// The backend's public int slot is a tagged value: +// low bit 0: mathematical value times two (immediate) +// low bit 1: pointer to a BigInt object plus one +// +// Only user programs run in this mode. corearch itself, and the BigInt runtime +// blob generated from src/runtime/bigint.cr, use raw machine integers. +// This file owns the encoding helpers and the cold-path jump/call emitters; the +// arithmetic policy lives in instr.cr next to the old 64-bit path. + +BI_TAG_IMM_MAX : int = 4611686018427387903 // 2^62 - 1 +BI_TAG_IMM_MIN : int = -4611686018427387904 // -2^62 + +// --big-int state is in backend_globals.cr so both entry points set it once. + +fn e2_big_mov_imm64(buf: string, p: int, reg: int, value: int) -> int: + // movabs reg, imm64 — REX.W (+B for reg>=8), 0xB8+reg, imm64 + cp := p + cp = cp + emit_rex(buf, cp, 1, 0, 0, reg / 8) + e2_w8(buf, cp, 184 + reg % 8) + cp = cp + 1 + cp = cp + e2_w64(buf, cp, value) + return cp - p + +fn e2_big_test_tag(buf: string, p: int, reg: int) -> int: + // test reg, 1 — REX.W(+B), 0xF7 /0, imm32 1 + cp := p + cp = cp + emit_rex(buf, cp, 1, 0, 0, reg / 8) + e2_w8(buf, cp, 247) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 0, reg % 8) + cp = cp + e2_w32(buf, cp, 1) + return cp - p + +fn e2_big_tag_reg(buf: string, p: int, reg: int) -> int: + // shl reg, 1 — REX.W(+B), 0xD1 /4 + cp := p + cp = cp + emit_rex(buf, cp, 1, 0, 0, reg / 8) + e2_w8(buf, cp, 209) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 4, reg % 8) + return cp - p + +fn e2_big_untag_reg(buf: string, p: int, reg: int) -> int: + // sar reg, 1 — REX.W(+B), 0xD1 /7 + cp := p + cp = cp + emit_rex(buf, cp, 1, 0, 0, reg / 8) + e2_w8(buf, cp, 209) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 7, reg % 8) + return cp - p + +fn e2_big_neg_reg(buf: string, p: int, reg: int) -> int: + // neg reg — REX.W(+B), 0xF7 /3 + cp := p + cp = cp + emit_rex(buf, cp, 1, 0, 0, reg / 8) + e2_w8(buf, cp, 247) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 3, reg % 8) + return cp - p + +fn e2_big_imul_rr(buf: string, p: int, dst: int, src: int) -> int: + // imul dst, src — REX.WR/B, 0x0F 0xAF, ModRM(3, dst, src) + cp := p + cp = cp + emit_rex(buf, cp, 1, dst / 8, 0, src / 8) + e2_w8(buf, cp, 15) + cp = cp + 1 + e2_w8(buf, cp, 175) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, dst % 8, src % 8) + return cp - p + +fn e2_big_jcc(buf: string, p: int, cond: int) -> int: + // jcc rel32 — 0x0F 0x80+cond, placeholder rel32 + cp := p + e2_w8(buf, cp, 15) + cp = cp + 1 + e2_w8(buf, cp, 128 + cond) + cp = cp + 1 + cp = cp + e2_w32(buf, cp, 0) + return cp - p + +fn e2_big_jmp(buf: string, p: int) -> int: + // jmp rel32 — 0xE9, placeholder rel32 + cp := p + e2_w8(buf, cp, 233) + cp = cp + 1 + cp = cp + e2_w32(buf, cp, 0) + return cp - p + +fn e2_big_patch_jcc(buf: string, jump_pos: int, target_pos: int): + w32(buf, jump_pos + 2, target_pos - (jump_pos + 6)) + +fn e2_big_patch_jmp(buf: string, jump_pos: int, target_pos: int): + w32(buf, jump_pos + 1, target_pos - (jump_pos + 5)) + +fn e2_big_call_name(buf: string, p: int, name_idx: int) -> int: + // E8 rel32 placeholder; resolved by the existing call-patch table in elf.cr. + grow_call_patch(g_x86_call_patch_count + 1) + w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, p) + w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_idx) + g_x86_call_patch_count = g_x86_call_patch_count + 1 + e2_w8(buf, p, 232) + e2_w32(buf, p + 1, 0) + return 5 + +fn e2_big_setcc(buf: string, p: int, cond: int) -> int: + // setcc al — 0x0F 0x90+cond, ModRM(3,0,0) + cp := p + e2_w8(buf, cp, 15) + cp = cp + 1 + e2_w8(buf, cp, 144 + cond) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 0, 0) + return cp - p + +fn e2_big_bool_to_r10(buf: string, p: int) -> int: + // movzx r10d, al — REX.R + 0x0F 0xB6, ModRM(3,r10,al) + cp := p + cp = cp + emit_rex(buf, cp, 0, 10 / 8, 0, 0) + e2_w8(buf, cp, 15) + cp = cp + 1 + e2_w8(buf, cp, 182) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, 10 % 8, 0) + return cp - p + +fn e2_big_test_rr(buf: string, p: int, a: int, b: int) -> int: + // test a, b — REX.WR/B, 0x85, ModRM(3,a,b) + cp := p + cp = cp + emit_rex(buf, cp, 1, a / 8, 0, b / 8) + e2_w8(buf, cp, 133) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 3, a % 8, b % 8) + return cp - p + +fn e2_big_test_rax(buf: string, p: int) -> int: + return e2_big_test_rr(buf, p, 0, 0) + +fn emit_big_neg(buf: string, pos: int, d: int, s1: int) -> int: + cp : ., mut = 0 + cp = cp + e2_load_var(buf, pos + cp, 10, s1) + cp = cp + e2_mov(buf, pos + cp, 8, 10) + cp = cp + e2_big_test_tag(buf, pos + cp, 10) + j1 := pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 5) // jne slow + cp = cp + e2_big_untag_reg(buf, pos + cp, 10) + cp = cp + e2_big_neg_reg(buf, pos + cp, 10) + cp = cp + e2_big_mov_imm64(buf, pos + cp, 11, BI_TAG_IMM_MAX) + cp = cp + e2_alu(buf, pos + cp, 57) + r1 := pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 15) // jg slow + cp = cp + e2_big_mov_imm64(buf, pos + cp, 11, BI_TAG_IMM_MIN) + cp = cp + e2_alu(buf, pos + cp, 57) + r2 := pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 12) // jl slow + cp = cp + e2_big_tag_reg(buf, pos + cp, 10) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + fast_jmp := pos + cp + cp = cp + e2_big_jmp(buf, pos + cp) + slow_pos := pos + cp + cp = cp + e2_mov(buf, pos + cp, 7, 8) + cp = cp + e2_big_call_name(buf, pos + cp, str_intern("bi_neg")) + cp = cp + e2_st_var(buf, pos + cp, 0, d) + done_pos := pos + cp + e2_big_patch_jcc(buf, j1, slow_pos) + e2_big_patch_jcc(buf, r1, slow_pos) + e2_big_patch_jcc(buf, r2, slow_pos) + e2_big_patch_jmp(buf, fast_jmp, done_pos) + return cp + + +// Emit one tagged integer binary operation. Returns the number of bytes, or 0 +// when the opcode is not an integer arithmetic/comparison op and the caller must +// keep the legacy path. +// +// Fast path: both operands have low bit 0. They are untagged with sar, the +// machine op runs, and the result is re-tagged when it fits the immediate range. +// Anything else (odd tag, arithmetic overflow, out-of-range result, division by +// zero) calls the raw BigInt runtime with the original tagged operands. +fn emit_big_binary(buf: string, pos: int, d: int, s1: int, s2: int, op: int) -> int: + is_arith := op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV || op == OP_MOD + is_cmp := op >= OP_EQ && op <= OP_GE + if !is_arith && !is_cmp: + return 0 + + cp : ., mut = 0 + cp = cp + e2_load_var(buf, pos + cp, 10, s1) + cp = cp + e2_load_var(buf, pos + cp, 11, s2) + // Keep the original tagged operands for every cold call. r8/r9 are + // caller-saved and the register allocator only assigns rbx/r12-r15. + cp = cp + e2_mov(buf, pos + cp, 8, 10) + cp = cp + e2_mov(buf, pos + cp, 9, 11) + + j1 : ., mut = -1 + j2 : ., mut = -1 + cp = cp + e2_big_test_tag(buf, pos + cp, 10) + j1 = pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 5) // jne slow + if s1 != s2: + cp = cp + e2_big_test_tag(buf, pos + cp, 11) + j2 = pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 5) + cp = cp + e2_big_untag_reg(buf, pos + cp, 10) + cp = cp + e2_big_untag_reg(buf, pos + cp, 11) + + slow_name : ., mut = "" + mul_j : ., mut = -1 + div_j : ., mut = -1 + if op == OP_ADD: + slow_name = "bi_add" + cp = cp + e2_alu(buf, pos + cp, 1) + else if op == OP_SUB: + slow_name = "bi_sub" + cp = cp + e2_alu(buf, pos + cp, 41) + else if op == OP_MUL: + slow_name = "bi_mul" + cp = cp + e2_big_imul_rr(buf, pos + cp, 10, 11) + // imul overflow means the 64-bit raw product is unusable; take the + // runtime path with the original tagged values. + mul_j = pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 0) // jo slow + else if op == OP_DIV || op == OP_MOD: + slow_name = "bi_div" + if op == OP_MOD: + slow_name = "bi_mod" + // Divisor zero must go through the runtime's unified error path. + cp = cp + e2_big_test_rr(buf, pos + cp, 11, 11) + div_j = pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 4) // je slow + cp = cp + e2_mov(buf, pos + cp, 0, 10) + cp = cp + emit_rex(buf, pos + cp, 1, 0, 0, 0) + e2_w8(buf, pos + cp, 153) // cqo + cp = cp + 1 + cp = cp + emit_rex(buf, pos + cp, 1, 0, 0, 11 / 8) + e2_w8(buf, pos + cp, 247) // idiv r11 + cp = cp + 1 + cp = cp + emit_modrm(buf, pos + cp, 3, 7, 11 % 8) + if op == OP_DIV: + cp = cp + e2_mov(buf, pos + cp, 10, 0) + else: + cp = cp + e2_mov(buf, pos + cp, 10, 2) + else: + // comparison + slow_name = "bi_cmp" + cp = cp + e2_alu(buf, pos + cp, 57) // cmp r10, r11 + cond : ., mut = 4 + if op == OP_NE: + cond = 5 + else if op == OP_LT: + cond = 12 + else if op == OP_GT: + cond = 15 + else if op == OP_LE: + cond = 14 + else if op == OP_GE: + cond = 13 + cp = cp + e2_big_setcc(buf, pos + cp, cond) + cp = cp + e2_big_bool_to_r10(buf, pos + cp) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + fast_jmp := pos + cp + cp = cp + e2_big_jmp(buf, pos + cp) + slow_pos := pos + cp + cp = cp + e2_mov(buf, pos + cp, 7, 8) + cp = cp + e2_mov(buf, pos + cp, 6, 9) + cp = cp + e2_big_call_name(buf, pos + cp, str_intern(slow_name)) + cp = cp + e2_big_test_rax(buf, pos + cp) + cp = cp + e2_big_setcc(buf, pos + cp, cond) + cp = cp + e2_big_bool_to_r10(buf, pos + cp) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + done_pos := pos + cp + e2_big_patch_jcc(buf, j1, slow_pos) + if j2 >= 0: + e2_big_patch_jcc(buf, j2, slow_pos) + e2_big_patch_jmp(buf, fast_jmp, done_pos) + return cp + + // Arithmetic fast path: reject results outside the immediate range. For + // add/sub this is a range check, not a machine overflow: both inputs are + // already in [-2^62, 2^62-1], and the sum fits signed 64-bit. + cp = cp + e2_big_mov_imm64(buf, pos + cp, 11, BI_TAG_IMM_MAX) + cp = cp + e2_alu(buf, pos + cp, 57) // cmp r10, r11 + range_j1 := pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 15) // jg slow + cp = cp + e2_big_mov_imm64(buf, pos + cp, 11, BI_TAG_IMM_MIN) + cp = cp + e2_alu(buf, pos + cp, 57) // cmp r10, r11 + range_j2 := pos + cp + cp = cp + e2_big_jcc(buf, pos + cp, 12) // jl slow + cp = cp + e2_big_tag_reg(buf, pos + cp, 10) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + fast_jmp2 := pos + cp + cp = cp + e2_big_jmp(buf, pos + cp) + slow_pos2 := pos + cp + cp = cp + e2_mov(buf, pos + cp, 7, 8) + cp = cp + e2_mov(buf, pos + cp, 6, 9) + cp = cp + e2_big_call_name(buf, pos + cp, str_intern(slow_name)) + cp = cp + e2_mov(buf, pos + cp, 10, 0) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + done_pos2 := pos + cp + e2_big_patch_jcc(buf, j1, slow_pos2) + if j2 >= 0: + e2_big_patch_jcc(buf, j2, slow_pos2) + if mul_j >= 0: + e2_big_patch_jcc(buf, mul_j, slow_pos2) + if div_j >= 0: + e2_big_patch_jcc(buf, div_j, slow_pos2) + e2_big_patch_jcc(buf, range_j1, slow_pos2) + e2_big_patch_jcc(buf, range_j2, slow_pos2) + e2_big_patch_jmp(buf, fast_jmp2, done_pos2) + return cp + diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index 49b4aed2..ed7df095 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -101,7 +101,7 @@ T_EXTERN : int = 100 // extern "C" / foreign function declaration // has a function named check and a CLI subcommand named check. T_HASH : int = 101 T_INTERP : int = 102 // interpolation hole token; lexeme is the hole text, iv carries part and closure flags -T_BIGINT : int = 103 // arbitrary precision integer token; lexeme is canonical decimal text +T_BIGINT : int = 113 // arbitrary precision integer token; lexeme is canonical decimal text // Layout tokens and the two colon meanings. // T_NEWLINE is emitted at the end of a logical line that contains a token. Blank diff --git a/src/compiler/big_lit.cr b/src/compiler/big_lit.cr index 4b7274a5..4f050cd9 100644 --- a/src/compiler/big_lit.cr +++ b/src/compiler/big_lit.cr @@ -3,10 +3,16 @@ // The compiler itself uses machine-word ints; this module only keeps the exact // decimal text of a literal that does not fit those ints. +// The frontend's alloc() header stores the requested byte count; str_len() +// subtracts one for the terminator. The decimal builder therefore carries an +// explicit length instead of calling str_len on a freshly allocated work buffer. +g_big_dec_len : int, mut + // Multiply a little-endian decimal string by base, then add digit. -// Returns a new little-endian decimal string. -fn dec_mul_add(dec: string, base: int, digit: int) -> string: - n := str_len(dec) +// dec_len is the exact digit count. The new digit count is stored in +// g_big_dec_len. Returns a new little-endian decimal buffer. +fn dec_mul_add(dec: string, dec_len: int, base: int, digit: int) -> string: + n := dec_len out := alloc(n + 2) carry : ., mut = digit i : ., mut = 0 @@ -25,6 +31,7 @@ fn dec_mul_add(dec: string, base: int, digit: int) -> string: carry = carry / 10 pos = pos + 1 store8(out, pos, 0) + g_big_dec_len = pos return out // Convert an integer literal (decimal or 0x/0o/0b) to canonical decimal text. @@ -45,6 +52,7 @@ fn big_lit_decimal(s: string) -> string: start = 2 dec := alloc(2) store8(dec, 0, 48) + dec_len : ., mut = 1 i : ., mut = start loop: if i >= sl: @@ -60,10 +68,11 @@ fn big_lit_decimal(s: string) -> string: d = c - 55 else: d = c - 87 - dec = dec_mul_add(dec, base, d) + dec = dec_mul_add(dec, dec_len, base, d) + dec_len = g_big_dec_len i = i + 1 - n := str_len(dec) + n := dec_len last : ., mut = n - 1 loop: if last <= 0: diff --git a/src/format/elf/elf.cr b/src/format/elf/elf.cr index adcdc18b..0658d52d 100644 --- a/src/format/elf/elf.cr +++ b/src/format/elf/elf.cr @@ -1490,6 +1490,7 @@ g_alloc_gl_jmp_pos : int, mut = -1 // Part 1 jl .Lglobal displacement position // ── Main ELF generation ── fn elf_gen(buf: string) -> int: + g_x86_big_exit_call_pos = -1 // Mark global variables for RIP-relative addressing gi := 0 loop: @@ -2076,6 +2077,69 @@ fn elf_gen(buf: string) -> int: sched_reg_one("m_start_workers", 0, cp + 15) cp = cp + emit_m_start_workers(buf, cp) + // ── BigInt runtime blob (--big-int) ── + // The generated blob is raw-core machine code plus its data. It is placed + // in the code segment; --big-int marks that segment writable so the runtime's + // g_bi_error/g_bi_rem slots work. All intra-blob relocations were resolved + // by tools/gen_bigint_blob.py; the only external calls are the four alloc + // sites, which are patched to the allocator emitted above. + if g_x86_big_enabled != 0: + blob := bigint_blob_bytes() + blob_start := cp + g_x86_big_blob_start = blob_start + bi : ., mut = 0 + loop: + if bi >= BIGINT_BLOB_SIZE: + break + store8(buf, blob_start + bi, load8(blob, bi)) + bi = bi + 1 + cp = cp + BIGINT_BLOB_SIZE + + // Register every bi_* entry so IR_CALL placeholders and the arithmetic + // cold paths resolve through the normal func_offsets fallback. + sci : ., mut = 0 + loop: + if sci >= bigint_blob_symbol_count(): + break + sym_name := bigint_blob_symbol_name(sci) + sym_off := bigint_blob_symbol(sym_name) + if sym_off >= 0: + grow_func_offsets(g_x86_func_off_count * 2 + 2) + w64(g_x86_func_offsets, g_x86_func_off_count * 16, + str_intern(sym_name)) + w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, + blob_start + sym_off - 176) + g_x86_func_off_count = g_x86_func_off_count + 1 + sci = sci + 1 + + aci : ., mut = 0 + loop: + if aci >= bigint_blob_alloc_count(): + break + aoff := bigint_blob_alloc_off(aci) + call_pos := blob_start + aoff + // gen_bigint_blob records the relocation offset, i.e. the first byte + // of the call's rel32 immediate. The call opcode is at call_pos-1. + w32(buf, call_pos, alloc_start - (call_pos + 4)) + aci = aci + 1 + + // _start's exit conversion call was emitted before the function list + // reset the general call-patch table. Resolve it explicitly now that the + // blob's bi_to_word_checked entry is registered. + if g_x86_big_exit_call_pos >= 0: + exi : ., mut = 0 + loop: + if exi >= g_x86_func_off_count: + break + if str_eq(istr_get(r64(g_x86_func_offsets, exi * 16)), + "bi_to_word_checked") != 0: + ex_off := r64(g_x86_func_offsets, exi * 16 + 8) + ex_target := 176 + ex_off + w32(buf, g_x86_big_exit_call_pos + 1, + ex_target - (g_x86_big_exit_call_pos + 5)) + break + exi = exi + 1 + // ── Patch forward calls using actual cp positions ── // Phase 3 stored cp at start of each function in g_x86_func_cp // Must run after ALL code emitted (incl alloc + sched_call) so func_offsets are final @@ -2379,6 +2443,11 @@ fn elf_gen(buf: string) -> int: total_sz := cp // Use actual total_sz as code_end so code segment covers all emitted content elf2_hdr(buf, total_sz, total_sz) + if g_x86_big_enabled != 0: + // The BigInt blob carries a few writable runtime words in the code + // segment. Mark that PT_LOAD RWX instead of adding another segment; + // user code itself remains the only executable content. + w32(buf, EHDR_SIZE + 4, 7) // Override entry point to bss_init (which zeroes heap then jumps to _start) w64(buf, E_ENTRY, TEXT_BASE + bss_init_cp) // Patch data segment VA to match bss_va (page after code+rodata) diff --git a/src/os/linux/entry.cr b/src/os/linux/entry.cr index 3b9b68e7..fa530d0e 100644 --- a/src/os/linux/entry.cr +++ b/src/os/linux/entry.cr @@ -114,6 +114,13 @@ fn emit_start(buf: string, pos: int) -> int: g_call_main_pos = cp cp = cp + e2_call(buf, cp, 0) // call main + if g_x86_big_enabled != 0: + // main's return value is a tagged mathematical int. Convert at the + // process boundary; sys_exit still takes a machine word. + cp = cp + e2_mov(buf, cp, 7, 0) // rdi = rax + g_x86_big_exit_call_pos = cp + cp = cp + e2_call(buf, cp, 0) // call bi_to_word_checked (patched later) + // mov edi, eax e2_w8(buf, cp, 137) cp = cp + 1 @@ -141,6 +148,9 @@ fn emit_start_size() -> int: // g_current_arena init: lea(7) + rex+mov+modrm+imm32(7) = 14 bytes if gv_current_arena >= 0: sz = sz + 14 + if g_x86_big_enabled != 0: + // mov rdi, rax (3) + call bi_to_word_checked (5) + sz = sz + 8 // Constant global initializers: lea r10(7) + mov imm32(7) = 14 bytes, // or mov rax imm64 + mov [r10],rax = 20 bytes for large values. gi0s : ., mut = 0 diff --git a/src/runtime/bigint.cr b/src/runtime/bigint.cr index 0d184fac..5bb6c134 100644 --- a/src/runtime/bigint.cr +++ b/src/runtime/bigint.cr @@ -29,6 +29,13 @@ BI_IMM_MIN : int = -4611686018427387904 g_bi_error : int, mut g_bi_rem : int, mut +fn bi_runtime_error(message: string): + // One shared runtime-error path for divide-by-zero and machine-word range + // failures. Keep the text here so the ELF and interpreter can converge on + // the same contract later. + syscall3(1, 2, message, str_len(message)) + syscall3(60, 1, 0, 0) + fn bi_is_big(v: int) -> int: if v % 2 != 0: return 1 @@ -87,6 +94,14 @@ fn bi_normalize(p: string) -> string: return p fn bi_from_imm(x: int) -> string: + if x == -9223372036854775808: + // -2^63 = -8 * 2^60. Negating INT64_MIN in a raw machine int + // would overflow back to itself. + p := bi_new(3) + bi_set_digit(p, 2, 8) + bi_set_sign(p, -1) + bi_set_len(p, 3) + return p p := bi_new(3) if x == 0: return p @@ -493,6 +508,7 @@ fn bi_to_dec(a: int) -> string: fn bi_divmod(a: int, b: int) -> int: if b == 0: + bi_runtime_error("division by zero\n") g_bi_error = 1 return 0 if a == 0: @@ -558,6 +574,7 @@ fn bi_to_word_checked(a: int) -> int: p := bi_as_big(a) n := bi_len_of(p) if n > 3: + bi_runtime_error("integer out of machine-word range\n") g_bi_error = 1 return 0 d0 := bi_digit(p, 0) @@ -568,6 +585,7 @@ fn bi_to_word_checked(a: int) -> int: if n > 2: d2 = bi_digit(p, 2) if d2 > 3: + bi_runtime_error("integer out of machine-word range\n") g_bi_error = 1 return 0 x := d0 + d1 * BI_BASE + d2 * BI_BASE2 @@ -580,6 +598,7 @@ fn bi_to_uword_checked(a: int) -> int: if g_bi_error != 0: return 0 if x < 0: + bi_runtime_error("integer out of unsigned machine-word range\n") g_bi_error = 1 return 0 return x diff --git a/src/runtime/bigint_blob.cr b/src/runtime/bigint_blob.cr new file mode 100644 index 00000000..4cc00053 --- /dev/null +++ b/src/runtime/bigint_blob.cr @@ -0,0 +1,233 @@ +// === bigint_blob.cr === +// Generated by tools/gen_bigint_blob.py from src/runtime/bigint.cr. +// Do not edit by hand. The blob is raw-core BigInt machine code plus +// its read-only and writable data; corearch copies it into the code segment. + +BIGINT_BLOB_SIZE : int = 18000 +BIGINT_BLOB_DATA_OFF : int = 17928 +BIGINT_BLOB_DATA_LEN : int = 72 + +fn bigint_blob_hex() -> string: + return "554889e54883ec5048897df848c745f00100000048c745e802000000488b7df8488b47f84883e801488945e0488b7df0488b75e8488b55f8488b4de04889f84889f74889d64889ca0f05488945d848c745d03c00000048c745c80100000048c745c00000000048c745b800000000488b7dd0488b75c8488b55c0488b4db84889f84889f74889d64889ca0f05488945b0488b45b04883c4505dc3554889e54883ec3848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989d24c8955e848c745e0000000004c8b55e84c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d001000000488b45d04883c4385dc348c745c800000000488b45c84883c4385dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989c24c8955e8488b45e84883c4185dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04d0fafd34c8955e8488b45e84883c4185dc3554889e54883ec2848897df84c8d15ef4300004c8955f04c8b55f04c8955e848c745e0010000004c8b55f84c8b5de04d29da4c8955d84c8b55d84c8955e8488b45e84883c4285dc3554889e54883ec2848897df848c745f0000000004c8b55f04c8955e84c8b55f84c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b45d84883c4285dc3554889e54883ec2848897df848c745f0080000004c8b55f84c8b5df04d0fafd34c8955e84c8b15ea4300004c8b5de84d01da4c8955e0488b7de0e800000000488945d8488b45d84883c4285dc3554889e54883ec1048897df8488b7df8488b35a1430000488b0437488945f0488b45f04883c4105dc3554889e54883ec1048897df8488b7df8488b3580430000488b0437488945f0488b45f04883c4105dc3554889e54883ec3048897df8488975f048c745e8080000004c8b55f04c8b5de84d0fafd34c8955e04c8b15474300004c8b5de04d01da4c8955d8488b7df8488b75d8488b0437488945d0488b45d04883c4305dc3554889e54883ec1848897df8488975f0488b7df8488b35f7420000488b55f048891437488b45e84883c4185dc3554889e54883ec1848897df8488975f0488b7df8488b35d2420000488b55f048891437488b45e84883c4185dc3554889e54883ec3848897df8488975f0488955e848c745e0080000004c8b55f04c8b5de04d0fafd34c8955d84c8b15954200004c8b5dd84d01da4c8955d0488b7df8488b75d0488b55e848891437488b45c84883c4385dc3554889e54881ec8800000048897df8488b7df8e8b9feffff488945f04c8b55f04c8955e8eb00eb0048c745e0000000004c8b55e84c8b5de04d39da0f9ec04c0fb6d04c8955d84c8b55d84983fa017402eb02eb7848c745d0010000004c8b55e84c8b5dd04d29da4c8955c8488b7df8488b75c8e882feffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f95c04c0fb6d04c8955b04c8b55b04983fa017402eb02eb2448c745a8010000004c8b55e84c8b5da84d29da4c8955a04c8b55a04c8955e8e95affffff488b7df8488b75e8e8a2feffff4889459848c74590000000004c8b55e84c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb1e48c7458000000000488b7df8488b7580e832feffff48898578ffffffeb00488b45f84881c4880000005dc3554889e54881ec3801000048897df849ba00000000000000804c8955f04c8b55f049f7da4c8955e84c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017405e98c00000048c745d803000000488b7dd8e8cbfcffff488945d04c8b55d04c8955c848c745c00200000048c745b808000000488b7dc8488b75c0488b55b8e8ebfdffff488945b048c745a8010000004c8b55a849f7da4c8955a0488b7dc8488b75a0e86dfdffff4889459848c7459003000000488b7dc8488b7590e881fdffff48894588488b45c84881c4380100005dc348c7458003000000488b7d80e83ffcffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b55f84c8b9d68ffffff4d39da0f94c04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb10488b8570ffffff4881c4380100005dc34c8b55f84c899558ffffff48c78550ffffff010000004c8b9550ffffff4c899548ffffff48c78540ffffff000000004c8b9558ffffff4c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb4b48c78530ffffff010000004c8b9530ffffff49f7da4c899528ffffff4c8b9528ffffff4c899548ffffff4c8b9558ffffff49f7da4c899520ffffff4c8b9520ffffff4c899558ffffffeb0048c78518ffffff000000004c8b9518ffffff4c899510ffffffeb00eb0048c78508ffffff000000004c8b9558ffffff4c8b9d08ffffff4d39da0f94c04c0fb6d04c899500ffffff4c8b9500ffffff4983fa017402eb05e9a50000004c8b9558ffffff4c8b1df03e00004c89d0489949f7fb4989d24c8995f8feffff488bbd70ffffff488bb510ffffff488b95f8feffffe8f4fbffff488985f0feffff4c8b9558ffffff4c8b1daf3e00004c89d0489949f7fb4989c24c8995e8feffff4c8b95e8feffff4c899558ffffff48c785e0feffff010000004c8b9510ffffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c899510ffffffe91bffffff488bbd70ffffff488bb548ffffffe81cfbffff488985d0feffff488bbd70ffffff488bb510ffffffe82ffbffff488985c8feffff488b8570ffffff4881c4380100005dc3554889e54883ec3848897df8488b7df8e87af8ffff488945f048c745e8000000004c8b55f04c8b5de84d39da0f95c04c0fb6d04c8955e04c8b55e04983fa017402eb17488b7df8e81ff9ffff488945d8488b45d84883c4385dc3488b7df8e8a5f8ffff488945d0488b7dd0e85ffcffff488945c8488b45c84883c4385dc3554889e54881ec5001000048897df8488b7df8e807fbffff488945f0488b7df8e8cbf9ffff488945e84c8b55e84c8955e048c745d8000000004c8b55e04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb1548c745c800000000488b45c84881c4500100005dc348c745c0030000004c8b55e04c8b5dc04d39da0f9fc04c0fb6d04c8955b84c8b55b84983fa017402eb1a488b7df8e892f8ffff488945b0488b45b04881c4500100005dc348c745a800000000488b7df8488b75a8e850f9ffff488945a04c8b55a04c89559848c74590000000004c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff010000004c8b55e04c8b9d70ffffff4d39da0f9fc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb2f48c78560ffffff01000000488b7df8488bb560ffffffe8d0f8ffff48898558ffffff4c8b9558ffffff4c895588eb0048c78550ffffff020000004c8b55e04c8b9d50ffffff4d39da0f9fc04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb3248c78540ffffff02000000488b7df8488bb540ffffffe86bf8ffff48898538ffffff4c8b9538ffffff4c899578ffffffeb0048c78530ffffff030000004c8b9578ffffff4c8b9d30ffffff4d39da0f9fc04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017402eb20488b7df8e82ef7ffff48898520ffffff488b8520ffffff4881c4500100005dc34c8b55884c8b1d6e3b00004d0fafd34c899518ffffff4c8b55984c8b9d18ffffff4d01da4c899510ffffff4c8b9578ffffff4c8b1d483b00004d0fafd34c899508ffffff4c8b9510ffffff4c8b9d08ffffff4d01da4c899500ffffff4c8b9500ffffff4c8995f8feffff488b7df8e836f7ffff488985f0feffff48c785e8feffff000000004c8b95f0feffff4c8b9de8feffff4d39da0f9cc04c0fb6d04c8995e0feffff4c8b95e0feffff4983fa017402eb214c8b95f8feffff49f7da4c8995d8feffff4c8b95d8feffff4c8995f8feffffeb004c8b95f8feffff4c8b1daf3a00004d39da0f9cc04c0fb6d04c8995d0feffff4c8b95f8feffff4c8b1d883a00004d39da0f9fc04c0fb6d04c8995c8feffff4c8b95d0feffff4c8b9dc8feffff4d09da4c8995c0feffff4c8b95c0feffff4983fa017402eb20488b7df8e8d5f5ffff488985b8feffff488b85b8feffff4881c4500100005dc3488bbdf8feffffe83cf5ffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54883ec7848897df8488b7df8e841f6ffff488945f04c8b55f04c8955e8488b7de8e8b6f5ffff488945e04c8b55e04c8955d8488b7df8e8eef5ffff488945d0488b7dd8488b75d0e883f6ffff488945c8488b7dd8488b75e8e89ff6ffff488945c048c745b8000000004c8b55b84c8955b0eb00eb004c8b55b04c8b5de84d39da0f9dc04c0fb6d04c8955a84c8b55a84983fa017402eb02eb47488b7df8488b75b0e8d5f5ffff488945a0488b7dd8488b75b0488b55a0e86ef6ffff4889459848c74590010000004c8b55b04c8b5d904d01da4c8955884c8b55884c8955b0eb93488b45d84883c4785dc3554889e54881ecd000000048897df8488975f0488b7df8e84cf5ffff488945e84c8b55e84c8955e0488b7df0e837f5ffff488945d84c8b55d84c8955d04c8b55e04c8b5dd04d39da0f9cc04c0fb6d04c8955c84c8b55c84983fa017402eb2048c745c0010000004c8b55c049f7da4c8955b8488b45b84881c4d00000005dc34c8b55e04c8b5dd04d39da0f9fc04c0fb6d04c8955b04c8b55b04983fa017402eb1548c745a801000000488b45a84881c4d00000005dc34c8b55e04c8955a0eb00eb0048c74598000000004c8b55a04c8b5d984d39da0f9ec04c0fb6d04c8955904c8b55904983fa017402eb05e90b01000048c74588010000004c8b55a04c8b5d884d29da4c8955804c8b55804c8955a0488b7df8488b75a0e874f4ffff48898578ffffff4c8b9578ffffff4c899570ffffff488b7df0488b75a0e852f4ffff48898568ffffff4c8b9568ffffff4c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d39da0f9cc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb2c48c78550ffffff010000004c8b9550ffffff49f7da4c899548ffffff488b8548ffffff4881c4d00000005dc34c8b9570ffffff4c8b9d60ffffff4d39da0f9fc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb1b48c78538ffffff01000000488b8538ffffff4881c4d00000005dc3e9c4feffff48c78530ffffff00000000488b8530ffffff4881c4d00000005dc3554889e54881ecc800000048897df8488975f0488b7df8e8c7f8ffff488945e84c8b55e84c8955e0488b7df0e8b2f8ffff488945d84c8b55d84c8955d0488b7de0e8e2f2ffff488945c84c8b55c84c8955c0488b7dd0e8cdf2ffff488945b84c8b55b84c8955b04c8b55c04c8b5db04d39da0f9cc04c0fb6d04c8955a84c8b55a84983fa017402eb2048c745a0010000004c8b55a049f7da4c895598488b45984881c4c80000005dc34c8b55c04c8b5db04d39da0f9fc04c0fb6d04c8955904c8b55904983fa017402eb1548c7458801000000488b45884881c4c80000005dc348c74580000000004c8b55c04c8b5d804d39da0f94c04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb1b48c78570ffffff00000000488b8570ffffff4881c4c80000005dc3488b7de0488b75d0e8b1fcffff48898568ffffff4c8b9568ffffff4c899560ffffff48c78558ffffff000000004c8b55c04c8b9d58ffffff4d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb344c8b9560ffffff49f7da4c899548ffffff488bbd48ffffffe880f0ffff48898540ffffff488b8540ffffff4881c4c80000005dc3488bbd60ffffffe85df0ffff48898538ffffff488b8538ffffff4881c4c80000005dc3554889e54881ec5001000048897df8488975f0488b7df8e85bf1ffff488945e84c8b55e84c8955e0488b7df0e846f1ffff488945d84c8b55d84c8955d04c8b55e04c8955c84c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb0a4c8b55d04c8955c8eb0048c745b8010000004c8b55c84c8b5db84d01da4c8955b0488b7db0e870f0ffff488945a84c8b55a84c8955a048c74598000000004c8b55984c89559048c74588000000004c8b55884c895580eb00eb004c8b55804c8b5dc84d39da0f9dc04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb05e99b01000048c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffff4c8b55804c8b5de04d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb24488b7df8488b7580e84bf0ffff48898548ffffff4c8b9548ffffff4c899568ffffffeb004c8b55804c8b5dd04d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb24488b7df0488b7580e8ffefffff48898538ffffff4c8b9538ffffff4c899558ffffffeb004c8b9568ffffff4c8b9d58ffffff4d01da4c899530ffffff4c8b9530ffffff4c8b5d904d01da4c899528ffffff4c8b9528ffffff4c899520ffffff4c8b9520ffffff4c8b1d1d3300004c89d0489949f7fb4989d24c899518ffffff488b7da0488b7580488b9518ffffffe827f0ffff48898510ffffff4c8b9520ffffff4c8b1de23200004c89d0489949f7fb4989c24c899508ffffff4c8b9508ffffff4c89559048c78500ffffff010000004c8b55804c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c895580e936feffff48c785f0feffff000000004c8b55904c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb45488b7da0488b75c8488b5590e87eefffff488985e0feffff48c785d8feffff010000004c8b55c84c8b9dd8feffff4d01da4c8995d0feffff4c8b95d0feffff4c8955c8eb00488b7da0488b75c8e810efffff488985c8feffff48c785c0feffff01000000488b7da0488bb5c0feffffe8c1eeffff488985b8feffff488b7da0e863efffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54881ec1001000048897df8488975f0488b7df8e801eeffff488945e84c8b55e84c8955e0488b7df0e8ecedffff488945d84c8b55d84c8955d0488b7de0e861edffff488945c84c8b55c84c8955c048c745b8000000004c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb004c8b55a04c8b5de04d39da0f9dc04c0fb6d04c8955984c8b55984983fa017402eb05e98a010000488b7df8488b75a0e89cedffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff4c8b55a04c8b5dd04d39da0f9cc04c0fb6d04c899570ffffff4c8b9570ffffff4983fa017402eb24488b7df0488b75a0e848edffff48898568ffffff4c8b9568ffffff4c899578ffffffeb004c8b55884c8b9d78ffffff4d29da4c899560ffffff4c8b9560ffffff4c8b5db04d29da4c899558ffffff4c8b9558ffffff4c899550ffffff48c78548ffffff000000004c8b9550ffffff4c8b9d48ffffff4d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb3e4c8b9550ffffff4c8b1d303000004d01da4c899538ffffff4c8b9538ffffff4c899550ffffff48c78530ffffff010000004c8b9530ffffff4c8955b0eb1848c78528ffffff000000004c8b9528ffffff4c8955b0eb00488b7dc0488b75a0488b9550ffffffe804edffff48898520ffffff48c78518ffffff010000004c8b55a04c8b9d18ffffff4d01da4c899510ffffff4c8b9510ffffff4c8955a0e94dfeffff488b7dc0488b75e0e893ecffff48898508ffffff48c78500ffffff01000000488b7dc0488bb500ffffffe844ecffff488985f8feffff488b7dc0e8e6ecffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec1001000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb0d488b45f04881c4100100005dc348c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb0d488b45f84881c4100100005dc3488b7df8e8a8f0ffff488945c84c8b55c84c8955c0488b7df0e893f0ffff488945b84c8b55b84c8955b0488b7dc0e8c3eaffff488945a84c8b55a84c8955a0488b7db0e8aeeaffff488945984c8b55984c8955904c8b55a04c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb56488b7dc0488b75b0e825f9ffff488945804c8b55804c899578ffffff488bbd78ffffff488b75a0e8faeaffff48898570ffffff488bbd78ffffffe87af0ffff48898568ffffff488b8568ffffff4881c4100100005dc3488b7dc0488b75b0e8def4ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9558ffffff4c8b9d50ffffff4d39da0f94c04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb1b48c78540ffffff00000000488b8540ffffff4881c4100100005dc348c78538ffffff000000004c8b9558ffffff4c8b9d38ffffff4d39da0f9fc04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb5c488b7dc0488b75b0e87afbffff48898528ffffff4c8b9528ffffff4c899520ffffff488bbd20ffffff488b75a0e8efe9ffff48898518ffffff488bbd20ffffffe86fefffff48898510ffffff488b8510ffffff4881c4100100005dc3488b7db0488b75c0e81efbffff48898508ffffff4c8b9508ffffff4c899500ffffff488bbd00ffffff488b7590e893e9ffff488985f8feffff488bbd00ffffffe813efffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec9000000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1548c745e000000000488b45e04881c4900000005dc3488b7df8e8b6e6ffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb32488b7df8e8f8e6ffff488945c04c8b55c049f7da4c8955b8488b7db8e815e7ffff488945b0488b45b04881c4900000005dc3488b7df8e829e7ffff488945a84c8b55a84c8955a0488b7da0e8c1f1ffff488945984c8b55984c895590488b7da0e8d9e7ffff488945884c8b558849f7da4c895580488b7d90488b7580e863e8ffff48898578ffffff488b7d90e8e6edffff48898570ffffff488b8570ffffff4881c4900000005dc3554889e54883ec2048897df8488975f0488b7df0e8bafeffff488945e8488b7df8488b75e8e8f5fbffff488945e0488b45e04883c4205dc3554889e54881ec1802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4180200005dc3488b7df8e891ecffff488945b84c8b55b84c8955b0488b7df0e87cecffff488945a84c8b55a84c8955a0488b7db0e8d5e6ffff488945984c8b55984c895590488b7da0e8c0e6ffff488945884c8b55884c8955804c8b55904c8b5d804d01da4c899578ffffff488bbd78ffffffe820e6ffff48898570ffffff4c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffffeb00eb004c8b9558ffffff4c8b5d904d39da0f9dc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb05e96803000048c78548ffffff000000004c8b9548ffffff4c899540ffffff488b7db0488bb558ffffffe834e6ffff48898538ffffff4c8b9538ffffff4c899530ffffff48c78528ffffff000000004c8b9528ffffff4c899520ffffffeb00eb004c8b9520ffffff4c8b5d804d39da0f9dc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb05e95d0100004c8b9558ffffff4c8b9d20ffffff4d01da4c899510ffffff488bbd68ffffff488bb510ffffffe8a7e5ffff48898508ffffff488b7da0488bb520ffffffe890e5ffff48898500ffffff4c8b9530ffffff4c8b9d00ffffff4d0fafd34c8995f8feffff4c8b9508ffffff4c8b9df8feffff4d01da4c8995f0feffff4c8b95f0feffff4c8b9d40ffffff4d01da4c8995e8feffff4c8b95e8feffff4c8995e0feffff4c8b9558ffffff4c8b9d20ffffff4d01da4c8995d8feffff4c8b95e0feffff4c8b1d8a2800004c89d0489949f7fb4989d24c8995d0feffff488bbd68ffffff488bb5d8feffff488b95d0feffffe88ee5ffff488985c8feffff4c8b95e0feffff4c8b1d492800004c89d0489949f7fb4989c24c8995c0feffff4c8b95c0feffff4c899540ffffff48c785b8feffff010000004c8b9520ffffff4c8b9db8feffff4d01da4c8995b0feffff4c8b95b0feffff4c899520ffffffe971feffff4c8b9558ffffff4c8b5d804d01da4c8995a8feffff4c8b95a8feffff4c8995a0feffffeb00eb0048c78598feffff000000004c8b9540ffffff4c8b9d98feffff4d39da0f94c04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9e5000000488bbd68ffffff488bb5a0feffffe8fde3ffff48898588feffff4c8b9588feffff4c8b9d40ffffff4d01da4c899580feffff4c8b9580feffff4c899578feffff4c8b9578feffff4c8b1d402700004c89d0489949f7fb4989d24c899570feffff488bbd68ffffff488bb5a0feffff488b9570feffffe844e4ffff48898568feffff4c8b9578feffff4c8b1dff2600004c89d0489949f7fb4989c24c899560feffff4c8b9560feffff4c899540ffffff48c78558feffff010000004c8b95a0feffff4c8b9d58feffff4d01da4c899550feffff4c8b9550feffff4c8995a0feffffe9dbfeffff48c78548feffff010000004c8b9558ffffff4c8b9d48feffff4d01da4c899540feffff4c8b9540feffff4c899558ffffffe966fcffff4c8b55904c8b5d804d01da4c899538feffff488bbd68ffffff488bb538feffffe851e3ffff48898530feffff48c78528feffff010000004c8b9528feffff4c899520feffff488b7db0e855e2ffff48898518feffff488b7da0e845e2ffff48898510feffff4c8b9518feffff4c8b9d10feffff4d39da0f95c04c0fb6d04c899508feffff4c8b9508feffff4983fa017402eb2c48c78500feffff010000004c8b9500feffff49f7da4c8995f8fdffff4c8b95f8fdffff4c899520feffffeb00488bbd68ffffff488bb520feffffe877e2ffff488985f0fdffff488bbd68ffffffe8f7e7ffff488985e8fdffff488b85e8fdffff4881c4180200005dc3554889e54883ec7848897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1248c745e001000000488b45e04883c4785dc3488b7df8e8a0dfffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8e833e0ffff488945b84c8b55b84c8955b0488b7db0e821e1ffff488945a848c745a0000000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55984983fa017402eb1248c7459001000000488b45904883c4785dc348c7458800000000488b45884883c4785dc3554889e54883ec5048897df848c745f0010000004c8b55f04c8955e848c745e0000000004c8b55e04c8955d8eb00eb004c8b55d84c8b5df84d39da0f9dc04c0fb6d04c8955d04c8b55d04983fa017402eb02eb4148c745c8020000004c8b55e84c8b5dc84d0fafd34c8955c04c8b55c04c8955e848c745b8010000004c8b55d84c8b5db84d01da4c8955b04c8b55b04c8955d8eb99488b45e84883c4505dc3554889e54881ecb800000048897df8488b7df8e818e0ffff488945f04c8b55f04c8955e848c745e0000000004c8b55e84c8b5de04d39da0f94c04c0fb6d04c8955d84c8b55d84983fa017402eb1548c745d000000000488b45d04881c4b80000005dc348c745c8010000004c8b55e84c8b5dc84d29da4c8955c0488b7df8488b75c0e8d2dfffff488945b84c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb0048c74598000000004c8b55b04c8b5d984d39da0f94c04c0fb6d04c8955904c8b55904983fa017402eb02eb5748c74588020000004c8b55b04c8b5d884c89d0489949f7fb4989c24c8955804c8b55804c8955b048c78578ffffff010000004c8b55a04c8b9d78ffffff4d01da4c899570ffffff4c8b9570ffffff4c8955a0e97bffffff48c78568ffffff010000004c8b55e84c8b9d68ffffff4d29da4c899560ffffff48c78558ffffff1e0000004c8b9560ffffff4c8b9d58ffffff4d0fafd34c899550ffffff4c8b9550ffffff4c8b5da04d01da4c899548ffffff488b8548ffffff4881c4b80000005dc3554889e54883ec7848897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e85ddeffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8488b75d8e841deffff488945b848c745b01e0000004c8b55f04c8b5db04c89d0489949f7fb4989d24c8955a8488b7da8e81dfdffff488945a04c8b55b84c8b5da04c89d0489949f7fb4989c24c89559848c74590020000004c8b55984c8b5d904c89d0489949f7fb4989d24c895588488b45884883c4785dc3554889e54883ec7048897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e868ddffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb064883c4705dc3488b7df8488b75d8e858ddffff488945c048c745b81e0000004c8b55f04c8b5db84c89d0489949f7fb4989d24c8955b0488b7db0e834fcffff488945a84c8b55c04c8b5da84d01da4c8955a04c8b55a04c895598488b7df8488b75d8488b5598e8aeddffff48894590488b45904883c4705dc3554889e54881ec0001000048897df8488b7df8e8b1dcffff488945f04c8b55f04c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b7dd8e80fdcffff488945d04c8b55d04c8955c848c745c0000000004c8b55c04c8955b848c745b0000000004c8b55b04c8955a8eb00eb004c8b55a84c8b5de84d39da0f9dc04c0fb6d04c8955a04c8b55a04983fa017402eb05e9d9000000488b7df8488b75a8e84adcffff4889459848c74590020000004c8b55984c8b5d904d0fafd34c8955884c8b55884c8b5db84d01da4c8955804c8b55804c899578ffffff4c8b9578ffffff4c8b1d841f00004c89d0489949f7fb4989d24c899570ffffff488b7dc8488b75a8488b9570ffffffe88edcffff48898568ffffff4c8b9578ffffff4c8b1d491f00004c89d0489949f7fb4989c24c899560ffffff4c8b9560ffffff4c8955b848c78558ffffff010000004c8b55a84c8b9d58ffffff4d01da4c899550ffffff4c8b9550ffffff4c8955a8e9fefeffff488b7dc8488b75e8e8f2dbffff48898548ffffff48c78540ffffff000000004c8b55b84c8b9d40ffffff4d39da0f95c04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb51488b7dc8488b75e8488b55b8e8d1dbffff48898530ffffff48c78528ffffff010000004c8b55e84c8b9d28ffffff4d01da4c899520ffffff488b7dc8488bb520ffffffe86ddbffff48898518ffffffeb0048c78510ffffff01000000488b7dc8488bb510ffffffe81cdbffff48898508ffffff488b7dc8e8bedbffff48898500ffffff488b8500ffffff4881c4000100005dc3554889e54883ec2048897df8488975f0488b7df0e82ed9ffff488945e8488b7df8488b75e8e8aeeeffff488945e0488b45e04883c4205dc3554889e54881ec0802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4080200005dc3488b7df8e8d9d7ffff488945b848c745b0000000004c8b55b84c8b5db04d39da0f94c04c0fb6d04c8955a84c8b55a84983fa017405e921020000488b7df8e818d8ffff488945a04c8b55a04c89559848c74590000000004c8b55f04c8b5d904d39da0f9fc04c0fb6d04c8955884c8b150c1d00004c8b5df04c89d0489949f7fb4989c24c8955804c8b55984c8b5d804d39da0f9dc04c0fb6d04c899578ffffff4c8b55884c8b9d78ffffff4d21da4c899570ffffff4c8b15bc1c00004c8b5df04c89d0489949f7fb4989c24c899568ffffff4c8b55984c8b9d68ffffff4d39da0f9ec04c0fb6d04c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d21da4c899558ffffff4c8b9558ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899550ffffff488bbd50ffffffe85cd7ffff48898548ffffff488b8548ffffff4881c4080200005dc348c78540ffffff000000004c8b55f04c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b15071c00004c8b5df04c89d0489949f7fb4989c24c899530ffffff4c8b55984c8b9d30ffffff4d39da0f9ec04c0fb6d04c899528ffffff4c8b9538ffffff4c8b9d28ffffff4d21da4c899520ffffff4c8b15ae1b00004c8b5df04c89d0489949f7fb4989c24c899518ffffff4c8b55984c8b9d18ffffff4d39da0f9dc04c0fb6d04c899510ffffff4c8b9520ffffff4c8b9d10ffffff4d21da4c899508ffffff4c8b9508ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899500ffffff488bbd00ffffffe84ed6ffff488985f8feffff488b85f8feffff4881c4080200005dc3eb00488b7df8e8efdcffff488985f0feffff4c8b95f0feffff4c8995e8feffff488bbde8feffffe83cd7ffff488985e0feffff4c8b95e0feffff4c8995d8feffff48c785d0feffff010000004c8b95d8feffff4c8b9dd0feffff4d01da4c8995c8feffff488bbdc8feffffe882d6ffff488985c0feffff4c8b95c0feffff4c8995b8feffff48c785b0feffff000000004c8b95b0feffff4c8995a8feffff48c785a0feffff000000004c8b95a0feffff4c899598feffffeb00eb004c8b9598feffff4c8b9dd8feffff4d39da0f9dc04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9fb000000488bbde8feffff488bb598feffffe890d6ffff48898588feffff4c8b9588feffff4c8b5df04d0fafd34c899580feffff4c8b9580feffff4c8b9da8feffff4d01da4c899578feffff4c8b9578feffff4c899570feffff4c8b9570feffff4c8b1dbd1900004c89d0489949f7fb4989d24c899568feffff488bbdb8feffff488bb598feffff488b9568feffffe8c1d6ffff48898560feffff4c8b9570feffff4c8b1d7c1900004c89d0489949f7fb4989c24c899558feffff4c8b9558feffff4c8995a8feffff48c78550feffff010000004c8b9598feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899598feffffe9d0feffff488bbdb8feffff488bb5d8feffffe816d6ffff48898540feffff48c78538feffff000000004c8b95a8feffff4c8b9d38feffff4d39da0f95c04c0fb6d04c899530feffff4c8b9530feffff4983fa017402eb60488bbdb8feffff488bb5d8feffff488b95a8feffffe8e9d5ffff48898528feffff48c78520feffff010000004c8b95d8feffff4c8b9d20feffff4d01da4c899518feffff488bbdb8feffff488bb518feffffe87fd5ffff48898510feffffeb00488bbde8feffffe897d4ffff48898508feffff488bbdb8feffff488bb508feffffe823d5ffff48898500feffff488bbdb8feffffe8a3daffff488985f8fdffff488b85f8fdffff4881c4080200005dc3554889e54881ec3801000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb3b48c745d8010000004c8b55d84c8915f317000048c745d0000000004c8b55d04c8915e817000048c745c800000000488b45c84881c4380100005dc3488b7df8e81cd2ffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb68488b7df8e85ed2ffff488945a84c8b55a84c8955a04c8b55a04c8b5df04c89d0489949f7fb4989d24c8955984c8b55984c8915651700004c8b55a04c8b5df04c89d0489949f7fb4989c24c895590488b7d90e845d2ffff48894588488b45884881c4380100005dc3488b7df8e8eed8ffff488945804c8b55804c899578ffffff488bbd78ffffffe841d3ffff48898570ffffff4c8b9570ffffff4c899568ffffff488bbd68ffffffe8aad2ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9550ffffff4c899548ffffff4c8b9568ffffff4c899540ffffffeb00eb0048c78538ffffff000000004c8b9540ffffff4c8b9d38ffffff4d39da0f9ec04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb05e9f800000048c78528ffffff010000004c8b9540ffffff4c8b9d28ffffff4d29da4c899520ffffff4c8b9520ffffff4c899540ffffff4c8b9548ffffff4c8b1d0a1600004d0fafd34c899518ffffff488bbd78ffffff488bb540ffffffe86ed2ffff48898510ffffff4c8b9518ffffff4c8b9d10ffffff4d01da4c899508ffffff4c8b9508ffffff4c899500ffffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989c24c8995f8feffff488bbd58ffffff488bb540ffffff488b95f8feffffe8b8d2ffff488985f0feffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989d24c8995e8feffff4c8b95e8feffff4c899548ffffffe9c8feffff488bbd58ffffff488bb568ffffffe841d2ffff488985e0feffff488bbd78ffffffe85bd1ffff488985d8feffff488bbd58ffffff488bb5d8feffffe8e7d1ffff488985d0feffff4c8b9548ffffff4c891524150000488bbd58ffffffe859d7ffff488985c8feffff488b85c8feffff4881c4380100005dc3554889e54881ec2001000048897df8488b7df8488b47f84883e801488945f04c8b55f04c8955e848c745e0000000004c8b55e04c8955d848c745d0000000004c8b55d04c8955c848c745c0000000004c8b55e84c8b5dc04d39da0f9fc04c0fb6d04c8955b848c745b000000000488b7df8488b75b0480fb60437488945a848c745a02d0000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55b84c8b5d984d21da4c8955904c8b55904983fa017402eb2248c74588010000004c8b55884c8955c848c74580010000004c8b55804c8955d8eb0048c78578ffffff000000004c8b9578ffffff4c899570ffffffeb00eb004c8b55d84c8b5de84d39da0f9dc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb05e950010000488b7df8488b75d8480fb6043748898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff300000004c8b9558ffffff4c8b9d50ffffff4d39da0f9cc04c0fb6d04c899548ffffff48c78540ffffff390000004c8b9558ffffff4c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9548ffffff4c8b9d38ffffff4d09da4c899530ffffff4c8b9530ffffff4983fa017402eb05e9ae00000048c78528ffffff0a000000488bbd70ffffff488bb528ffffffe80ff5ffff48898520ffffff4c8b9520ffffff4c899570ffffff48c78518ffffff300000004c8b9558ffffff4c8b9d18ffffff4d29da4c899510ffffff488bbd70ffffff488bb510ffffffe88cf4ffff48898508ffffff4c8b9508ffffff4c899570ffffff48c78500ffffff010000004c8b55d84c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c8955d8e981feffff48c785f0feffff000000004c8b55c84c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb23488bbd70ffffffe891e5ffff488985e0feffff4c8b95e0feffff4c899570ffffffeb00488b8570ffffff4881c4200100005dc3554889e54881ec1802000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb5148c745e002000000488b7de0e800000000488945d84c8b55d84c8955d048c745c80000000048c745c030000000488b7dd0488b75c8488b55c088143748c745b800000000488b45d04881c4180200005dc348c745b0000000004c8b55b04c8955a8488b7df8e845d3ffff488945a04c8b55a04c895598488b7d98e875cdffff4889459048c74588000000004c8b55904c8b5d884d39da0f9cc04c0fb6d04c8955804c8b55804983fa017402eb4648c78578ffffff010000004c8b9578ffffff4c8955a8488b7df8e865e4ffff48898570ffffff488bbd70ffffffe8d0d2ffff48898568ffffff4c8b9568ffffff4c895598eb00488b7d98e821cdffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff0a0000004c8b9558ffffff4c8b9d50ffffff4d0fafd34c899548ffffff48c78540ffffff040000004c8b9548ffffff4c8b9d40ffffff4d01da4c899538ffffff4c8b9538ffffff4c899530ffffff488bbd30ffffffe80000000048898528ffffff4c8b9528ffffff4c899520ffffff48c78518ffffff010000004c8b9530ffffff4c8b9d18ffffff4d29da4c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff00000000488bbd20ffffff488bb508ffffff488b9500ffffff88143748c785f8feffff00000000488b7d98e83ed2ffff488985f0feffff4c8b95f0feffff4c8995e8feffffeb00eb0048c785e0feffff000000004c8b95e8feffff4c8b9de0feffff4d39da0f94c04c0fb6d04c8995d8feffff4c8b95d8feffff4983fa017402eb05e9bd00000048c785d0feffff0a000000488bbde8feffff488bb5d0feffffe83df7ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b9508ffffff4c8b9db8feffff4d29da4c8995b0feffff4c8b95b0feffff4c899508ffffff48c785a8feffff300000004c8b15300f00004c8b9da8feffff4d01da4c8995a0feffff488bbd20ffffff488bb508ffffff488b95a0feffff88143748c78598feffff000000004c8b95c0feffff4c8995e8feffffe903ffffff48c78590feffff000000004c8b55a84c8b9d90feffff4d39da0f95c04c0fb6d04c899588feffff4c8b9588feffff4983fa017402eb6148c78580feffff010000004c8b9508ffffff4c8b9d80feffff4d29da4c899578feffff4c8b9578feffff4c899508ffffff48c78570feffff2d000000488bbd20ffffff488bb508ffffff488b9570feffff88143748c78568feffff00000000eb0048c78560feffff010000004c8b9530ffffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c8b9d08ffffff4d29da4c899550feffff4c8b9550feffff4c899548feffff48c78540feffff010000004c8b9548feffff4c8b9d40feffff4d01da4c899538feffff488bbd38feffffe80000000048898530feffff4c8b9530feffff4c899528feffff48c78520feffff000000004c8b9520feffff4c899518feffffeb00eb004c8b9518feffff4c8b9d48feffff4d39da0f9dc04c0fb6d04c899510feffff4c8b9510feffff4983fa017402eb05e98b0000004c8b9508ffffff4c8b9d18feffff4d01da4c899508feffff488bbd20ffffff488bb508feffff480fb6043748898500feffff488bbd28feffff488bb518feffff488b9500feffff88143748c785f8fdffff0000000048c785f0fdffff010000004c8b9518feffff4c8b9df0fdffff4d01da4c8995e8fdffff4c8b95e8fdffff4c899518feffffe940ffffff488b8528feffff4881c4180200005dc3554889e54881ec5802000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb404c8d15d60b00004c8955d8488b7dd8e841c6ffff488945d048c745c8010000004c8b55c84c89156a0c000048c745c000000000488b45c04881c4580200005dc348c745b8000000004c8b55f84c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb2848c745a8000000004c8b55a84c8915200c000048c745a000000000488b45a04881c4580200005dc3488b7df8e8c5cdffff488945984c8b55984c895590488b7df0e8b0cdffff488945884c8b55884c895580488b7d90488b7580e89dd2ffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b9570ffffff4c8b9d68ffffff4d39da0f9cc04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb264c8b55f84c89157b0b000048c78558ffffff00000000488b8558ffffff4881c4580200005dc3488b7d90e85fc7ffff48898550ffffff4c8b9550ffffff4c899548ffffff488b7d80e841c7ffff48898540ffffff4c8b9540ffffff4c899538ffffff48c78530ffffff000000004c8b9570ffffff4c8b9d30ffffff4d39da0f94c04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017405e9af00000048c78520ffffff010000004c8b9520ffffff4c899518ffffff4c8b9548ffffff4c8b9d38ffffff4d39da0f95c04c0fb6d04c899510ffffff4c8b9510ffffff4983fa017402eb2c48c78508ffffff010000004c8b9508ffffff49f7da4c899500ffffff4c8b9500ffffff4c899518ffffffeb0048c785f8feffff000000004c8b95f8feffff4c89155c0a0000488bbd18ffffffe850c5ffff488985f0feffff488b85f0feffff4881c4580200005dc3488b7d90e861c6ffff488985e8feffff4c8b95e8feffff4c8995e0feffff48c785d8feffff010000004c8b95e0feffff4c8b9dd8feffff4d01da4c8995d0feffff488bbdd0feffffe8a7c5ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b95e0feffff4c8b9db8feffff4d01da4c8995b0feffff488bbdc0feffff488bb5b0feffffe87cc6ffff488985a8feffff48c785a0feffff00000000488bbda0feffffe815c8ffff48898598feffff4c8b9598feffff4c899590feffff488b7d90e866e5ffff48898588feffff4c8b9588feffff4c899580feffff4c8b9580feffff4c899578feffffeb00eb0048c78570feffff000000004c8b9578feffff4c8b9d70feffff4d39da0f9ec04c0fb6d04c899568feffff4c8b9568feffff4983fa017402eb05e99301000048c78560feffff010000004c8b9578feffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c899578feffff488bbd90feffffe82be8ffff48898550feffff4c8b9550feffff4c899590feffff488b7d90488bb578feffffe832e6ffff48898548feffff48c78540feffff000000004c8b9548feffff4c8b9d40feffff4d39da0f95c04c0fb6d04c899538feffff4c8b9538feffff4983fa017402eb5b488bbd90feffffe893caffff48898530feffff48c78528feffff01000000488bbd30feffff488bb528feffffe8e6e9ffff48898520feffff488bbd20feffffe8ddc9ffff48898518feffff4c8b9518feffff4c899590feffffeb00488bbd90feffff488b7580e8bcceffff48898510feffff48c78508feffff000000004c8b9510feffff4c8b9d08feffff4d39da0f9dc04c0fb6d04c899500feffff4c8b9500feffff4983fa017402eb41488bbd90feffff488b7580e8b7d5ffff488985f8fdffff4c8b95f8fdffff4c899590feffff488bbdc0feffff488bb578feffffe804e6ffff488985f0fdffffeb00e92dfeffff48c785e8fdffff010000004c8b95e0feffff4c8b9de8fdffff4d01da4c8995e0fdffff488bbdc0feffff488bb5e0fdffffe812c4ffff488985d8fdffff4c8b9548ffffff4c8b9d38ffffff4d0fafd34c8995d0fdffff488bbdc0feffff488bb5d0fdffffe8b2c3ffff488985c8fdffff488bbdc0feffffe832c9ffff488985c0fdffff4c8b95c0fdffff4c8995b8fdffff488bbd90feffff488bb548ffffffe877c3ffff488985b0fdffff488bbd90feffffe8f7c8ffff488985a8fdffff4c8b95a8fdffff4c8915a1060000488b85b8fdffff4881c4580200005dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e89af9ffff488945e84c8b55e84c8955e0488b45e04883c4205dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e867f9ffff488945e84c8b55e84c8955e0488b052e0600004883c4205dc3554889e54883ec3848897df84c8b55f84c8b1d010600004d39da0f9dc04c0fb6d04c8955f04c8b55f84c8b1de00500004d39da0f9ec04c0fb6d04c8955e84c8b55f04c8b5de84d21da4c8955e04c8b55e04983fa017402eb17488b7df8e8c6c0ffff488945d8488b45d84883c4385dc3488b7df8e841c4ffff488945d0488b7dd0e8e3c7ffff488945c8488b45c84883c4385dc3554889e54883ec4048897df848c745f0000000004c8b55f84c8b5df04d39da0f9dc04c0fb6d04c8955e84c8b55f84c8b1d470500004d39da0f9ec04c0fb6d04c8955e04c8b55e84c8b5de04d21da4c8955d84c8b55d84983fa017402eb17488b7df8e82dc0ffff488945d0488b45d04883c4405dc3488b7df8e8a8c3ffff488945c8488b7dc8e84ac7ffff488945c0488b45c04883c4405dc3554889e54881ec5001000048897df848c745f0000000004c8b55f04c8915d1040000488b7df8e893c6ffff488945e84c8b55e84c8955e0488b7de0e8ecc0ffff488945d84c8b55d84c8955d048c745c8030000004c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb404c8d15de0300004c8955b8488b7db8e829beffff488945b048c745a8010000004c8b55a84c89155204000048c745a000000000488b45a04881c4500100005dc348c7459800000000488b7de0488b7598e88ac0ffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff010000004c8b55d04c8b9d60ffffff4d39da0f9fc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb3248c78550ffffff01000000488b7de0488bb550ffffffe801c0ffff48898548ffffff4c8b9548ffffff4c899578ffffffeb0048c78540ffffff020000004c8b55d04c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb3248c78530ffffff02000000488b7de0488bb530ffffffe899bfffff48898528ffffff4c8b9528ffffff4c899568ffffffeb0048c78520ffffff030000004c8b9568ffffff4c8b9d20ffffff4d39da0f9fc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb554c8d15480200004c899510ffffff488bbd10ffffffe88dbcffff48898508ffffff48c78500ffffff010000004c8b9500ffffff4c8915ad02000048c785f8feffff00000000488b85f8feffff4881c4500100005dc34c8b9578ffffff4c8b1d640200004d0fafd34c8995f0feffff4c8b55884c8b9df0feffff4d01da4c8995e8feffff4c8b9568ffffff4c8b1d3e0200004d0fafd34c8995e0feffff4c8b95e8feffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c8995d0feffff488b7de0e82cbeffff488985c8feffff48c785c0feffff000000004c8b95c8feffff4c8b9dc0feffff4d39da0f9cc04c0fb6d04c8995b8feffff4c8b95b8feffff4983fa017402eb214c8b95d0feffff49f7da4c8995b0feffff4c8b95b0feffff4c8995d0feffffeb00488b85d0feffff4881c4500100005dc3554889e54883ec6048897df8488b7df8e8a3fcffff488945f04c8b55f04c8955e848c745e0000000004c8b157b0100004c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d000000000488b45d04883c4605dc348c745c8000000004c8b55e84c8b5dc84d39da0f9cc04c0fb6d04c8955c04c8b55c04983fa017402eb3d4c8d15b20000004c8955b8488b7db8e8cdbaffff488945b048c745a8010000004c8b55a84c8915f600000048c745a000000000488b45a04883c4605dc3488b45e84883c4605dc3000000000000000000000100000000000000000000000000000012000000000000006469766973696f6e206279207a65726f0a000000000000002300000000000000696e7465676572206f7574206f66206d616368696e652d776f72642072616e67650a0000000000002c00000000000000696e7465676572206f7574206f6620756e7369676e6564206d616368696e652d776f72642072616e67650a000000000000000000000000000800000000000000100000000000000000000040000000000000000000000010ffffffffffffff3f000000000000000000000000000000000000000000000000" + +fn bigint_blob_byte_from_hex(c: int) -> int: + if c >= 48 && c <= 57: + return c - 48 + if c >= 97 && c <= 102: + return c - 87 + return c - 55 + +fn bigint_blob_bytes() -> string: + h := bigint_blob_hex() + hl := str_len(h) + n := hl / 2 + out := alloc(n + 1) + i : ., mut = 0 + loop: + if i >= n: + break + hi := bigint_blob_byte_from_hex(load8(h, i * 2)) + lo := bigint_blob_byte_from_hex(load8(h, i * 2 + 1)) + store8(out, i, hi * 16 + lo) + i = i + 1 + store8(out, n, 0) + return out + +fn bigint_blob_symbol(name: string) -> int: + if str_eq(name, "bi_add") != 0: + return 5849 + if str_eq(name, "bi_add_abs") != 0: + return 4354 + if str_eq(name, "bi_add_small") != 0: + return 10241 + if str_eq(name, "bi_as_big") != 0: + return 2059 + if str_eq(name, "bi_big_ptr") != 0: + return 374 + if str_eq(name, "bi_bit") != 0: + return 9180 + if str_eq(name, "bi_bitlen_abs") != 0: + return 8777 + if str_eq(name, "bi_cmp") != 0: + return 3880 + if str_eq(name, "bi_cmp_abs") != 0: + return 3345 + if str_eq(name, "bi_copy") != 0: + return 3107 + if str_eq(name, "bi_digit") != 0: + return 674 + if str_eq(name, "bi_div") != 0: + return 16311 + if str_eq(name, "bi_div_small") != 0: + return 11773 + if str_eq(name, "bi_divmod") != 0: + return 14702 + if str_eq(name, "bi_from_dec") != 0: + return 12615 + if str_eq(name, "bi_from_imm") != 0: + return 1242 + if str_eq(name, "bi_from_uword") != 0: + return 16564 + if str_eq(name, "bi_from_word") != 0: + return 16416 + if str_eq(name, "bi_imm") != 0: + return 275 + if str_eq(name, "bi_is_big") != 0: + return 154 + if str_eq(name, "bi_is_zero") != 0: + return 8361 + if str_eq(name, "bi_len_of") != 0: + return 633 + if str_eq(name, "bi_mod") != 0: + return 16362 + if str_eq(name, "bi_mul") != 0: + return 6898 + if str_eq(name, "bi_mul_small") != 0: + return 10297 + if str_eq(name, "bi_neg") != 0: + return 6541 + if str_eq(name, "bi_new") != 0: + return 515 + if str_eq(name, "bi_normalize") != 0: + return 936 + if str_eq(name, "bi_pow2") != 0: + return 8618 + if str_eq(name, "bi_result") != 0: + return 2185 + if str_eq(name, "bi_runtime_error") != 0: + return 0 + if str_eq(name, "bi_set_bit") != 0: + return 9425 + if str_eq(name, "bi_set_digit") != 0: + return 848 + if str_eq(name, "bi_set_len") != 0: + return 803 + if str_eq(name, "bi_set_sign") != 0: + return 758 + if str_eq(name, "bi_shl1") != 0: + return 9648 + if str_eq(name, "bi_sign_of") != 0: + return 592 + if str_eq(name, "bi_sub") != 0: + return 6842 + if str_eq(name, "bi_sub_abs") != 0: + return 5212 + if str_eq(name, "bi_tag") != 0: + return 328 + if str_eq(name, "bi_tag_ptr") != 0: + return 446 + if str_eq(name, "bi_to_dec") != 0: + return 13347 + if str_eq(name, "bi_to_uword_checked") != 0: + return 17557 + if str_eq(name, "bi_to_word_checked") != 0: + return 16717 + return -1 + +fn bigint_blob_symbol_count() -> int: + return 44 + +fn bigint_blob_symbol_name(i: int) -> string: + if i == 0: + return "bi_add" + if i == 1: + return "bi_add_abs" + if i == 2: + return "bi_add_small" + if i == 3: + return "bi_as_big" + if i == 4: + return "bi_big_ptr" + if i == 5: + return "bi_bit" + if i == 6: + return "bi_bitlen_abs" + if i == 7: + return "bi_cmp" + if i == 8: + return "bi_cmp_abs" + if i == 9: + return "bi_copy" + if i == 10: + return "bi_digit" + if i == 11: + return "bi_div" + if i == 12: + return "bi_div_small" + if i == 13: + return "bi_divmod" + if i == 14: + return "bi_from_dec" + if i == 15: + return "bi_from_imm" + if i == 16: + return "bi_from_uword" + if i == 17: + return "bi_from_word" + if i == 18: + return "bi_imm" + if i == 19: + return "bi_is_big" + if i == 20: + return "bi_is_zero" + if i == 21: + return "bi_len_of" + if i == 22: + return "bi_mod" + if i == 23: + return "bi_mul" + if i == 24: + return "bi_mul_small" + if i == 25: + return "bi_neg" + if i == 26: + return "bi_new" + if i == 27: + return "bi_normalize" + if i == 28: + return "bi_pow2" + if i == 29: + return "bi_result" + if i == 30: + return "bi_runtime_error" + if i == 31: + return "bi_set_bit" + if i == 32: + return "bi_set_digit" + if i == 33: + return "bi_set_len" + if i == 34: + return "bi_set_sign" + if i == 35: + return "bi_shl1" + if i == 36: + return "bi_sign_of" + if i == 37: + return "bi_sub" + if i == 38: + return "bi_sub_abs" + if i == 39: + return "bi_tag" + if i == 40: + return "bi_tag_ptr" + if i == 41: + return "bi_to_dec" + if i == 42: + return "bi_to_uword_checked" + if i == 43: + return "bi_to_word_checked" + return "" + +fn bigint_blob_alloc_count() -> int: + return 4 + +fn bigint_blob_alloc_off(i: int) -> int: + if i == 0: + return 574 + if i == 1: + return 13417 + if i == 2: + return 13770 + if i == 3: + return 14442 + return -1 diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux/_import.cr index 039290ae..cf07d5de 100644 --- a/src/targets/x86_64-linux/_import.cr +++ b/src/targets/x86_64-linux/_import.cr @@ -29,6 +29,8 @@ import ent_kernel // build_linear_schedule in regalloc.cr. import regalloc import sizes +import int_rep +import bigint_blob import instr // Architecture tag-table owner: mw_setup_tags and the mw encoders used by elf.cr. // tag2l and frame.cr reference each other; pf_epilogue calls e2_mw_*, and tag2l diff --git a/src/targets/x86_64-linux/main.cr b/src/targets/x86_64-linux/main.cr index 34c8f4f4..84267a2f 100644 --- a/src/targets/x86_64-linux/main.cr +++ b/src/targets/x86_64-linux/main.cr @@ -76,6 +76,7 @@ fn corearch_main() -> int: if cli_parse() != 0: return 1 + g_x86_big_enabled = cli_has("big-int") // This entry emits ELF only. Table and debug channels exist only in the // concat entry, so reject them explicitly instead of reporting an unknown // flag or ignoring a known flag. diff --git a/tests/selfhost/test_bigint_arith.py b/tests/selfhost/test_bigint_arith.py new file mode 100644 index 00000000..2a920670 --- /dev/null +++ b/tests/selfhost/test_bigint_arith.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +"""Self-hosted backend BigInt arithmetic gate (--big-int).""" + +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" + +ARITH = r""" +g: int = 9223372036854775808 + +fn bump(v: int) -> int: + return v + 1 + +fn main() -> int: + // small fast path and a loop + total := 0 + i := 0 + loop: + if i >= 100: + break + total = total + i + i = i + 1 + if total != 4950: + return 1 + + // immediate boundaries + a := 4611686018427387903 + if a + 1 != 4611686018427387904: + return 2 + if a + 2 != 4611686018427387905: + return 3 + c := -4611686018427387904 + if c - 1 != -4611686018427387905: + return 4 + if -c != 4611686018427387904: + return 5 + if c * -1 != 4611686018427387904: + return 6 + + // 2^100 arithmetic + x := 1267650600228229401496703205376 + y := x + 123 + if y != 1267650600228229401496703205499: + return 7 + if y - x != 123: + return 8 + if x * 3 != 3802951800684688204490109616128: + return 9 + if x / 3 != 422550200076076467165567735125: + return 10 + if x % 3 != 1: + return 11 + + // truncation toward zero and remainder sign follows dividend + nx := -x + if nx / 3 != -422550200076076467165567735125: + return 12 + if nx % 3 != -1: + return 13 + if !(nx < x): + return 14 + if !(x < y): + return 15 + if nx > 0: + return 16 + if -nx != x: + return 17 + + // decimal/hex/octal/binary literals + h := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + if h != 340282366920938463463374607431768211455: + return 18 + o := 0o400000000000000000000000000000000 + if o != 316912650057057350374175801344: + return 19 + b := 0b10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + if b != 1267650600228229401496703205376: + return 20 + + // calls, parameters, returns, and a big global initializer + if bump(x) != 1267650600228229401496703205377: + return 21 + if g + 2 != 9223372036854775810: + return 22 + + return 0 +""" + +SMALL = r""" +fn main() -> int: + a := 7 + b := 5 + if a + b != 12: + return 1 + if a - b != 2: + return 2 + if a * b != 35: + return 3 + if a / b != 1: + return 4 + if a % b != 2: + return 5 + if !(b < a): + return 6 + return 0 +""" + + +def run(args): + return subprocess.run(args, cwd=str(BASE), capture_output=True, text=True) + + +def build(src: Path, ccr: Path) -> str: + r = run([str(COREC), "ccr", str(src), "-o", str(ccr)]) + if r.returncode != 0: + return (r.stdout + r.stderr).strip() + return "" + + +def emit(ccr: Path, out: Path, big: bool) -> str: + args = [str(COREARCH), str(ccr), "-o", str(out)] + if big: + args.append("--big-int") + r = run(args) + if r.returncode != 0: + return (r.stdout + r.stderr).strip() + return "" + + +def main() -> int: + if not COREC.exists() or not COREARCH.exists(): + print("missing build/corec or build/corearch") + return 1 + with tempfile.TemporaryDirectory(prefix="core-bigint-arith-") as td: + d = Path(td) + src = d / "arith.cr" + ccr = d / "arith.ccr" + exe = d / "arith" + src.write_text(ARITH, encoding="utf-8") + err = build(src, ccr) + if err: + print("frontend failed:\n" + err) + return 1 + err = emit(ccr, exe, True) + if err: + print("backend failed:\n" + err) + return 1 + r = run([str(exe)]) + if r.returncode != 0: + print(f"bigint arithmetic exit {r.returncode}") + return 1 + + # Divide-by-zero and an out-of-word main return must reach the shared + # runtime error path rather than silently producing a tagged pointer. + for name, source, needle in ( + ("zero", "fn main() -> int:\n x := 9223372036854775808\n y := x / 0\n return 0\n", + "division by zero"), + ("word", "fn main() -> int:\n return 1267650600228229401496703205376\n", + "machine-word range"), + ): + zsrc = d / f"{name}.cr" + zccr = d / f"{name}.ccr" + zexe = d / name + zsrc.write_text(source, encoding="utf-8") + err = build(zsrc, zccr) + if err: + print(f"{name} frontend failed:\n" + err) + return 1 + err = emit(zccr, zexe, True) + if err: + print(f"{name} backend failed:\n" + err) + return 1 + zr = run([str(zexe)]) + if zr.returncode == 0: + print(f"{name}: expected runtime failure") + return 1 + if needle not in (zr.stdout + zr.stderr): + print(f"{name}: missing error text {needle!r}: {zr.stdout + zr.stderr!r}") + return 1 + + # The same .ccr must keep working on the raw backend for programs whose + # values stay in machine-word range. + ssrc = d / "small.cr" + sccr = d / "small.ccr" + raw = d / "small_raw" + bigs = d / "small_big" + ssrc.write_text(SMALL, encoding="utf-8") + err = build(ssrc, sccr) + if err: + print("small frontend failed:\n" + err) + return 1 + for out, big in ((raw, False), (bigs, True)): + err = emit(sccr, out, big) + if err: + print("small backend failed:\n" + err) + return 1 + rr = run([str(out)]) + if rr.returncode != 0: + print(f"small parity exit {rr.returncode} (big={big})") + return 1 + + print("PASS bigint arithmetic") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/gen_bigint_blob.py b/tools/gen_bigint_blob.py new file mode 100644 index 00000000..21fa0693 --- /dev/null +++ b/tools/gen_bigint_blob.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 +"""Generate src/runtime/bigint_blob.cr from src/runtime/bigint.cr. + +The BigInt runtime is pure Core. For the self-hosted backend we need its raw +machine code as a relocatable-free blob: the backend compiles user programs with +tagged integers, but the runtime itself must use raw limbs (otherwise limb +arithmetic would recurse into BigInt operations). + +The blob is position independent after this script resolves every relocation +that is relative to another symbol in the blob. The only external symbol is +`alloc`; corearch patches its four call sites to the allocator it emits into the +same ELF. The blob is placed in the code segment, which --big-int marks RWX so +the few runtime globals (g_bi_error/g_bi_rem) are writable. +""" + +from __future__ import annotations + +import struct +import subprocess +import sys +from pathlib import Path + +BASE = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(BASE / "bootstrap")) + +from corec.backend.x86_64_stack_asm import X86_64StackAsmGen +from corec.frontend.desugar import MatchDesugarer +from corec.frontend.ir_gen import IRGen +from corec.frontend.lexer import Lexer +from corec.frontend.name_resolver import NameResolver +from corec.frontend.parser import Parser +from corec.frontend.type_checker import TypeChecker + + +def compile_runtime() -> bytes: + src = (BASE / "src" / "runtime" / "bigint.cr").read_text(encoding="utf-8") + ast = Parser(Lexer(src).tokenize()).parse_compilation_unit() + resolver = NameResolver() + resolver.resolve(ast) + ast = MatchDesugarer(resolver.symtab).desugar(ast) + checker = TypeChecker(resolver.symtab) + checker.check(ast) + if resolver.errors or checker.errors: + raise SystemExit(f"bigint type errors: {resolver.errors + checker.errors}") + module = IRGen(resolver.symtab, bigint_mode=False).gen_module(ast) + asm = X86_64StackAsmGen(module, runtime_only=True).generate() + tmp = BASE / "build" / "bigint_blob" + tmp.mkdir(exist_ok=True) + asm_path = tmp / "bigint.s" + obj_path = tmp / "bigint.o" + asm_path.write_text(asm + "\n", encoding="utf-8") + r = subprocess.run(["as", "-o", str(obj_path), str(asm_path)], + capture_output=True, text=True) + if r.returncode != 0: + raise SystemExit(r.stderr) + return obj_path.read_bytes() + + +class Elf: + def __init__(self, data: bytes): + self.data = data + if data[:4] != b"\x7fELF" or data[4] != 2: + raise SystemExit("not an ELF64 object") + self.e_shoff = struct.unpack_from(" dict: + return self.sections[self.by_name[name]] + + def section_data(self, name: str) -> bytes: + s = self.section(name) + return self.data[s["offset"]:s["offset"] + s["size"]] + + def symbols(self): + symtab = self.section(".symtab") + strtab = self.sections[symtab["link"]] + strings = self.data[strtab["offset"]:strtab["offset"] + strtab["size"]] + out = [] + for off in range(symtab["offset"], symtab["offset"] + symtab["size"], 24): + name_off, info, other, shndx, value, size = struct.unpack_from( + "> 32, r_info & 0xFFFFFFFF, r_addend)) + return out + + +def align_up(n: int, a: int) -> int: + return (n + a - 1) // a * a + + +def build_blob(obj_data: bytes): + elf = Elf(obj_data) + text = elf.section_data(".text") + rodata = elf.section_data(".rodata") + data = elf.section_data(".data") + bss = elf.section(".bss") + if bss["size"] != 0: + raise SystemExit("unexpected bigint .bss") + + ro_base = align_up(len(text), 16) + data_base = align_up(ro_base + len(rodata), 8) + total = align_up(data_base + len(data), 16) + blob = bytearray(total) + blob[0:len(text)] = text + blob[ro_base:ro_base + len(rodata)] = rodata + blob[data_base:data_base + len(data)] = data + + syms = elf.symbols() + sec_base = { + elf.by_name[".text"]: 0, + elf.by_name[".rodata"]: ro_base, + elf.by_name[".data"]: data_base, + elf.by_name[".bss"]: data_base + len(data), + } + alloc_calls = [] + for r_offset, sym_idx, r_type, addend in elf.rela(".rela.text"): + sym = syms[sym_idx] + if sym["name"] == "alloc" and sym["shndx"] == 0: + if r_type not in (2, 4): + raise SystemExit(f"unexpected alloc relocation type {r_type}") + alloc_calls.append(r_offset) + # Placeholder; corearch writes the final rel32. + struct.pack_into("= (1 << 31): + raise SystemExit(f"relocation overflow at {r_offset:#x}: {value}") + struct.pack_into(" str: + return data.hex() + + +def emit_core(blob: bytes, funcs: dict[str, int], alloc_calls: list[int], + data_base: int, data_len: int) -> str: + funcs_sorted = sorted(funcs.items()) + lines = [] + lines.append("// === bigint_blob.cr ===") + lines.append("// Generated by tools/gen_bigint_blob.py from src/runtime/bigint.cr.") + lines.append("// Do not edit by hand. The blob is raw-core BigInt machine code plus") + lines.append("// its read-only and writable data; corearch copies it into the code segment.") + lines.append("") + lines.append(f"BIGINT_BLOB_SIZE : int = {len(blob)}") + lines.append(f"BIGINT_BLOB_DATA_OFF : int = {data_base}") + lines.append(f"BIGINT_BLOB_DATA_LEN : int = {data_len}") + lines.append("") + # Keep the literal on one line. The Core lexer handles long string literals; + # it must be returned from a function, not stored in a global: the Python + # bootstrap emits global string initializers as raw quad words. + lines.append("fn bigint_blob_hex() -> string:") + lines.append(" return \"" + hex_literal(blob) + "\"") + lines.append("") + lines.append("fn bigint_blob_byte_from_hex(c: int) -> int:") + lines.append(" if c >= 48 && c <= 57:") + lines.append(" return c - 48") + lines.append(" if c >= 97 && c <= 102:") + lines.append(" return c - 87") + lines.append(" return c - 55") + lines.append("") + lines.append("fn bigint_blob_bytes() -> string:") + lines.append(" h := bigint_blob_hex()") + lines.append(" hl := str_len(h)") + lines.append(" n := hl / 2") + lines.append(" out := alloc(n + 1)") + lines.append(" i : ., mut = 0") + lines.append(" loop:") + lines.append(" if i >= n:") + lines.append(" break") + lines.append(" hi := bigint_blob_byte_from_hex(load8(h, i * 2))") + lines.append(" lo := bigint_blob_byte_from_hex(load8(h, i * 2 + 1))") + lines.append(" store8(out, i, hi * 16 + lo)") + lines.append(" i = i + 1") + lines.append(" store8(out, n, 0)") + lines.append(" return out") + lines.append("") + lines.append("fn bigint_blob_symbol(name: string) -> int:") + for name, off in funcs_sorted: + lines.append(f" if str_eq(name, \"{name}\") != 0:") + lines.append(f" return {off}") + lines.append(" return -1") + lines.append("") + lines.append("fn bigint_blob_symbol_count() -> int:") + lines.append(f" return {len(funcs_sorted)}") + lines.append("") + lines.append("fn bigint_blob_symbol_name(i: int) -> string:") + for i, (name, _) in enumerate(funcs_sorted): + lines.append(f" if i == {i}:") + lines.append(f" return \"{name}\"") + lines.append(" return \"\"") + lines.append("") + lines.append("fn bigint_blob_alloc_count() -> int:") + lines.append(f" return {len(alloc_calls)}") + lines.append("") + lines.append("fn bigint_blob_alloc_off(i: int) -> int:") + for i, off in enumerate(alloc_calls): + lines.append(f" if i == {i}:") + lines.append(f" return {off}") + lines.append(" return -1") + lines.append("") + return "\n".join(lines) + + +def main() -> int: + obj = compile_runtime() + blob, funcs, alloc_calls, data_base, data_len = build_blob(obj) + out = BASE / "src" / "runtime" / "bigint_blob.cr" + out.write_text(emit_core(blob, funcs, alloc_calls, data_base, data_len), + encoding="utf-8") + print(f"wrote {out} ({len(blob)} bytes, {len(funcs)} symbols, " + f"{len(alloc_calls)} alloc calls)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 968eb7fd5be903b45a04064cd36ff5e0bf8c7c36 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 11:59:53 +0800 Subject: [PATCH 22/58] backend: carry tagged ints through containers and calls --- src/arch/x86_64/instr.cr | 133 +++++++++++++++------ src/arch/x86_64/int_rep.cr | 20 ++++ tests/selfhost/test_bigint_containers.py | 143 +++++++++++++++++++++++ 3 files changed, 259 insertions(+), 37 deletions(-) create mode 100644 tests/selfhost/test_bigint_containers.py diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 583b0827..4630a79c 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -1522,10 +1522,16 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_ADDR_INDEX && d >= 0: do2 := g2_slot(d) - // load array pointer from arr base slot (handles both local and global) - cp = cp + e2_load_var(buf, pos+cp, 10, s1) - // load index (handles both local and global) - cp = cp + e2_load_var(buf, pos+cp, 11, s2) + if g_x86_big_enabled != 0: + // The index is a mathematical int. Convert it before loading the + // array pointer because the runtime call may clobber caller-saved r10. + cp = cp + e2_big_word_var(buf, pos+cp, 11, s2) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + else: + // load array pointer from arr base slot (handles both local and global) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + // load index (handles both local and global) + cp = cp + e2_load_var(buf, pos+cp, 11, s2) // lea r10, [r10 + r11*8] = address of arr[i] on heap cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 11/8, 10/8) e2_w8(buf, pos+cp, 141) @@ -1634,6 +1640,10 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 2, 10%8, 10%8) cp = cp + e2_w32(buf, pos+cp, 0) + if g_x86_big_enabled != 0: + // The enum tag is an int value in user code (variant name index). + // The object stores it raw; tag it before it reaches a comparison. + cp = cp + e2_big_tag_reg(buf, pos+cp, 10) cp = cp + e2_st(buf, pos+cp, 10, do2) return cp @@ -1685,10 +1695,15 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_LOAD_INDEX_VAR && d >= 0: do2 := g2_slot(d) - // load array pointer (handles local and global) - cp = cp + e2_load_var(buf, pos+cp, 10, s1) - // load index (handles local and global) - cp = cp + e2_load_var(buf, pos+cp, 11, s2) + if g_x86_big_enabled != 0: + // Convert the index before loading the array pointer. + cp = cp + e2_big_word_var(buf, pos+cp, 11, s2) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + else: + // load array pointer (handles local and global) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + // load index (handles local and global) + cp = cp + e2_load_var(buf, pos+cp, 11, s2) // Strings use byte indexing; arrays use 8-byte element indexing. if irv_type(s1) == TI_STR: cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 11/8, 10/8) @@ -1708,26 +1723,45 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: return cp if op == IR_STORE_INDEX_VAR && d >= 0: - // load array pointer (handles local and global) - cp = cp + e2_load_var(buf, pos+cp, 10, s1) - // load index (handles local and global) - cp = cp + e2_load_var(buf, pos+cp, 11, s2) - // load value to store (handles local and global) - cp = cp + e2_load_var(buf, pos+cp, 12, d) + if g_x86_big_enabled != 0: + cp = cp + e2_big_word_var(buf, pos+cp, 11, s2) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + cp = cp + e2_load_var(buf, pos+cp, 8, d) + else: + // load array pointer (handles local and global) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + // load index (handles local and global) + cp = cp + e2_load_var(buf, pos+cp, 11, s2) + // load value to store (handles local and global) + cp = cp + e2_load_var(buf, pos+cp, 12, d) if irv_type(s1) == TI_STR: // String assignment stores one byte at the runtime byte index. - cp = cp + emit_rex(buf, pos+cp, 0, 12/8, 0, 10/8) - e2_w8(buf, pos+cp, 136) - cp = cp + 1 - cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4) - cp = cp + emit_sib(buf, pos+cp, 0, 11%8, 10%8) + if g_x86_big_enabled != 0: + cp = cp + emit_rex(buf, pos+cp, 0, 8/8, 0, 10/8) + e2_w8(buf, pos+cp, 136) + cp = cp + 1 + cp = cp + emit_modrm(buf, pos+cp, 0, 8%8, 4) + cp = cp + emit_sib(buf, pos+cp, 0, 11%8, 10%8) + else: + cp = cp + emit_rex(buf, pos+cp, 0, 12/8, 0, 10/8) + e2_w8(buf, pos+cp, 136) + cp = cp + 1 + cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4) + cp = cp + emit_sib(buf, pos+cp, 0, 11%8, 10%8) else: - // mov [r10 + r11*8], r12 — SIB(scale=3, index=r11%8, base=r10%8) - cp = cp + emit_rex(buf, pos+cp, 1, 12/8, 11/8, 10/8) - e2_w8(buf, pos+cp, 137) - cp = cp + 1 - cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4) - cp = cp + emit_sib(buf, pos+cp, 3, 11%8, 10%8) + // mov [r10 + r11*8], value — SIB(scale=3, index=r11%8, base=r10%8) + if g_x86_big_enabled != 0: + cp = cp + emit_rex(buf, pos+cp, 1, 8/8, 11/8, 10/8) + e2_w8(buf, pos+cp, 137) + cp = cp + 1 + cp = cp + emit_modrm(buf, pos+cp, 0, 8%8, 4) + cp = cp + emit_sib(buf, pos+cp, 3, 11%8, 10%8) + else: + cp = cp + emit_rex(buf, pos+cp, 1, 12/8, 11/8, 10/8) + e2_w8(buf, pos+cp, 137) + cp = cp + 1 + cp = cp + emit_modrm(buf, pos+cp, 0, 12%8, 4) + cp = cp + emit_sib(buf, pos+cp, 3, 11%8, 10%8) return cp if op == IR_MAKE_ENUM && d >= 0: @@ -1755,9 +1789,14 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_SLICE && d >= 0: do2 := g2_slot(d) // Both the array pointer (s1) and the low bound (s2) go through the - // global operand seam. - cp = cp + e2_load_var(buf, pos+cp, 10, s1) - cp = cp + e2_load_var(buf, pos+cp, 11, s2) + // global operand seam. Convert the bound first: the runtime call may + // clobber caller-saved r10. + if g_x86_big_enabled != 0: + cp = cp + e2_big_word_var(buf, pos+cp, 11, s2) + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + else: + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + cp = cp + e2_load_var(buf, pos+cp, 11, s2) // shl r11, 3 — REX.WB + 0xC1, /4 cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 11/8) e2_w8(buf, pos+cp, 193) @@ -1782,16 +1821,36 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // s1 = index var, s2 = max_len literal. index < 0 or index >= max_len // traps with ud2. When ti == 0, s2 is a literal upper bound; otherwise it // is a variable holding the length. - cp = cp + e2_load_var(buf, pos+cp, 10, s1) // index - if ti != 0: - // Dynamic upper bound: load len_var into r11. - cp = cp + e2_load_var(buf, pos+cp, 11, s2) + if g_x86_big_enabled != 0: + if ti != 0: + // Convert both tagged operands. Save the length on the stack: + // r12 belongs to the register allocator at O2 and a runtime call + // may clobber caller-saved registers. Two pushes keep rsp + // 16-byte aligned across the calls. + cp = cp + e2_big_word_var(buf, pos+cp, 11, s2) + cp = cp + e2_big_push_r11(buf, pos+cp) + cp = cp + e2_big_push_r11(buf, pos+cp) + cp = cp + e2_big_word_var(buf, pos+cp, 10, s1) + cp = cp + e2_big_pop_r11(buf, pos+cp) + cp = cp + e2_big_pop_r11(buf, pos+cp) + else: + cp = cp + e2_big_word_var(buf, pos+cp, 10, s1) + // Constant upper bound: movabs r11, imm64. + e2_w8(buf, pos+cp, 73) + e2_w8(buf, pos+cp+1, 187) + e2_w64(buf, pos+cp+2, s2) + cp = cp + 10 else: - // Constant upper bound: movabs r11, imm64. - e2_w8(buf, pos+cp, 73) - e2_w8(buf, pos+cp+1, 187) - e2_w64(buf, pos+cp+2, s2) - cp = cp + 10 + cp = cp + e2_load_var(buf, pos+cp, 10, s1) // index + if ti != 0: + // Dynamic upper bound: load len_var into r11. + cp = cp + e2_load_var(buf, pos+cp, 11, s2) + else: + // Constant upper bound: movabs r11, imm64. + e2_w8(buf, pos+cp, 73) + e2_w8(buf, pos+cp+1, 187) + e2_w64(buf, pos+cp+2, s2) + cp = cp + 10 cp = cp + e2_alu(buf, pos+cp, 57) // cmp r10, r11 // jb +2: if index < max (unsigned below), skip the 2-byte ud2 -> continue e2_w8(buf, pos+cp, 114) // 0x72 = jb rel8 diff --git a/src/arch/x86_64/int_rep.cr b/src/arch/x86_64/int_rep.cr index 27cda1be..d07a563a 100644 --- a/src/arch/x86_64/int_rep.cr +++ b/src/arch/x86_64/int_rep.cr @@ -139,6 +139,26 @@ fn e2_big_test_rr(buf: string, p: int, a: int, b: int) -> int: fn e2_big_test_rax(buf: string, p: int) -> int: return e2_big_test_rr(buf, p, 0, 0) +// Convert an int variable to a raw machine word through the checked runtime +// boundary. The value remains tagged in its slot; only the temporary register +// is raw. Used by index, slice, and bounds-check paths. +fn e2_big_word_var(buf: string, p: int, reg: int, var_idx: int) -> int: + cp : ., mut = 0 + cp = cp + e2_load_var(buf, p + cp, 7, var_idx) + cp = cp + e2_big_call_name(buf, p + cp, str_intern("bi_to_word_checked")) + cp = cp + e2_mov(buf, p + cp, reg, 0) + return cp + +fn e2_big_push_r11(buf: string, p: int) -> int: + e2_w8(buf, p, 65) + e2_w8(buf, p + 1, 83) + return 2 + +fn e2_big_pop_r11(buf: string, p: int) -> int: + e2_w8(buf, p, 65) + e2_w8(buf, p + 1, 91) + return 2 + fn emit_big_neg(buf: string, pos: int, d: int, s1: int) -> int: cp : ., mut = 0 cp = cp + e2_load_var(buf, pos + cp, 10, s1) diff --git a/tests/selfhost/test_bigint_containers.py b/tests/selfhost/test_bigint_containers.py new file mode 100644 index 00000000..18559b6c --- /dev/null +++ b/tests/selfhost/test_bigint_containers.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +"""Self-hosted BigInt container/call/global gate (--big-int).""" + +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" + +SRC = r""" +struct Pair: + a: int + b: int + +enum Box: + Empty + One(int) + Two(int, int) + +g: int = 1267650600228229401496703205376 + +fn fact(n: int) -> int: + if n <= 1: + return 1 + return n * fact(n - 1) + +fn add(a: int, b: int) -> int: + return a + b + +fn main() -> int: + x := 9223372036854775808 + y := x + 1 + + arr : ., mut = [x, y, x + 2] + i := 1 + if arr[i] != y: + return 1 + arr[i] = x + 10 + if arr[1] != x + 10: + return 2 + if arr[0] + arr[i] != 2 * x + 10: + return 3 + + p : ., mut = Pair { a = x, b = y } + if p.a + p.b != 2 * x + 1: + return 4 + p.a = x + 3 + if p.a != x + 3: + return 5 + + t := (x, y) + if t . 0 + t . 1 != 2 * x + 1: + return 6 + + b := Two(x, y) + v := match b: + Empty => 0, + One(q) => q, + Two(q, r) => q + r, + if v != 2 * x + 1: + return 7 + + lo := 1 + hi := 3 + s := arr[lo..hi] + if s[0] != x + 10: + return 8 + j := 1 + if s[j] != x + 2: + return 9 + + if add(x, y) != 2 * x + 1: + return 10 + if fact(21) != 51090942171709440000: + return 11 + if g + 1 != 1267650600228229401496703205377: + return 12 + return 0 +""" + +OOB = r""" +fn main() -> int: + arr := [1, 2, 3] + i := 3 + return arr[i] +""" + + +def run(args): + return subprocess.run(args, cwd=str(BASE), capture_output=True, text=True) + + +def compile_and_emit(src: Path, ccr: Path, exe: Path, opt: int = 0) -> str: + r = run([str(COREC), "ccr", str(src), "-o", str(ccr)]) + if r.returncode != 0: + return r.stdout + r.stderr + r = run([str(COREARCH), str(ccr), "--big-int", "-O", str(opt), "-o", str(exe)]) + if r.returncode != 0: + return r.stdout + r.stderr + return "" + + +def main() -> int: + if not COREC.exists() or not COREARCH.exists(): + print("missing build/corec or build/corearch") + return 1 + with tempfile.TemporaryDirectory(prefix="core-bigint-containers-") as td: + d = Path(td) + src = d / "containers.cr" + ccr = d / "containers.ccr" + src.write_text(SRC, encoding="utf-8") + for opt in (0, 2): + exe = d / f"containers_o{opt}" + err = compile_and_emit(src, ccr, exe, opt) + if err: + print(err) + return 1 + r = run([str(exe)]) + if r.returncode != 0: + print(f"containers O{opt} exit {r.returncode}") + return 1 + + osrc = d / "oob.cr" + occr = d / "oob.ccr" + oexe = d / "oob" + osrc.write_text(OOB, encoding="utf-8") + err = compile_and_emit(osrc, occr, oexe) + if err: + print(err) + return 1 + orc = run([str(oexe)]) + if orc.returncode == 0: + print("out-of-bounds index unexpectedly succeeded") + return 1 + + print("PASS bigint containers") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 9195b6467432f8fe493fbd86359f474326460adf Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 12:05:35 +0800 Subject: [PATCH 23/58] backend: enforce raw-word boundaries for BigInt --- src/arch/x86_64/instr.cr | 187 ++++++++++++++++++-- src/arch/x86_64/int_rep.cr | 121 +++++++++++++ src/compiler/ast.cr | 4 +- src/compiler/cir_cache.cr | 2 +- src/compiler/dataflow.cr | 4 + src/compiler/ir_gen.cr | 23 ++- src/format/elf/ld.cr | 8 +- src/runtime/bigint.cr | 11 +- src/runtime/bigint_blob.cr | 8 +- tests/selfhost/test_bigint_boundaries.py | 207 +++++++++++++++++++++++ tests/selfhost/test_ccr_types.py | 4 +- 11 files changed, 552 insertions(+), 27 deletions(-) create mode 100644 tests/selfhost/test_bigint_boundaries.py diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 4630a79c..8e04de30 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -673,7 +673,13 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_BINARY: do2 := g2_slot(d) - if g_x86_big_enabled != 0 && ti == TI_INT: + if g_x86_big_enabled != 0 && (s3 == OP_PTR_ADD || s3 == OP_PTR_SUB || s3 == OP_PTR_DIFF): + pz := emit_big_ptr_binary(buf, pos, d, s1, s2, s3) + if pz > 0: + return pz + if g_x86_big_enabled != 0 && s3 >= OP_EQ && s3 <= OP_GE && (irv_type(s1) == TI_CHAR || irv_type(s2) == TI_CHAR): + return emit_big_char_cmp(buf, pos, d, s1, s2, s3) + if g_x86_big_enabled != 0 && ti == TI_INT && irv_type(s1) == TI_INT && irv_type(s2) == TI_INT: bz := emit_big_binary(buf, pos, d, s1, s2, s3) if bz > 0: return bz @@ -917,7 +923,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_UNARY && d >= 0: do2 := g2_slot(d) - if g_x86_big_enabled != 0 && s3 == UOP_NEG: + if g_x86_big_enabled != 0 && s3 == UOP_NEG && irv_type(s1) == TI_INT: return emit_big_neg(buf, pos, d, s1) cp = cp + e2_load_var(buf, pos+cp, 10, s1) if s3 == UOP_NEG: @@ -960,15 +966,64 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cs_stack_args(buf, pos, cp, fa, ac) // Match builtins by interned string index (integer compare, no str_eq) if s3 == g_ni_syscall3: + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 32) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_store_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_store_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_convert_stack(buf, pos+cp, 0) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_convert_stack(buf, pos+cp, 16) + cp = cp + e2_big_convert_stack(buf, pos+cp, 24) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_load_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_load_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_add_rsp(buf, pos+cp, 32) // Linux syscall 约定发射序 = OS 轴序列(syscall.cr, // 抽取,rax 号 + rdi/rsi/rdx 参数序 + 0F 05 + rax 回存)。 cp = sys_syscall3_stub(buf, pos, cp, d) + if g_x86_big_enabled != 0 && d >= 0: + cp = cp + e2_load_var(buf, pos+cp, 7, d) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) else if s3 == g_ni_syscall4: + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 48) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_store_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_store_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_store_rsp(buf, pos+cp, 32, 8) + cp = cp + e2_big_convert_stack(buf, pos+cp, 0) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_convert_stack(buf, pos+cp, 16) + cp = cp + e2_big_convert_stack(buf, pos+cp, 24) + cp = cp + e2_big_convert_stack(buf, pos+cp, 32) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_load_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_load_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_load_rsp(buf, pos+cp, 32, 8) + cp = cp + e2_big_add_rsp(buf, pos+cp, 48) // Linux syscall 约定发射序 = OS 轴序列(syscall.cr, // 抽取,第 4 参经 r10 的约定与 I-2 修复史见该文件内注)。 cp = sys_syscall4_stub(buf, pos, cp, d) + if g_x86_big_enabled != 0 && d >= 0: + cp = cp + e2_load_var(buf, pos+cp, 7, d) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) else if s3 == g_ni_load8: // movzx rax, byte [rdi+rsi] — REX.W + 0x0FB6 + SIB + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 16) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_add_rsp(buf, pos+cp, 16) cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 0) e2_w8(buf, pos+cp, 15) cp = cp + 1 @@ -977,32 +1032,70 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + emit_modrm(buf, pos+cp, 0, 0, 4) cp = cp + emit_sib(buf, pos+cp, 0, 6, 7) if d >= 0: - cp = cp + e2_store_ret(buf, pos+cp, d) + if g_x86_big_enabled != 0: + cp = cp + e2_big_tag_reg(buf, pos+cp, 0) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else: + cp = cp + e2_store_ret(buf, pos+cp, d) else if s3 == g_ni_store8: // mov [rdi+rsi], dl — 0x88 + SIB (3rd arg in rdx = register 2) + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 32) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_store_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + if fa + 2 < fa + ac && irv_type(fa + 2) == TI_INT: + cp = cp + e2_big_convert_stack(buf, pos+cp, 16) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_load_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_add_rsp(buf, pos+cp, 32) e2_w8(buf, pos+cp, 136) cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 0, 2, 4) cp = cp + emit_sib(buf, pos+cp, 0, 6, 7) else if s3 == g_ni_load64 || s3 == g_ni_load_str_ptr || s3 == g_ni_r64: // mov rax, [rdi + rsi] - // mov rax, [rdi+rsi] — REX.W + 0x8B + SIB + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 16) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_add_rsp(buf, pos+cp, 16) cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 0) e2_w8(buf, pos+cp, 139) cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 0, 0, 4) cp = cp + emit_sib(buf, pos+cp, 0, 6, 7) if d >= 0: - cp = cp + e2_store_ret(buf, pos+cp, d) + if g_x86_big_enabled != 0 && s3 != g_ni_load_str_ptr: + cp = cp + e2_big_box_rax(buf, pos+cp, 0) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else: + cp = cp + e2_store_ret(buf, pos+cp, d) else if s3 == g_ni_store_str_ptr: // mov [rdi + rsi], rdx - // mov [rdi+rsi], rdx — REX.W + 0x89 + SIB + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 16) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_add_rsp(buf, pos+cp, 16) cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 0) e2_w8(buf, pos+cp, 137) cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 0, 2, 4) cp = cp + emit_sib(buf, pos+cp, 0, 6, 7) else if s3 == g_ni_get_arg && gv_argv >= 0: + // Convert the tagged argv index to a raw word first. + if g_x86_big_enabled != 0: + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_to_word_checked")) + cp = cp + e2_mov(buf, pos+cp, 7, 0) // Convert C argv[n] into a Core string with the hidden length header. // NB: gv_argv must be >= 0 (g_rt_argv_ptr registered as a global). // If it's -1, fall through to regular call path — the LEA displacement @@ -1121,9 +1214,19 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if d >= 0: cp = cp + e2_store_ret(buf, pos+cp, d) else if s3 == g_ni_w64: - // w64(buf, pos, val) -> mov [rsi+rdi??], rdx - // Actually args: rdi=buf, rsi=pos, rdx=val - // Just: mov [rdi+rsi], rdx + // w64(buf, pos, val) -> mov [rdi+rsi], rdx + // In --big-int pos/val are tagged ints; convert them at the boundary. + if g_x86_big_enabled != 0: + cp = cp + e2_big_sub_rsp(buf, pos+cp, 32) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_store_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_convert_stack(buf, pos+cp, 8) + cp = cp + e2_big_convert_stack(buf, pos+cp, 16) + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_load_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_add_rsp(buf, pos+cp, 32) cp = cp + emit_rex(buf, pos+cp, 1, 0, 0, 0) e2_w8(buf, pos+cp, 137) cp = cp + 1 @@ -1179,7 +1282,12 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w8(buf, pos+cp+1, 186) e2_w64(buf, pos+cp+2, 0) // placeholder — patched in elf.cr Phase 3 cp = cp + 10 - cp = cp + e2_st(buf, pos+cp, 10, do2) + if g_x86_big_enabled != 0 && d >= 0: + cp = cp + e2_mov(buf, pos+cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_uword")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else: + cp = cp + e2_st(buf, pos+cp, 10, do2) else if s3 >= 0: // 通用调用路径 = callseq.cr cs_call_direct(查名 -> e2_call / // 外部位 + 返回值存; 抽取) @@ -1205,6 +1313,31 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // does not push stack arguments or clean the stack. This is existing // behavior and is kept byte-for-byte; see callseq.cr for the difference. cp = cs_args_dispatch(buf, pos, cp, s2, s3) + if g_x86_big_enabled != 0 && s3 > 0: + // Extern int arguments must be raw machine words. Save the six + // integer argument registers, convert the int-typed ones, and + // restore. Float/string arguments are left untouched. + cp = cp + e2_big_sub_rsp(buf, pos+cp, 48) + cp = cp + e2_big_store_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_store_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_store_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_store_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_store_rsp(buf, pos+cp, 32, 8) + cp = cp + e2_big_store_rsp(buf, pos+cp, 40, 9) + ai3 : ., mut = 0 + loop: + if ai3 >= s3: + break + if ai3 < 6 && irv_type(s2 + ai3) == TI_INT: + cp = cp + e2_big_convert_stack(buf, pos+cp, ai3 * 8) + ai3 = ai3 + 1 + cp = cp + e2_big_load_rsp(buf, pos+cp, 0, 7) + cp = cp + e2_big_load_rsp(buf, pos+cp, 8, 6) + cp = cp + e2_big_load_rsp(buf, pos+cp, 16, 2) + cp = cp + e2_big_load_rsp(buf, pos+cp, 24, 1) + cp = cp + e2_big_load_rsp(buf, pos+cp, 32, 8) + cp = cp + e2_big_load_rsp(buf, pos+cp, 40, 9) + cp = cp + e2_big_add_rsp(buf, pos+cp, 48) // Record external relocation grow_ext_rel(g_x86_ext_rel_count + 1) w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp) @@ -1215,7 +1348,12 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w32(buf, pos+cp+1, 0) cp = cp + 5 // Store return value from rax - cp = cp + e2_st(buf, pos+cp, 0, do2) + if d >= 0: + if g_x86_big_enabled != 0 && irv_type(d) == TI_INT: + cp = cp + e2_big_box_rax(buf, pos+cp, 0) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else: + cp = cp + e2_st(buf, pos+cp, 0, do2) return cp if op == IR_HOTPATCH_ROUTE: @@ -1280,7 +1418,12 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w8(buf, pos+cp+1, 186) // movabs r10, imm64 e2_w64(buf, pos+cp+2, 0) // placeholder — patched in elf.cr Phase 3 cp = cp + 10 - cp = cp + e2_st(buf, pos+cp, 10, do2) + if g_x86_big_enabled != 0 && d >= 0: + cp = cp + e2_mov(buf, pos+cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_uword")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else: + cp = cp + e2_st(buf, pos+cp, 10, do2) return cp if op == IR_RETURN: @@ -1862,6 +2005,26 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 2 // ud2 (SIGILL) return cp + if op == IR_PTR_OF: + if g_x86_big_enabled != 0: + cp = cp + e2_load_var(buf, pos+cp, 7, s1) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_uword")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else if d >= 0 && d != s1: + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + cp = cp + e2_st_var(buf, pos+cp, 10, d) + return cp + + if op == IR_STR_OF: + if g_x86_big_enabled != 0: + cp = cp + e2_load_var(buf, pos+cp, 7, s1) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_to_uword_checked")) + cp = cp + e2_st_var(buf, pos+cp, 0, d) + else if d >= 0 && d != s1: + cp = cp + e2_load_var(buf, pos+cp, 10, s1) + cp = cp + e2_st_var(buf, pos+cp, 10, d) + return cp + if op == IR_INLINE: // No-op at runtime — just a compile hint return 0 diff --git a/src/arch/x86_64/int_rep.cr b/src/arch/x86_64/int_rep.cr index d07a563a..cb48c635 100644 --- a/src/arch/x86_64/int_rep.cr +++ b/src/arch/x86_64/int_rep.cr @@ -159,6 +159,127 @@ fn e2_big_pop_r11(buf: string, p: int) -> int: e2_w8(buf, p + 1, 91) return 2 +fn e2_big_sub_rsp(buf: string, p: int, n: int) -> int: + e2_w8(buf, p, 72) + e2_w8(buf, p + 1, 131) + e2_w8(buf, p + 2, 236) + e2_w8(buf, p + 3, n) + return 4 + +fn e2_big_add_rsp(buf: string, p: int, n: int) -> int: + e2_w8(buf, p, 72) + e2_w8(buf, p + 1, 131) + e2_w8(buf, p + 2, 196) + e2_w8(buf, p + 3, n) + return 4 + +fn e2_big_store_rsp(buf: string, p: int, off: int, reg: int) -> int: + // mov [rsp+off], reg — REX.WR, 0x89, ModRM(1,reg,4), SIB(0,4,4), disp8 + cp := p + cp = cp + emit_rex(buf, cp, 1, reg / 8, 0, 0) + e2_w8(buf, cp, 137) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 1, reg % 8, 4) + e2_w8(buf, cp, 36) + cp = cp + 1 + e2_w8(buf, cp, off) + cp = cp + 1 + return cp - p + +fn e2_big_load_rsp(buf: string, p: int, off: int, reg: int) -> int: + // mov reg, [rsp+off] — REX.WR, 0x8B, ModRM(1,reg,4), SIB(0,4,4), disp8 + cp := p + cp = cp + emit_rex(buf, cp, 1, reg / 8, 0, 0) + e2_w8(buf, cp, 139) + cp = cp + 1 + cp = cp + emit_modrm(buf, cp, 1, reg % 8, 4) + e2_w8(buf, cp, 36) + cp = cp + 1 + e2_w8(buf, cp, off) + cp = cp + 1 + return cp - p + +fn e2_big_convert_stack(buf: string, p: int, off: int) -> int: + cp : ., mut = 0 + cp = cp + e2_big_load_rsp(buf, p + cp, off, 7) + cp = cp + e2_big_call_name(buf, p + cp, str_intern("bi_to_word_checked")) + cp = cp + e2_big_store_rsp(buf, p + cp, off, 0) + return cp + +fn e2_big_box_rax(buf: string, p: int, unsigned: int) -> int: + cp : ., mut = 0 + cp = cp + e2_mov(buf, p + cp, 7, 0) + if unsigned != 0: + cp = cp + e2_big_call_name(buf, p + cp, str_intern("bi_from_uword")) + else: + cp = cp + e2_big_call_name(buf, p + cp, str_intern("bi_from_word")) + return cp + +fn emit_big_char_cmp(buf: string, pos: int, d: int, s1: int, s2: int, op: int) -> int: + cp : ., mut = 0 + // Convert the int operand before loading the raw char operand: the runtime + // call is allowed to clobber caller-saved r10/r11. + if irv_type(s2) == TI_INT: + cp = cp + e2_big_word_var(buf, pos + cp, 11, s2) + else: + cp = cp + e2_load_var(buf, pos + cp, 11, s2) + if irv_type(s1) == TI_INT: + cp = cp + e2_big_word_var(buf, pos + cp, 10, s1) + else: + cp = cp + e2_load_var(buf, pos + cp, 10, s1) + cp = cp + e2_alu(buf, pos + cp, 57) // cmp r10, r11 + cond : ., mut = 4 + if op == OP_NE: + cond = 5 + else if op == OP_LT: + cond = 12 + else if op == OP_GT: + cond = 15 + else if op == OP_LE: + cond = 14 + else if op == OP_GE: + cond = 13 + cp = cp + e2_big_setcc(buf, pos + cp, cond) + cp = cp + e2_big_bool_to_r10(buf, pos + cp) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + return cp + +fn emit_big_ptr_binary(buf: string, pos: int, d: int, s1: int, s2: int, op: int) -> int: + cp : ., mut = 0 + if op == OP_PTR_ADD || op == OP_PTR_SUB: + // Convert the int offset before loading the raw pointer: the runtime call + // may clobber caller-saved r10. + cp = cp + e2_big_word_var(buf, pos + cp, 11, s2) + cp = cp + e2_load_var(buf, pos + cp, 10, s1) + cp = cp + emit_rex(buf, pos + cp, 1, 0, 0, 11 / 8) + e2_w8(buf, pos + cp, 193) // shl r11, 3 + cp = cp + 1 + cp = cp + emit_modrm(buf, pos + cp, 3, 4, 11 % 8) + e2_w8(buf, pos + cp, 3) + cp = cp + 1 + if op == OP_PTR_ADD: + cp = cp + e2_alu(buf, pos + cp, 1) + else: + cp = cp + e2_alu(buf, pos + cp, 41) + cp = cp + e2_st_var(buf, pos + cp, 10, d) + return cp + if op == OP_PTR_DIFF: + cp = cp + e2_load_var(buf, pos + cp, 10, s1) + cp = cp + e2_load_var(buf, pos + cp, 11, s2) + cp = cp + e2_alu(buf, pos + cp, 41) // sub r10, r11 + cp = cp + emit_rex(buf, pos + cp, 1, 0, 0, 10 / 8) + e2_w8(buf, pos + cp, 193) // sar r10, 3 + cp = cp + 1 + cp = cp + emit_modrm(buf, pos + cp, 3, 7, 10 % 8) + e2_w8(buf, pos + cp, 3) + cp = cp + 1 + // The difference is a mathematical int, not a pointer. + cp = cp + e2_mov(buf, pos + cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos + cp, str_intern("bi_from_word")) + cp = cp + e2_st_var(buf, pos + cp, 0, d) + return cp + return 0 + fn emit_big_neg(buf: string, pos: int, d: int, s1: int) -> int: cp : ., mut = 0 cp = cp + e2_load_var(buf, pos + cp, 10, s1) diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index ed7df095..1eeabfd3 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -625,7 +625,7 @@ IR_AWAIT : int = 29 // dest=value_var, s1=future_var; block until future rea IR_BOUNDS_CHECK : int = 30 // s1=index, s2=max_len/len_var, type_kind=1 for dynamic max IR_ARENA_NEW : int = 32 // dest=arena_var, src1=size_estimate IR_ARENA_RESET : int = 33 // src1=arena_id -IR_CONST_BIG : int = 34 // dest=value, s1=canonical decimal string index, tk=TI_INT (dest=-1) +IR_CONST_BIG : int = 40 // dest=value, s1=canonical decimal string index, tk=TI_INT (dest=-1) IR_INLINE : int = 34 // src1=fn_var; inline hint IR_NO_BOUNDS_CHECK : int = 35 //skip bounds check for subsequent DEREFs IR_FAST : int = 36 //allow precision-for-speed optimizations @@ -643,6 +643,8 @@ IR_FNADDR : int = 48 // dest=addr_var, s1=fn_name_ni; load function address (mo IR_I2F : int = 49 // dest=dex_var(bits), s1=int_var; int to binary64(cvtsi2sd,apx 快路径) IR_F2I : int = 50 // dest=int_var, s1=dex_var(bits); binary64 to int(cvttsd2si 截断,apx 快路径) IR_APPROX : int = 51 //无操作数注解:apx 变量声明处(语义许可,后端可忽略,interp 跳过) +IR_PTR_OF : int = 52 // dest=same int var, s1=same string var; view/convert address to mathematical int +IR_STR_OF : int = 53 // dest=same string var, s1=same int var; view/convert mathematical int to raw address // Resolution flag for BRANCH/JUMP (stored in type_kind field after label resolution) IR_RESOLVED : int = 1 diff --git a/src/compiler/cir_cache.cr b/src/compiler/cir_cache.cr index 4b6ffac2..8011ef71 100644 --- a/src/compiler/cir_cache.cr +++ b/src/compiler/cir_cache.cr @@ -56,7 +56,7 @@ // writer and reader use the same constant, so the cache remains self-consistent. // The limitation is recorded in the project backlog. CIR_CACHE_MAGIC : int = -4485090715960753727 -CIR_CACHE_VER : int = 22 +CIR_CACHE_VER : int = 23 g_cir_write_buf : string, mut g_cir_write_pos : int, mut diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index fd97c254..8b8124db 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -551,6 +551,10 @@ fn df_opcode_name(opcode: int, s3: int) -> string: return "const" if opcode == IR_CONST_BIG: return "const_big" + if opcode == IR_PTR_OF: + return "ptr_of" + if opcode == IR_STR_OF: + return "str_of" if opcode == IR_BINARY: if s3 == OP_PTR_ADD: return "ptr_add" diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 44d3c231..4331d250 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -2172,7 +2172,15 @@ fn gen_expr(node: int) -> int: // int/string 同属 GP 字 -> 两侧一致。 if str_eq(name, "str_of") != 0 || str_eq(name, "ptr_of") != 0: inner := gen_expr(ast_a(first_arg)) - return force_if_thunk(inner) + inner = force_if_thunk(inner) + // Keep the view as a zero-byte alias in raw mode. In --big-int + // the backend converts this same slot between a raw address and + // a tagged mathematical int. + if str_eq(name, "ptr_of") != 0: + emit(IR_PTR_OF, inner, inner, 0, 0, TI_INT) + else: + emit(IR_STR_OF, inner, inner, 0, 0, TI_STR) + return inner return -1 @@ -3005,6 +3013,8 @@ fn gen_expr(node: int) -> int: else: emit(IR_BOUNDS_CHECK, -1, high_var, arr_len_lit + 1, 0, 0) v := new_ir_var("slice", TI_INT) + if irv_type(arr_var) == TI_STR: + irv_set_type(v, TI_STR) emit(IR_SLICE, v, arr_var, low_var, high_var, 0) // (A) T3:源数组元素可选 -> 切片登记声明信息(切片元素写点据此装箱; // 非可选不登记 -> 零足迹。切片 var 的 IR 类型信息不动,避免影响 dtype/宽度等消费者) @@ -3026,19 +3036,24 @@ fn gen_expr(node: int) -> int: return v v := new_ir_var("elem", TI_INT) // 元素读定类型取声明信息形式(元素 ti 为 dex -> TI_DEX_S;纯表读) - irv_set_type(v, agg_elem_read_form(arr_var)) + elem_tk : ., mut = 0 + if irv_type(arr_var) == TI_STR: + irv_set_type(v, TI_CHAR) + elem_tk = TI_CHAR + else: + irv_set_type(v, agg_elem_read_form(arr_var)) if idx_kind == EXPR_INT: emit_string_lit_bounds(arr_var, ast_int_val(idx_node)) emit_slice_lit_bounds(arr_var, ast_int_val(idx_node)) if pass_before_array_access(arr_var, -1, ast_int_val(idx_node), arr_len_lit) == 0: - emit(IR_LOAD_INDEX, v, arr_var, 0, ast_int_val(idx_node), 0) + emit(IR_LOAD_INDEX, v, arr_var, 0, ast_int_val(idx_node), elem_tk) else: idx_var := gen_expr(idx_node) idx_var = force_if_thunk(idx_var) emit_string_bounds(arr_var, idx_var) emit_slice_bounds(arr_var, idx_var) if pass_before_array_access(arr_var, idx_var, -1, arr_len_lit) == 0: - emit(IR_LOAD_INDEX_VAR, v, arr_var, idx_var, 0, 0) + emit(IR_LOAD_INDEX_VAR, v, arr_var, idx_var, 0, elem_tk) return v // Enum constructor diff --git a/src/format/elf/ld.cr b/src/format/elf/ld.cr index b64aafe9..5fc66e41 100644 --- a/src/format/elf/ld.cr +++ b/src/format/elf/ld.cr @@ -878,7 +878,13 @@ fn ctx_emit_static(buf: string, path: string) -> int: w16(buf,56,2) w16(buf,58,64) w32(buf,64,1) - w32(buf,68,5) + if g_x86_big_enabled != 0: + // The BigInt runtime blob carries g_bi_error/g_bi_rem in the code + // segment. Static --link copies the code into a single load segment; + // keep those runtime words writable. + w32(buf,68,7) + else: + w32(buf,68,5) w64(buf,72,0) w64(buf,80,0x400000) w64(buf,88,0x400000) diff --git a/src/runtime/bigint.cr b/src/runtime/bigint.cr index 5bb6c134..a780a416 100644 --- a/src/runtime/bigint.cr +++ b/src/runtime/bigint.cr @@ -584,12 +584,19 @@ fn bi_to_word_checked(a: int) -> int: d1 = bi_digit(p, 1) if n > 2: d2 = bi_digit(p, 2) - if d2 > 3: + neg := bi_sign_of(p) < 0 + // 2^63-1 is 7*2^60 + (2^60-1); 2^63 is 8*2^60 and is + // representable only as the negative minimum, -2^63. + if d2 > 8: + bi_runtime_error("integer out of machine-word range\n") + g_bi_error = 1 + return 0 + if d2 == 8 && (!neg || d1 != 0 || d0 != 0): bi_runtime_error("integer out of machine-word range\n") g_bi_error = 1 return 0 x := d0 + d1 * BI_BASE + d2 * BI_BASE2 - if bi_sign_of(p) < 0: + if neg: x = -x return x diff --git a/src/runtime/bigint_blob.cr b/src/runtime/bigint_blob.cr index 4cc00053..9ad0bfff 100644 --- a/src/runtime/bigint_blob.cr +++ b/src/runtime/bigint_blob.cr @@ -3,12 +3,12 @@ // Do not edit by hand. The blob is raw-core BigInt machine code plus // its read-only and writable data; corearch copies it into the code segment. -BIGINT_BLOB_SIZE : int = 18000 -BIGINT_BLOB_DATA_OFF : int = 17928 +BIGINT_BLOB_SIZE : int = 18336 +BIGINT_BLOB_DATA_OFF : int = 18264 BIGINT_BLOB_DATA_LEN : int = 72 fn bigint_blob_hex() -> string: - return "554889e54883ec5048897df848c745f00100000048c745e802000000488b7df8488b47f84883e801488945e0488b7df0488b75e8488b55f8488b4de04889f84889f74889d64889ca0f05488945d848c745d03c00000048c745c80100000048c745c00000000048c745b800000000488b7dd0488b75c8488b55c0488b4db84889f84889f74889d64889ca0f05488945b0488b45b04883c4505dc3554889e54883ec3848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989d24c8955e848c745e0000000004c8b55e84c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d001000000488b45d04883c4385dc348c745c800000000488b45c84883c4385dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989c24c8955e8488b45e84883c4185dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04d0fafd34c8955e8488b45e84883c4185dc3554889e54883ec2848897df84c8d15ef4300004c8955f04c8b55f04c8955e848c745e0010000004c8b55f84c8b5de04d29da4c8955d84c8b55d84c8955e8488b45e84883c4285dc3554889e54883ec2848897df848c745f0000000004c8b55f04c8955e84c8b55f84c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b45d84883c4285dc3554889e54883ec2848897df848c745f0080000004c8b55f84c8b5df04d0fafd34c8955e84c8b15ea4300004c8b5de84d01da4c8955e0488b7de0e800000000488945d8488b45d84883c4285dc3554889e54883ec1048897df8488b7df8488b35a1430000488b0437488945f0488b45f04883c4105dc3554889e54883ec1048897df8488b7df8488b3580430000488b0437488945f0488b45f04883c4105dc3554889e54883ec3048897df8488975f048c745e8080000004c8b55f04c8b5de84d0fafd34c8955e04c8b15474300004c8b5de04d01da4c8955d8488b7df8488b75d8488b0437488945d0488b45d04883c4305dc3554889e54883ec1848897df8488975f0488b7df8488b35f7420000488b55f048891437488b45e84883c4185dc3554889e54883ec1848897df8488975f0488b7df8488b35d2420000488b55f048891437488b45e84883c4185dc3554889e54883ec3848897df8488975f0488955e848c745e0080000004c8b55f04c8b5de04d0fafd34c8955d84c8b15954200004c8b5dd84d01da4c8955d0488b7df8488b75d0488b55e848891437488b45c84883c4385dc3554889e54881ec8800000048897df8488b7df8e8b9feffff488945f04c8b55f04c8955e8eb00eb0048c745e0000000004c8b55e84c8b5de04d39da0f9ec04c0fb6d04c8955d84c8b55d84983fa017402eb02eb7848c745d0010000004c8b55e84c8b5dd04d29da4c8955c8488b7df8488b75c8e882feffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f95c04c0fb6d04c8955b04c8b55b04983fa017402eb02eb2448c745a8010000004c8b55e84c8b5da84d29da4c8955a04c8b55a04c8955e8e95affffff488b7df8488b75e8e8a2feffff4889459848c74590000000004c8b55e84c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb1e48c7458000000000488b7df8488b7580e832feffff48898578ffffffeb00488b45f84881c4880000005dc3554889e54881ec3801000048897df849ba00000000000000804c8955f04c8b55f049f7da4c8955e84c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017405e98c00000048c745d803000000488b7dd8e8cbfcffff488945d04c8b55d04c8955c848c745c00200000048c745b808000000488b7dc8488b75c0488b55b8e8ebfdffff488945b048c745a8010000004c8b55a849f7da4c8955a0488b7dc8488b75a0e86dfdffff4889459848c7459003000000488b7dc8488b7590e881fdffff48894588488b45c84881c4380100005dc348c7458003000000488b7d80e83ffcffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b55f84c8b9d68ffffff4d39da0f94c04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb10488b8570ffffff4881c4380100005dc34c8b55f84c899558ffffff48c78550ffffff010000004c8b9550ffffff4c899548ffffff48c78540ffffff000000004c8b9558ffffff4c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb4b48c78530ffffff010000004c8b9530ffffff49f7da4c899528ffffff4c8b9528ffffff4c899548ffffff4c8b9558ffffff49f7da4c899520ffffff4c8b9520ffffff4c899558ffffffeb0048c78518ffffff000000004c8b9518ffffff4c899510ffffffeb00eb0048c78508ffffff000000004c8b9558ffffff4c8b9d08ffffff4d39da0f94c04c0fb6d04c899500ffffff4c8b9500ffffff4983fa017402eb05e9a50000004c8b9558ffffff4c8b1df03e00004c89d0489949f7fb4989d24c8995f8feffff488bbd70ffffff488bb510ffffff488b95f8feffffe8f4fbffff488985f0feffff4c8b9558ffffff4c8b1daf3e00004c89d0489949f7fb4989c24c8995e8feffff4c8b95e8feffff4c899558ffffff48c785e0feffff010000004c8b9510ffffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c899510ffffffe91bffffff488bbd70ffffff488bb548ffffffe81cfbffff488985d0feffff488bbd70ffffff488bb510ffffffe82ffbffff488985c8feffff488b8570ffffff4881c4380100005dc3554889e54883ec3848897df8488b7df8e87af8ffff488945f048c745e8000000004c8b55f04c8b5de84d39da0f95c04c0fb6d04c8955e04c8b55e04983fa017402eb17488b7df8e81ff9ffff488945d8488b45d84883c4385dc3488b7df8e8a5f8ffff488945d0488b7dd0e85ffcffff488945c8488b45c84883c4385dc3554889e54881ec5001000048897df8488b7df8e807fbffff488945f0488b7df8e8cbf9ffff488945e84c8b55e84c8955e048c745d8000000004c8b55e04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb1548c745c800000000488b45c84881c4500100005dc348c745c0030000004c8b55e04c8b5dc04d39da0f9fc04c0fb6d04c8955b84c8b55b84983fa017402eb1a488b7df8e892f8ffff488945b0488b45b04881c4500100005dc348c745a800000000488b7df8488b75a8e850f9ffff488945a04c8b55a04c89559848c74590000000004c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff010000004c8b55e04c8b9d70ffffff4d39da0f9fc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb2f48c78560ffffff01000000488b7df8488bb560ffffffe8d0f8ffff48898558ffffff4c8b9558ffffff4c895588eb0048c78550ffffff020000004c8b55e04c8b9d50ffffff4d39da0f9fc04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb3248c78540ffffff02000000488b7df8488bb540ffffffe86bf8ffff48898538ffffff4c8b9538ffffff4c899578ffffffeb0048c78530ffffff030000004c8b9578ffffff4c8b9d30ffffff4d39da0f9fc04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017402eb20488b7df8e82ef7ffff48898520ffffff488b8520ffffff4881c4500100005dc34c8b55884c8b1d6e3b00004d0fafd34c899518ffffff4c8b55984c8b9d18ffffff4d01da4c899510ffffff4c8b9578ffffff4c8b1d483b00004d0fafd34c899508ffffff4c8b9510ffffff4c8b9d08ffffff4d01da4c899500ffffff4c8b9500ffffff4c8995f8feffff488b7df8e836f7ffff488985f0feffff48c785e8feffff000000004c8b95f0feffff4c8b9de8feffff4d39da0f9cc04c0fb6d04c8995e0feffff4c8b95e0feffff4983fa017402eb214c8b95f8feffff49f7da4c8995d8feffff4c8b95d8feffff4c8995f8feffffeb004c8b95f8feffff4c8b1daf3a00004d39da0f9cc04c0fb6d04c8995d0feffff4c8b95f8feffff4c8b1d883a00004d39da0f9fc04c0fb6d04c8995c8feffff4c8b95d0feffff4c8b9dc8feffff4d09da4c8995c0feffff4c8b95c0feffff4983fa017402eb20488b7df8e8d5f5ffff488985b8feffff488b85b8feffff4881c4500100005dc3488bbdf8feffffe83cf5ffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54883ec7848897df8488b7df8e841f6ffff488945f04c8b55f04c8955e8488b7de8e8b6f5ffff488945e04c8b55e04c8955d8488b7df8e8eef5ffff488945d0488b7dd8488b75d0e883f6ffff488945c8488b7dd8488b75e8e89ff6ffff488945c048c745b8000000004c8b55b84c8955b0eb00eb004c8b55b04c8b5de84d39da0f9dc04c0fb6d04c8955a84c8b55a84983fa017402eb02eb47488b7df8488b75b0e8d5f5ffff488945a0488b7dd8488b75b0488b55a0e86ef6ffff4889459848c74590010000004c8b55b04c8b5d904d01da4c8955884c8b55884c8955b0eb93488b45d84883c4785dc3554889e54881ecd000000048897df8488975f0488b7df8e84cf5ffff488945e84c8b55e84c8955e0488b7df0e837f5ffff488945d84c8b55d84c8955d04c8b55e04c8b5dd04d39da0f9cc04c0fb6d04c8955c84c8b55c84983fa017402eb2048c745c0010000004c8b55c049f7da4c8955b8488b45b84881c4d00000005dc34c8b55e04c8b5dd04d39da0f9fc04c0fb6d04c8955b04c8b55b04983fa017402eb1548c745a801000000488b45a84881c4d00000005dc34c8b55e04c8955a0eb00eb0048c74598000000004c8b55a04c8b5d984d39da0f9ec04c0fb6d04c8955904c8b55904983fa017402eb05e90b01000048c74588010000004c8b55a04c8b5d884d29da4c8955804c8b55804c8955a0488b7df8488b75a0e874f4ffff48898578ffffff4c8b9578ffffff4c899570ffffff488b7df0488b75a0e852f4ffff48898568ffffff4c8b9568ffffff4c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d39da0f9cc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb2c48c78550ffffff010000004c8b9550ffffff49f7da4c899548ffffff488b8548ffffff4881c4d00000005dc34c8b9570ffffff4c8b9d60ffffff4d39da0f9fc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb1b48c78538ffffff01000000488b8538ffffff4881c4d00000005dc3e9c4feffff48c78530ffffff00000000488b8530ffffff4881c4d00000005dc3554889e54881ecc800000048897df8488975f0488b7df8e8c7f8ffff488945e84c8b55e84c8955e0488b7df0e8b2f8ffff488945d84c8b55d84c8955d0488b7de0e8e2f2ffff488945c84c8b55c84c8955c0488b7dd0e8cdf2ffff488945b84c8b55b84c8955b04c8b55c04c8b5db04d39da0f9cc04c0fb6d04c8955a84c8b55a84983fa017402eb2048c745a0010000004c8b55a049f7da4c895598488b45984881c4c80000005dc34c8b55c04c8b5db04d39da0f9fc04c0fb6d04c8955904c8b55904983fa017402eb1548c7458801000000488b45884881c4c80000005dc348c74580000000004c8b55c04c8b5d804d39da0f94c04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb1b48c78570ffffff00000000488b8570ffffff4881c4c80000005dc3488b7de0488b75d0e8b1fcffff48898568ffffff4c8b9568ffffff4c899560ffffff48c78558ffffff000000004c8b55c04c8b9d58ffffff4d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb344c8b9560ffffff49f7da4c899548ffffff488bbd48ffffffe880f0ffff48898540ffffff488b8540ffffff4881c4c80000005dc3488bbd60ffffffe85df0ffff48898538ffffff488b8538ffffff4881c4c80000005dc3554889e54881ec5001000048897df8488975f0488b7df8e85bf1ffff488945e84c8b55e84c8955e0488b7df0e846f1ffff488945d84c8b55d84c8955d04c8b55e04c8955c84c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb0a4c8b55d04c8955c8eb0048c745b8010000004c8b55c84c8b5db84d01da4c8955b0488b7db0e870f0ffff488945a84c8b55a84c8955a048c74598000000004c8b55984c89559048c74588000000004c8b55884c895580eb00eb004c8b55804c8b5dc84d39da0f9dc04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb05e99b01000048c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffff4c8b55804c8b5de04d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb24488b7df8488b7580e84bf0ffff48898548ffffff4c8b9548ffffff4c899568ffffffeb004c8b55804c8b5dd04d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb24488b7df0488b7580e8ffefffff48898538ffffff4c8b9538ffffff4c899558ffffffeb004c8b9568ffffff4c8b9d58ffffff4d01da4c899530ffffff4c8b9530ffffff4c8b5d904d01da4c899528ffffff4c8b9528ffffff4c899520ffffff4c8b9520ffffff4c8b1d1d3300004c89d0489949f7fb4989d24c899518ffffff488b7da0488b7580488b9518ffffffe827f0ffff48898510ffffff4c8b9520ffffff4c8b1de23200004c89d0489949f7fb4989c24c899508ffffff4c8b9508ffffff4c89559048c78500ffffff010000004c8b55804c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c895580e936feffff48c785f0feffff000000004c8b55904c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb45488b7da0488b75c8488b5590e87eefffff488985e0feffff48c785d8feffff010000004c8b55c84c8b9dd8feffff4d01da4c8995d0feffff4c8b95d0feffff4c8955c8eb00488b7da0488b75c8e810efffff488985c8feffff48c785c0feffff01000000488b7da0488bb5c0feffffe8c1eeffff488985b8feffff488b7da0e863efffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54881ec1001000048897df8488975f0488b7df8e801eeffff488945e84c8b55e84c8955e0488b7df0e8ecedffff488945d84c8b55d84c8955d0488b7de0e861edffff488945c84c8b55c84c8955c048c745b8000000004c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb004c8b55a04c8b5de04d39da0f9dc04c0fb6d04c8955984c8b55984983fa017402eb05e98a010000488b7df8488b75a0e89cedffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff4c8b55a04c8b5dd04d39da0f9cc04c0fb6d04c899570ffffff4c8b9570ffffff4983fa017402eb24488b7df0488b75a0e848edffff48898568ffffff4c8b9568ffffff4c899578ffffffeb004c8b55884c8b9d78ffffff4d29da4c899560ffffff4c8b9560ffffff4c8b5db04d29da4c899558ffffff4c8b9558ffffff4c899550ffffff48c78548ffffff000000004c8b9550ffffff4c8b9d48ffffff4d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb3e4c8b9550ffffff4c8b1d303000004d01da4c899538ffffff4c8b9538ffffff4c899550ffffff48c78530ffffff010000004c8b9530ffffff4c8955b0eb1848c78528ffffff000000004c8b9528ffffff4c8955b0eb00488b7dc0488b75a0488b9550ffffffe804edffff48898520ffffff48c78518ffffff010000004c8b55a04c8b9d18ffffff4d01da4c899510ffffff4c8b9510ffffff4c8955a0e94dfeffff488b7dc0488b75e0e893ecffff48898508ffffff48c78500ffffff01000000488b7dc0488bb500ffffffe844ecffff488985f8feffff488b7dc0e8e6ecffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec1001000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb0d488b45f04881c4100100005dc348c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb0d488b45f84881c4100100005dc3488b7df8e8a8f0ffff488945c84c8b55c84c8955c0488b7df0e893f0ffff488945b84c8b55b84c8955b0488b7dc0e8c3eaffff488945a84c8b55a84c8955a0488b7db0e8aeeaffff488945984c8b55984c8955904c8b55a04c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb56488b7dc0488b75b0e825f9ffff488945804c8b55804c899578ffffff488bbd78ffffff488b75a0e8faeaffff48898570ffffff488bbd78ffffffe87af0ffff48898568ffffff488b8568ffffff4881c4100100005dc3488b7dc0488b75b0e8def4ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9558ffffff4c8b9d50ffffff4d39da0f94c04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb1b48c78540ffffff00000000488b8540ffffff4881c4100100005dc348c78538ffffff000000004c8b9558ffffff4c8b9d38ffffff4d39da0f9fc04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb5c488b7dc0488b75b0e87afbffff48898528ffffff4c8b9528ffffff4c899520ffffff488bbd20ffffff488b75a0e8efe9ffff48898518ffffff488bbd20ffffffe86fefffff48898510ffffff488b8510ffffff4881c4100100005dc3488b7db0488b75c0e81efbffff48898508ffffff4c8b9508ffffff4c899500ffffff488bbd00ffffff488b7590e893e9ffff488985f8feffff488bbd00ffffffe813efffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec9000000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1548c745e000000000488b45e04881c4900000005dc3488b7df8e8b6e6ffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb32488b7df8e8f8e6ffff488945c04c8b55c049f7da4c8955b8488b7db8e815e7ffff488945b0488b45b04881c4900000005dc3488b7df8e829e7ffff488945a84c8b55a84c8955a0488b7da0e8c1f1ffff488945984c8b55984c895590488b7da0e8d9e7ffff488945884c8b558849f7da4c895580488b7d90488b7580e863e8ffff48898578ffffff488b7d90e8e6edffff48898570ffffff488b8570ffffff4881c4900000005dc3554889e54883ec2048897df8488975f0488b7df0e8bafeffff488945e8488b7df8488b75e8e8f5fbffff488945e0488b45e04883c4205dc3554889e54881ec1802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4180200005dc3488b7df8e891ecffff488945b84c8b55b84c8955b0488b7df0e87cecffff488945a84c8b55a84c8955a0488b7db0e8d5e6ffff488945984c8b55984c895590488b7da0e8c0e6ffff488945884c8b55884c8955804c8b55904c8b5d804d01da4c899578ffffff488bbd78ffffffe820e6ffff48898570ffffff4c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffffeb00eb004c8b9558ffffff4c8b5d904d39da0f9dc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb05e96803000048c78548ffffff000000004c8b9548ffffff4c899540ffffff488b7db0488bb558ffffffe834e6ffff48898538ffffff4c8b9538ffffff4c899530ffffff48c78528ffffff000000004c8b9528ffffff4c899520ffffffeb00eb004c8b9520ffffff4c8b5d804d39da0f9dc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb05e95d0100004c8b9558ffffff4c8b9d20ffffff4d01da4c899510ffffff488bbd68ffffff488bb510ffffffe8a7e5ffff48898508ffffff488b7da0488bb520ffffffe890e5ffff48898500ffffff4c8b9530ffffff4c8b9d00ffffff4d0fafd34c8995f8feffff4c8b9508ffffff4c8b9df8feffff4d01da4c8995f0feffff4c8b95f0feffff4c8b9d40ffffff4d01da4c8995e8feffff4c8b95e8feffff4c8995e0feffff4c8b9558ffffff4c8b9d20ffffff4d01da4c8995d8feffff4c8b95e0feffff4c8b1d8a2800004c89d0489949f7fb4989d24c8995d0feffff488bbd68ffffff488bb5d8feffff488b95d0feffffe88ee5ffff488985c8feffff4c8b95e0feffff4c8b1d492800004c89d0489949f7fb4989c24c8995c0feffff4c8b95c0feffff4c899540ffffff48c785b8feffff010000004c8b9520ffffff4c8b9db8feffff4d01da4c8995b0feffff4c8b95b0feffff4c899520ffffffe971feffff4c8b9558ffffff4c8b5d804d01da4c8995a8feffff4c8b95a8feffff4c8995a0feffffeb00eb0048c78598feffff000000004c8b9540ffffff4c8b9d98feffff4d39da0f94c04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9e5000000488bbd68ffffff488bb5a0feffffe8fde3ffff48898588feffff4c8b9588feffff4c8b9d40ffffff4d01da4c899580feffff4c8b9580feffff4c899578feffff4c8b9578feffff4c8b1d402700004c89d0489949f7fb4989d24c899570feffff488bbd68ffffff488bb5a0feffff488b9570feffffe844e4ffff48898568feffff4c8b9578feffff4c8b1dff2600004c89d0489949f7fb4989c24c899560feffff4c8b9560feffff4c899540ffffff48c78558feffff010000004c8b95a0feffff4c8b9d58feffff4d01da4c899550feffff4c8b9550feffff4c8995a0feffffe9dbfeffff48c78548feffff010000004c8b9558ffffff4c8b9d48feffff4d01da4c899540feffff4c8b9540feffff4c899558ffffffe966fcffff4c8b55904c8b5d804d01da4c899538feffff488bbd68ffffff488bb538feffffe851e3ffff48898530feffff48c78528feffff010000004c8b9528feffff4c899520feffff488b7db0e855e2ffff48898518feffff488b7da0e845e2ffff48898510feffff4c8b9518feffff4c8b9d10feffff4d39da0f95c04c0fb6d04c899508feffff4c8b9508feffff4983fa017402eb2c48c78500feffff010000004c8b9500feffff49f7da4c8995f8fdffff4c8b95f8fdffff4c899520feffffeb00488bbd68ffffff488bb520feffffe877e2ffff488985f0fdffff488bbd68ffffffe8f7e7ffff488985e8fdffff488b85e8fdffff4881c4180200005dc3554889e54883ec7848897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1248c745e001000000488b45e04883c4785dc3488b7df8e8a0dfffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8e833e0ffff488945b84c8b55b84c8955b0488b7db0e821e1ffff488945a848c745a0000000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55984983fa017402eb1248c7459001000000488b45904883c4785dc348c7458800000000488b45884883c4785dc3554889e54883ec5048897df848c745f0010000004c8b55f04c8955e848c745e0000000004c8b55e04c8955d8eb00eb004c8b55d84c8b5df84d39da0f9dc04c0fb6d04c8955d04c8b55d04983fa017402eb02eb4148c745c8020000004c8b55e84c8b5dc84d0fafd34c8955c04c8b55c04c8955e848c745b8010000004c8b55d84c8b5db84d01da4c8955b04c8b55b04c8955d8eb99488b45e84883c4505dc3554889e54881ecb800000048897df8488b7df8e818e0ffff488945f04c8b55f04c8955e848c745e0000000004c8b55e84c8b5de04d39da0f94c04c0fb6d04c8955d84c8b55d84983fa017402eb1548c745d000000000488b45d04881c4b80000005dc348c745c8010000004c8b55e84c8b5dc84d29da4c8955c0488b7df8488b75c0e8d2dfffff488945b84c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb0048c74598000000004c8b55b04c8b5d984d39da0f94c04c0fb6d04c8955904c8b55904983fa017402eb02eb5748c74588020000004c8b55b04c8b5d884c89d0489949f7fb4989c24c8955804c8b55804c8955b048c78578ffffff010000004c8b55a04c8b9d78ffffff4d01da4c899570ffffff4c8b9570ffffff4c8955a0e97bffffff48c78568ffffff010000004c8b55e84c8b9d68ffffff4d29da4c899560ffffff48c78558ffffff1e0000004c8b9560ffffff4c8b9d58ffffff4d0fafd34c899550ffffff4c8b9550ffffff4c8b5da04d01da4c899548ffffff488b8548ffffff4881c4b80000005dc3554889e54883ec7848897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e85ddeffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8488b75d8e841deffff488945b848c745b01e0000004c8b55f04c8b5db04c89d0489949f7fb4989d24c8955a8488b7da8e81dfdffff488945a04c8b55b84c8b5da04c89d0489949f7fb4989c24c89559848c74590020000004c8b55984c8b5d904c89d0489949f7fb4989d24c895588488b45884883c4785dc3554889e54883ec7048897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e868ddffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb064883c4705dc3488b7df8488b75d8e858ddffff488945c048c745b81e0000004c8b55f04c8b5db84c89d0489949f7fb4989d24c8955b0488b7db0e834fcffff488945a84c8b55c04c8b5da84d01da4c8955a04c8b55a04c895598488b7df8488b75d8488b5598e8aeddffff48894590488b45904883c4705dc3554889e54881ec0001000048897df8488b7df8e8b1dcffff488945f04c8b55f04c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b7dd8e80fdcffff488945d04c8b55d04c8955c848c745c0000000004c8b55c04c8955b848c745b0000000004c8b55b04c8955a8eb00eb004c8b55a84c8b5de84d39da0f9dc04c0fb6d04c8955a04c8b55a04983fa017402eb05e9d9000000488b7df8488b75a8e84adcffff4889459848c74590020000004c8b55984c8b5d904d0fafd34c8955884c8b55884c8b5db84d01da4c8955804c8b55804c899578ffffff4c8b9578ffffff4c8b1d841f00004c89d0489949f7fb4989d24c899570ffffff488b7dc8488b75a8488b9570ffffffe88edcffff48898568ffffff4c8b9578ffffff4c8b1d491f00004c89d0489949f7fb4989c24c899560ffffff4c8b9560ffffff4c8955b848c78558ffffff010000004c8b55a84c8b9d58ffffff4d01da4c899550ffffff4c8b9550ffffff4c8955a8e9fefeffff488b7dc8488b75e8e8f2dbffff48898548ffffff48c78540ffffff000000004c8b55b84c8b9d40ffffff4d39da0f95c04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb51488b7dc8488b75e8488b55b8e8d1dbffff48898530ffffff48c78528ffffff010000004c8b55e84c8b9d28ffffff4d01da4c899520ffffff488b7dc8488bb520ffffffe86ddbffff48898518ffffffeb0048c78510ffffff01000000488b7dc8488bb510ffffffe81cdbffff48898508ffffff488b7dc8e8bedbffff48898500ffffff488b8500ffffff4881c4000100005dc3554889e54883ec2048897df8488975f0488b7df0e82ed9ffff488945e8488b7df8488b75e8e8aeeeffff488945e0488b45e04883c4205dc3554889e54881ec0802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4080200005dc3488b7df8e8d9d7ffff488945b848c745b0000000004c8b55b84c8b5db04d39da0f94c04c0fb6d04c8955a84c8b55a84983fa017405e921020000488b7df8e818d8ffff488945a04c8b55a04c89559848c74590000000004c8b55f04c8b5d904d39da0f9fc04c0fb6d04c8955884c8b150c1d00004c8b5df04c89d0489949f7fb4989c24c8955804c8b55984c8b5d804d39da0f9dc04c0fb6d04c899578ffffff4c8b55884c8b9d78ffffff4d21da4c899570ffffff4c8b15bc1c00004c8b5df04c89d0489949f7fb4989c24c899568ffffff4c8b55984c8b9d68ffffff4d39da0f9ec04c0fb6d04c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d21da4c899558ffffff4c8b9558ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899550ffffff488bbd50ffffffe85cd7ffff48898548ffffff488b8548ffffff4881c4080200005dc348c78540ffffff000000004c8b55f04c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b15071c00004c8b5df04c89d0489949f7fb4989c24c899530ffffff4c8b55984c8b9d30ffffff4d39da0f9ec04c0fb6d04c899528ffffff4c8b9538ffffff4c8b9d28ffffff4d21da4c899520ffffff4c8b15ae1b00004c8b5df04c89d0489949f7fb4989c24c899518ffffff4c8b55984c8b9d18ffffff4d39da0f9dc04c0fb6d04c899510ffffff4c8b9520ffffff4c8b9d10ffffff4d21da4c899508ffffff4c8b9508ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899500ffffff488bbd00ffffffe84ed6ffff488985f8feffff488b85f8feffff4881c4080200005dc3eb00488b7df8e8efdcffff488985f0feffff4c8b95f0feffff4c8995e8feffff488bbde8feffffe83cd7ffff488985e0feffff4c8b95e0feffff4c8995d8feffff48c785d0feffff010000004c8b95d8feffff4c8b9dd0feffff4d01da4c8995c8feffff488bbdc8feffffe882d6ffff488985c0feffff4c8b95c0feffff4c8995b8feffff48c785b0feffff000000004c8b95b0feffff4c8995a8feffff48c785a0feffff000000004c8b95a0feffff4c899598feffffeb00eb004c8b9598feffff4c8b9dd8feffff4d39da0f9dc04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9fb000000488bbde8feffff488bb598feffffe890d6ffff48898588feffff4c8b9588feffff4c8b5df04d0fafd34c899580feffff4c8b9580feffff4c8b9da8feffff4d01da4c899578feffff4c8b9578feffff4c899570feffff4c8b9570feffff4c8b1dbd1900004c89d0489949f7fb4989d24c899568feffff488bbdb8feffff488bb598feffff488b9568feffffe8c1d6ffff48898560feffff4c8b9570feffff4c8b1d7c1900004c89d0489949f7fb4989c24c899558feffff4c8b9558feffff4c8995a8feffff48c78550feffff010000004c8b9598feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899598feffffe9d0feffff488bbdb8feffff488bb5d8feffffe816d6ffff48898540feffff48c78538feffff000000004c8b95a8feffff4c8b9d38feffff4d39da0f95c04c0fb6d04c899530feffff4c8b9530feffff4983fa017402eb60488bbdb8feffff488bb5d8feffff488b95a8feffffe8e9d5ffff48898528feffff48c78520feffff010000004c8b95d8feffff4c8b9d20feffff4d01da4c899518feffff488bbdb8feffff488bb518feffffe87fd5ffff48898510feffffeb00488bbde8feffffe897d4ffff48898508feffff488bbdb8feffff488bb508feffffe823d5ffff48898500feffff488bbdb8feffffe8a3daffff488985f8fdffff488b85f8fdffff4881c4080200005dc3554889e54881ec3801000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb3b48c745d8010000004c8b55d84c8915f317000048c745d0000000004c8b55d04c8915e817000048c745c800000000488b45c84881c4380100005dc3488b7df8e81cd2ffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb68488b7df8e85ed2ffff488945a84c8b55a84c8955a04c8b55a04c8b5df04c89d0489949f7fb4989d24c8955984c8b55984c8915651700004c8b55a04c8b5df04c89d0489949f7fb4989c24c895590488b7d90e845d2ffff48894588488b45884881c4380100005dc3488b7df8e8eed8ffff488945804c8b55804c899578ffffff488bbd78ffffffe841d3ffff48898570ffffff4c8b9570ffffff4c899568ffffff488bbd68ffffffe8aad2ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9550ffffff4c899548ffffff4c8b9568ffffff4c899540ffffffeb00eb0048c78538ffffff000000004c8b9540ffffff4c8b9d38ffffff4d39da0f9ec04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb05e9f800000048c78528ffffff010000004c8b9540ffffff4c8b9d28ffffff4d29da4c899520ffffff4c8b9520ffffff4c899540ffffff4c8b9548ffffff4c8b1d0a1600004d0fafd34c899518ffffff488bbd78ffffff488bb540ffffffe86ed2ffff48898510ffffff4c8b9518ffffff4c8b9d10ffffff4d01da4c899508ffffff4c8b9508ffffff4c899500ffffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989c24c8995f8feffff488bbd58ffffff488bb540ffffff488b95f8feffffe8b8d2ffff488985f0feffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989d24c8995e8feffff4c8b95e8feffff4c899548ffffffe9c8feffff488bbd58ffffff488bb568ffffffe841d2ffff488985e0feffff488bbd78ffffffe85bd1ffff488985d8feffff488bbd58ffffff488bb5d8feffffe8e7d1ffff488985d0feffff4c8b9548ffffff4c891524150000488bbd58ffffffe859d7ffff488985c8feffff488b85c8feffff4881c4380100005dc3554889e54881ec2001000048897df8488b7df8488b47f84883e801488945f04c8b55f04c8955e848c745e0000000004c8b55e04c8955d848c745d0000000004c8b55d04c8955c848c745c0000000004c8b55e84c8b5dc04d39da0f9fc04c0fb6d04c8955b848c745b000000000488b7df8488b75b0480fb60437488945a848c745a02d0000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55b84c8b5d984d21da4c8955904c8b55904983fa017402eb2248c74588010000004c8b55884c8955c848c74580010000004c8b55804c8955d8eb0048c78578ffffff000000004c8b9578ffffff4c899570ffffffeb00eb004c8b55d84c8b5de84d39da0f9dc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb05e950010000488b7df8488b75d8480fb6043748898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff300000004c8b9558ffffff4c8b9d50ffffff4d39da0f9cc04c0fb6d04c899548ffffff48c78540ffffff390000004c8b9558ffffff4c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9548ffffff4c8b9d38ffffff4d09da4c899530ffffff4c8b9530ffffff4983fa017402eb05e9ae00000048c78528ffffff0a000000488bbd70ffffff488bb528ffffffe80ff5ffff48898520ffffff4c8b9520ffffff4c899570ffffff48c78518ffffff300000004c8b9558ffffff4c8b9d18ffffff4d29da4c899510ffffff488bbd70ffffff488bb510ffffffe88cf4ffff48898508ffffff4c8b9508ffffff4c899570ffffff48c78500ffffff010000004c8b55d84c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c8955d8e981feffff48c785f0feffff000000004c8b55c84c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb23488bbd70ffffffe891e5ffff488985e0feffff4c8b95e0feffff4c899570ffffffeb00488b8570ffffff4881c4200100005dc3554889e54881ec1802000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb5148c745e002000000488b7de0e800000000488945d84c8b55d84c8955d048c745c80000000048c745c030000000488b7dd0488b75c8488b55c088143748c745b800000000488b45d04881c4180200005dc348c745b0000000004c8b55b04c8955a8488b7df8e845d3ffff488945a04c8b55a04c895598488b7d98e875cdffff4889459048c74588000000004c8b55904c8b5d884d39da0f9cc04c0fb6d04c8955804c8b55804983fa017402eb4648c78578ffffff010000004c8b9578ffffff4c8955a8488b7df8e865e4ffff48898570ffffff488bbd70ffffffe8d0d2ffff48898568ffffff4c8b9568ffffff4c895598eb00488b7d98e821cdffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff0a0000004c8b9558ffffff4c8b9d50ffffff4d0fafd34c899548ffffff48c78540ffffff040000004c8b9548ffffff4c8b9d40ffffff4d01da4c899538ffffff4c8b9538ffffff4c899530ffffff488bbd30ffffffe80000000048898528ffffff4c8b9528ffffff4c899520ffffff48c78518ffffff010000004c8b9530ffffff4c8b9d18ffffff4d29da4c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff00000000488bbd20ffffff488bb508ffffff488b9500ffffff88143748c785f8feffff00000000488b7d98e83ed2ffff488985f0feffff4c8b95f0feffff4c8995e8feffffeb00eb0048c785e0feffff000000004c8b95e8feffff4c8b9de0feffff4d39da0f94c04c0fb6d04c8995d8feffff4c8b95d8feffff4983fa017402eb05e9bd00000048c785d0feffff0a000000488bbde8feffff488bb5d0feffffe83df7ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b9508ffffff4c8b9db8feffff4d29da4c8995b0feffff4c8b95b0feffff4c899508ffffff48c785a8feffff300000004c8b15300f00004c8b9da8feffff4d01da4c8995a0feffff488bbd20ffffff488bb508ffffff488b95a0feffff88143748c78598feffff000000004c8b95c0feffff4c8995e8feffffe903ffffff48c78590feffff000000004c8b55a84c8b9d90feffff4d39da0f95c04c0fb6d04c899588feffff4c8b9588feffff4983fa017402eb6148c78580feffff010000004c8b9508ffffff4c8b9d80feffff4d29da4c899578feffff4c8b9578feffff4c899508ffffff48c78570feffff2d000000488bbd20ffffff488bb508ffffff488b9570feffff88143748c78568feffff00000000eb0048c78560feffff010000004c8b9530ffffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c8b9d08ffffff4d29da4c899550feffff4c8b9550feffff4c899548feffff48c78540feffff010000004c8b9548feffff4c8b9d40feffff4d01da4c899538feffff488bbd38feffffe80000000048898530feffff4c8b9530feffff4c899528feffff48c78520feffff000000004c8b9520feffff4c899518feffffeb00eb004c8b9518feffff4c8b9d48feffff4d39da0f9dc04c0fb6d04c899510feffff4c8b9510feffff4983fa017402eb05e98b0000004c8b9508ffffff4c8b9d18feffff4d01da4c899508feffff488bbd20ffffff488bb508feffff480fb6043748898500feffff488bbd28feffff488bb518feffff488b9500feffff88143748c785f8fdffff0000000048c785f0fdffff010000004c8b9518feffff4c8b9df0fdffff4d01da4c8995e8fdffff4c8b95e8fdffff4c899518feffffe940ffffff488b8528feffff4881c4180200005dc3554889e54881ec5802000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb404c8d15d60b00004c8955d8488b7dd8e841c6ffff488945d048c745c8010000004c8b55c84c89156a0c000048c745c000000000488b45c04881c4580200005dc348c745b8000000004c8b55f84c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb2848c745a8000000004c8b55a84c8915200c000048c745a000000000488b45a04881c4580200005dc3488b7df8e8c5cdffff488945984c8b55984c895590488b7df0e8b0cdffff488945884c8b55884c895580488b7d90488b7580e89dd2ffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b9570ffffff4c8b9d68ffffff4d39da0f9cc04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb264c8b55f84c89157b0b000048c78558ffffff00000000488b8558ffffff4881c4580200005dc3488b7d90e85fc7ffff48898550ffffff4c8b9550ffffff4c899548ffffff488b7d80e841c7ffff48898540ffffff4c8b9540ffffff4c899538ffffff48c78530ffffff000000004c8b9570ffffff4c8b9d30ffffff4d39da0f94c04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017405e9af00000048c78520ffffff010000004c8b9520ffffff4c899518ffffff4c8b9548ffffff4c8b9d38ffffff4d39da0f95c04c0fb6d04c899510ffffff4c8b9510ffffff4983fa017402eb2c48c78508ffffff010000004c8b9508ffffff49f7da4c899500ffffff4c8b9500ffffff4c899518ffffffeb0048c785f8feffff000000004c8b95f8feffff4c89155c0a0000488bbd18ffffffe850c5ffff488985f0feffff488b85f0feffff4881c4580200005dc3488b7d90e861c6ffff488985e8feffff4c8b95e8feffff4c8995e0feffff48c785d8feffff010000004c8b95e0feffff4c8b9dd8feffff4d01da4c8995d0feffff488bbdd0feffffe8a7c5ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b95e0feffff4c8b9db8feffff4d01da4c8995b0feffff488bbdc0feffff488bb5b0feffffe87cc6ffff488985a8feffff48c785a0feffff00000000488bbda0feffffe815c8ffff48898598feffff4c8b9598feffff4c899590feffff488b7d90e866e5ffff48898588feffff4c8b9588feffff4c899580feffff4c8b9580feffff4c899578feffffeb00eb0048c78570feffff000000004c8b9578feffff4c8b9d70feffff4d39da0f9ec04c0fb6d04c899568feffff4c8b9568feffff4983fa017402eb05e99301000048c78560feffff010000004c8b9578feffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c899578feffff488bbd90feffffe82be8ffff48898550feffff4c8b9550feffff4c899590feffff488b7d90488bb578feffffe832e6ffff48898548feffff48c78540feffff000000004c8b9548feffff4c8b9d40feffff4d39da0f95c04c0fb6d04c899538feffff4c8b9538feffff4983fa017402eb5b488bbd90feffffe893caffff48898530feffff48c78528feffff01000000488bbd30feffff488bb528feffffe8e6e9ffff48898520feffff488bbd20feffffe8ddc9ffff48898518feffff4c8b9518feffff4c899590feffffeb00488bbd90feffff488b7580e8bcceffff48898510feffff48c78508feffff000000004c8b9510feffff4c8b9d08feffff4d39da0f9dc04c0fb6d04c899500feffff4c8b9500feffff4983fa017402eb41488bbd90feffff488b7580e8b7d5ffff488985f8fdffff4c8b95f8fdffff4c899590feffff488bbdc0feffff488bb578feffffe804e6ffff488985f0fdffffeb00e92dfeffff48c785e8fdffff010000004c8b95e0feffff4c8b9de8fdffff4d01da4c8995e0fdffff488bbdc0feffff488bb5e0fdffffe812c4ffff488985d8fdffff4c8b9548ffffff4c8b9d38ffffff4d0fafd34c8995d0fdffff488bbdc0feffff488bb5d0fdffffe8b2c3ffff488985c8fdffff488bbdc0feffffe832c9ffff488985c0fdffff4c8b95c0fdffff4c8995b8fdffff488bbd90feffff488bb548ffffffe877c3ffff488985b0fdffff488bbd90feffffe8f7c8ffff488985a8fdffff4c8b95a8fdffff4c8915a1060000488b85b8fdffff4881c4580200005dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e89af9ffff488945e84c8b55e84c8955e0488b45e04883c4205dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e867f9ffff488945e84c8b55e84c8955e0488b052e0600004883c4205dc3554889e54883ec3848897df84c8b55f84c8b1d010600004d39da0f9dc04c0fb6d04c8955f04c8b55f84c8b1de00500004d39da0f9ec04c0fb6d04c8955e84c8b55f04c8b5de84d21da4c8955e04c8b55e04983fa017402eb17488b7df8e8c6c0ffff488945d8488b45d84883c4385dc3488b7df8e841c4ffff488945d0488b7dd0e8e3c7ffff488945c8488b45c84883c4385dc3554889e54883ec4048897df848c745f0000000004c8b55f84c8b5df04d39da0f9dc04c0fb6d04c8955e84c8b55f84c8b1d470500004d39da0f9ec04c0fb6d04c8955e04c8b55e84c8b5de04d21da4c8955d84c8b55d84983fa017402eb17488b7df8e82dc0ffff488945d0488b45d04883c4405dc3488b7df8e8a8c3ffff488945c8488b7dc8e84ac7ffff488945c0488b45c04883c4405dc3554889e54881ec5001000048897df848c745f0000000004c8b55f04c8915d1040000488b7df8e893c6ffff488945e84c8b55e84c8955e0488b7de0e8ecc0ffff488945d84c8b55d84c8955d048c745c8030000004c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb404c8d15de0300004c8955b8488b7db8e829beffff488945b048c745a8010000004c8b55a84c89155204000048c745a000000000488b45a04881c4500100005dc348c7459800000000488b7de0488b7598e88ac0ffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff010000004c8b55d04c8b9d60ffffff4d39da0f9fc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb3248c78550ffffff01000000488b7de0488bb550ffffffe801c0ffff48898548ffffff4c8b9548ffffff4c899578ffffffeb0048c78540ffffff020000004c8b55d04c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb3248c78530ffffff02000000488b7de0488bb530ffffffe899bfffff48898528ffffff4c8b9528ffffff4c899568ffffffeb0048c78520ffffff030000004c8b9568ffffff4c8b9d20ffffff4d39da0f9fc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb554c8d15480200004c899510ffffff488bbd10ffffffe88dbcffff48898508ffffff48c78500ffffff010000004c8b9500ffffff4c8915ad02000048c785f8feffff00000000488b85f8feffff4881c4500100005dc34c8b9578ffffff4c8b1d640200004d0fafd34c8995f0feffff4c8b55884c8b9df0feffff4d01da4c8995e8feffff4c8b9568ffffff4c8b1d3e0200004d0fafd34c8995e0feffff4c8b95e8feffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c8995d0feffff488b7de0e82cbeffff488985c8feffff48c785c0feffff000000004c8b95c8feffff4c8b9dc0feffff4d39da0f9cc04c0fb6d04c8995b8feffff4c8b95b8feffff4983fa017402eb214c8b95d0feffff49f7da4c8995b0feffff4c8b95b0feffff4c8995d0feffffeb00488b85d0feffff4881c4500100005dc3554889e54883ec6048897df8488b7df8e8a3fcffff488945f04c8b55f04c8955e848c745e0000000004c8b157b0100004c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d000000000488b45d04883c4605dc348c745c8000000004c8b55e84c8b5dc84d39da0f9cc04c0fb6d04c8955c04c8b55c04983fa017402eb3d4c8d15b20000004c8955b8488b7db8e8cdbaffff488945b048c745a8010000004c8b55a84c8915f600000048c745a000000000488b45a04883c4605dc3488b45e84883c4605dc3000000000000000000000100000000000000000000000000000012000000000000006469766973696f6e206279207a65726f0a000000000000002300000000000000696e7465676572206f7574206f66206d616368696e652d776f72642072616e67650a0000000000002c00000000000000696e7465676572206f7574206f6620756e7369676e6564206d616368696e652d776f72642072616e67650a000000000000000000000000000800000000000000100000000000000000000040000000000000000000000010ffffffffffffff3f000000000000000000000000000000000000000000000000" + return "554889e54883ec5048897df848c745f00100000048c745e802000000488b7df8488b47f84883e801488945e0488b7df0488b75e8488b55f8488b4de04889f84889f74889d64889ca0f05488945d848c745d03c00000048c745c80100000048c745c00000000048c745b800000000488b7dd0488b75c8488b55c0488b4db84889f84889f74889d64889ca0f05488945b0488b45b04883c4505dc3554889e54883ec3848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989d24c8955e848c745e0000000004c8b55e84c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d001000000488b45d04883c4385dc348c745c800000000488b45c84883c4385dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989c24c8955e8488b45e84883c4185dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04d0fafd34c8955e8488b45e84883c4185dc3554889e54883ec2848897df84c8d153f4500004c8955f04c8b55f04c8955e848c745e0010000004c8b55f84c8b5de04d29da4c8955d84c8b55d84c8955e8488b45e84883c4285dc3554889e54883ec2848897df848c745f0000000004c8b55f04c8955e84c8b55f84c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b45d84883c4285dc3554889e54883ec2848897df848c745f0080000004c8b55f84c8b5df04d0fafd34c8955e84c8b153a4500004c8b5de84d01da4c8955e0488b7de0e800000000488945d8488b45d84883c4285dc3554889e54883ec1048897df8488b7df8488b35f1440000488b0437488945f0488b45f04883c4105dc3554889e54883ec1048897df8488b7df8488b35d0440000488b0437488945f0488b45f04883c4105dc3554889e54883ec3048897df8488975f048c745e8080000004c8b55f04c8b5de84d0fafd34c8955e04c8b15974400004c8b5de04d01da4c8955d8488b7df8488b75d8488b0437488945d0488b45d04883c4305dc3554889e54883ec1848897df8488975f0488b7df8488b3547440000488b55f048891437488b45e84883c4185dc3554889e54883ec1848897df8488975f0488b7df8488b3522440000488b55f048891437488b45e84883c4185dc3554889e54883ec3848897df8488975f0488955e848c745e0080000004c8b55f04c8b5de04d0fafd34c8955d84c8b15e54300004c8b5dd84d01da4c8955d0488b7df8488b75d0488b55e848891437488b45c84883c4385dc3554889e54881ec8800000048897df8488b7df8e8b9feffff488945f04c8b55f04c8955e8eb00eb0048c745e0000000004c8b55e84c8b5de04d39da0f9ec04c0fb6d04c8955d84c8b55d84983fa017402eb02eb7848c745d0010000004c8b55e84c8b5dd04d29da4c8955c8488b7df8488b75c8e882feffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f95c04c0fb6d04c8955b04c8b55b04983fa017402eb02eb2448c745a8010000004c8b55e84c8b5da84d29da4c8955a04c8b55a04c8955e8e95affffff488b7df8488b75e8e8a2feffff4889459848c74590000000004c8b55e84c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb1e48c7458000000000488b7df8488b7580e832feffff48898578ffffffeb00488b45f84881c4880000005dc3554889e54881ec3801000048897df849ba00000000000000804c8955f04c8b55f049f7da4c8955e84c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017405e98c00000048c745d803000000488b7dd8e8cbfcffff488945d04c8b55d04c8955c848c745c00200000048c745b808000000488b7dc8488b75c0488b55b8e8ebfdffff488945b048c745a8010000004c8b55a849f7da4c8955a0488b7dc8488b75a0e86dfdffff4889459848c7459003000000488b7dc8488b7590e881fdffff48894588488b45c84881c4380100005dc348c7458003000000488b7d80e83ffcffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b55f84c8b9d68ffffff4d39da0f94c04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb10488b8570ffffff4881c4380100005dc34c8b55f84c899558ffffff48c78550ffffff010000004c8b9550ffffff4c899548ffffff48c78540ffffff000000004c8b9558ffffff4c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb4b48c78530ffffff010000004c8b9530ffffff49f7da4c899528ffffff4c8b9528ffffff4c899548ffffff4c8b9558ffffff49f7da4c899520ffffff4c8b9520ffffff4c899558ffffffeb0048c78518ffffff000000004c8b9518ffffff4c899510ffffffeb00eb0048c78508ffffff000000004c8b9558ffffff4c8b9d08ffffff4d39da0f94c04c0fb6d04c899500ffffff4c8b9500ffffff4983fa017402eb05e9a50000004c8b9558ffffff4c8b1d404000004c89d0489949f7fb4989d24c8995f8feffff488bbd70ffffff488bb510ffffff488b95f8feffffe8f4fbffff488985f0feffff4c8b9558ffffff4c8b1dff3f00004c89d0489949f7fb4989c24c8995e8feffff4c8b95e8feffff4c899558ffffff48c785e0feffff010000004c8b9510ffffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c899510ffffffe91bffffff488bbd70ffffff488bb548ffffffe81cfbffff488985d0feffff488bbd70ffffff488bb510ffffffe82ffbffff488985c8feffff488b8570ffffff4881c4380100005dc3554889e54883ec3848897df8488b7df8e87af8ffff488945f048c745e8000000004c8b55f04c8b5de84d39da0f95c04c0fb6d04c8955e04c8b55e04983fa017402eb17488b7df8e81ff9ffff488945d8488b45d84883c4385dc3488b7df8e8a5f8ffff488945d0488b7dd0e85ffcffff488945c8488b45c84883c4385dc3554889e54881ec5001000048897df8488b7df8e807fbffff488945f0488b7df8e8cbf9ffff488945e84c8b55e84c8955e048c745d8000000004c8b55e04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb1548c745c800000000488b45c84881c4500100005dc348c745c0030000004c8b55e04c8b5dc04d39da0f9fc04c0fb6d04c8955b84c8b55b84983fa017402eb1a488b7df8e892f8ffff488945b0488b45b04881c4500100005dc348c745a800000000488b7df8488b75a8e850f9ffff488945a04c8b55a04c89559848c74590000000004c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff010000004c8b55e04c8b9d70ffffff4d39da0f9fc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb2f48c78560ffffff01000000488b7df8488bb560ffffffe8d0f8ffff48898558ffffff4c8b9558ffffff4c895588eb0048c78550ffffff020000004c8b55e04c8b9d50ffffff4d39da0f9fc04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb3248c78540ffffff02000000488b7df8488bb540ffffffe86bf8ffff48898538ffffff4c8b9538ffffff4c899578ffffffeb0048c78530ffffff030000004c8b9578ffffff4c8b9d30ffffff4d39da0f9fc04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017402eb20488b7df8e82ef7ffff48898520ffffff488b8520ffffff4881c4500100005dc34c8b55884c8b1dbe3c00004d0fafd34c899518ffffff4c8b55984c8b9d18ffffff4d01da4c899510ffffff4c8b9578ffffff4c8b1d983c00004d0fafd34c899508ffffff4c8b9510ffffff4c8b9d08ffffff4d01da4c899500ffffff4c8b9500ffffff4c8995f8feffff488b7df8e836f7ffff488985f0feffff48c785e8feffff000000004c8b95f0feffff4c8b9de8feffff4d39da0f9cc04c0fb6d04c8995e0feffff4c8b95e0feffff4983fa017402eb214c8b95f8feffff49f7da4c8995d8feffff4c8b95d8feffff4c8995f8feffffeb004c8b95f8feffff4c8b1dff3b00004d39da0f9cc04c0fb6d04c8995d0feffff4c8b95f8feffff4c8b1dd83b00004d39da0f9fc04c0fb6d04c8995c8feffff4c8b95d0feffff4c8b9dc8feffff4d09da4c8995c0feffff4c8b95c0feffff4983fa017402eb20488b7df8e8d5f5ffff488985b8feffff488b85b8feffff4881c4500100005dc3488bbdf8feffffe83cf5ffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54883ec7848897df8488b7df8e841f6ffff488945f04c8b55f04c8955e8488b7de8e8b6f5ffff488945e04c8b55e04c8955d8488b7df8e8eef5ffff488945d0488b7dd8488b75d0e883f6ffff488945c8488b7dd8488b75e8e89ff6ffff488945c048c745b8000000004c8b55b84c8955b0eb00eb004c8b55b04c8b5de84d39da0f9dc04c0fb6d04c8955a84c8b55a84983fa017402eb02eb47488b7df8488b75b0e8d5f5ffff488945a0488b7dd8488b75b0488b55a0e86ef6ffff4889459848c74590010000004c8b55b04c8b5d904d01da4c8955884c8b55884c8955b0eb93488b45d84883c4785dc3554889e54881ecd000000048897df8488975f0488b7df8e84cf5ffff488945e84c8b55e84c8955e0488b7df0e837f5ffff488945d84c8b55d84c8955d04c8b55e04c8b5dd04d39da0f9cc04c0fb6d04c8955c84c8b55c84983fa017402eb2048c745c0010000004c8b55c049f7da4c8955b8488b45b84881c4d00000005dc34c8b55e04c8b5dd04d39da0f9fc04c0fb6d04c8955b04c8b55b04983fa017402eb1548c745a801000000488b45a84881c4d00000005dc34c8b55e04c8955a0eb00eb0048c74598000000004c8b55a04c8b5d984d39da0f9ec04c0fb6d04c8955904c8b55904983fa017402eb05e90b01000048c74588010000004c8b55a04c8b5d884d29da4c8955804c8b55804c8955a0488b7df8488b75a0e874f4ffff48898578ffffff4c8b9578ffffff4c899570ffffff488b7df0488b75a0e852f4ffff48898568ffffff4c8b9568ffffff4c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d39da0f9cc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb2c48c78550ffffff010000004c8b9550ffffff49f7da4c899548ffffff488b8548ffffff4881c4d00000005dc34c8b9570ffffff4c8b9d60ffffff4d39da0f9fc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb1b48c78538ffffff01000000488b8538ffffff4881c4d00000005dc3e9c4feffff48c78530ffffff00000000488b8530ffffff4881c4d00000005dc3554889e54881ecc800000048897df8488975f0488b7df8e8c7f8ffff488945e84c8b55e84c8955e0488b7df0e8b2f8ffff488945d84c8b55d84c8955d0488b7de0e8e2f2ffff488945c84c8b55c84c8955c0488b7dd0e8cdf2ffff488945b84c8b55b84c8955b04c8b55c04c8b5db04d39da0f9cc04c0fb6d04c8955a84c8b55a84983fa017402eb2048c745a0010000004c8b55a049f7da4c895598488b45984881c4c80000005dc34c8b55c04c8b5db04d39da0f9fc04c0fb6d04c8955904c8b55904983fa017402eb1548c7458801000000488b45884881c4c80000005dc348c74580000000004c8b55c04c8b5d804d39da0f94c04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb1b48c78570ffffff00000000488b8570ffffff4881c4c80000005dc3488b7de0488b75d0e8b1fcffff48898568ffffff4c8b9568ffffff4c899560ffffff48c78558ffffff000000004c8b55c04c8b9d58ffffff4d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb344c8b9560ffffff49f7da4c899548ffffff488bbd48ffffffe880f0ffff48898540ffffff488b8540ffffff4881c4c80000005dc3488bbd60ffffffe85df0ffff48898538ffffff488b8538ffffff4881c4c80000005dc3554889e54881ec5001000048897df8488975f0488b7df8e85bf1ffff488945e84c8b55e84c8955e0488b7df0e846f1ffff488945d84c8b55d84c8955d04c8b55e04c8955c84c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb0a4c8b55d04c8955c8eb0048c745b8010000004c8b55c84c8b5db84d01da4c8955b0488b7db0e870f0ffff488945a84c8b55a84c8955a048c74598000000004c8b55984c89559048c74588000000004c8b55884c895580eb00eb004c8b55804c8b5dc84d39da0f9dc04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb05e99b01000048c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffff4c8b55804c8b5de04d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb24488b7df8488b7580e84bf0ffff48898548ffffff4c8b9548ffffff4c899568ffffffeb004c8b55804c8b5dd04d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb24488b7df0488b7580e8ffefffff48898538ffffff4c8b9538ffffff4c899558ffffffeb004c8b9568ffffff4c8b9d58ffffff4d01da4c899530ffffff4c8b9530ffffff4c8b5d904d01da4c899528ffffff4c8b9528ffffff4c899520ffffff4c8b9520ffffff4c8b1d6d3400004c89d0489949f7fb4989d24c899518ffffff488b7da0488b7580488b9518ffffffe827f0ffff48898510ffffff4c8b9520ffffff4c8b1d323400004c89d0489949f7fb4989c24c899508ffffff4c8b9508ffffff4c89559048c78500ffffff010000004c8b55804c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c895580e936feffff48c785f0feffff000000004c8b55904c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb45488b7da0488b75c8488b5590e87eefffff488985e0feffff48c785d8feffff010000004c8b55c84c8b9dd8feffff4d01da4c8995d0feffff4c8b95d0feffff4c8955c8eb00488b7da0488b75c8e810efffff488985c8feffff48c785c0feffff01000000488b7da0488bb5c0feffffe8c1eeffff488985b8feffff488b7da0e863efffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54881ec1001000048897df8488975f0488b7df8e801eeffff488945e84c8b55e84c8955e0488b7df0e8ecedffff488945d84c8b55d84c8955d0488b7de0e861edffff488945c84c8b55c84c8955c048c745b8000000004c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb004c8b55a04c8b5de04d39da0f9dc04c0fb6d04c8955984c8b55984983fa017402eb05e98a010000488b7df8488b75a0e89cedffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff4c8b55a04c8b5dd04d39da0f9cc04c0fb6d04c899570ffffff4c8b9570ffffff4983fa017402eb24488b7df0488b75a0e848edffff48898568ffffff4c8b9568ffffff4c899578ffffffeb004c8b55884c8b9d78ffffff4d29da4c899560ffffff4c8b9560ffffff4c8b5db04d29da4c899558ffffff4c8b9558ffffff4c899550ffffff48c78548ffffff000000004c8b9550ffffff4c8b9d48ffffff4d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb3e4c8b9550ffffff4c8b1d803100004d01da4c899538ffffff4c8b9538ffffff4c899550ffffff48c78530ffffff010000004c8b9530ffffff4c8955b0eb1848c78528ffffff000000004c8b9528ffffff4c8955b0eb00488b7dc0488b75a0488b9550ffffffe804edffff48898520ffffff48c78518ffffff010000004c8b55a04c8b9d18ffffff4d01da4c899510ffffff4c8b9510ffffff4c8955a0e94dfeffff488b7dc0488b75e0e893ecffff48898508ffffff48c78500ffffff01000000488b7dc0488bb500ffffffe844ecffff488985f8feffff488b7dc0e8e6ecffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec1001000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb0d488b45f04881c4100100005dc348c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb0d488b45f84881c4100100005dc3488b7df8e8a8f0ffff488945c84c8b55c84c8955c0488b7df0e893f0ffff488945b84c8b55b84c8955b0488b7dc0e8c3eaffff488945a84c8b55a84c8955a0488b7db0e8aeeaffff488945984c8b55984c8955904c8b55a04c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb56488b7dc0488b75b0e825f9ffff488945804c8b55804c899578ffffff488bbd78ffffff488b75a0e8faeaffff48898570ffffff488bbd78ffffffe87af0ffff48898568ffffff488b8568ffffff4881c4100100005dc3488b7dc0488b75b0e8def4ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9558ffffff4c8b9d50ffffff4d39da0f94c04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb1b48c78540ffffff00000000488b8540ffffff4881c4100100005dc348c78538ffffff000000004c8b9558ffffff4c8b9d38ffffff4d39da0f9fc04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb5c488b7dc0488b75b0e87afbffff48898528ffffff4c8b9528ffffff4c899520ffffff488bbd20ffffff488b75a0e8efe9ffff48898518ffffff488bbd20ffffffe86fefffff48898510ffffff488b8510ffffff4881c4100100005dc3488b7db0488b75c0e81efbffff48898508ffffff4c8b9508ffffff4c899500ffffff488bbd00ffffff488b7590e893e9ffff488985f8feffff488bbd00ffffffe813efffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec9000000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1548c745e000000000488b45e04881c4900000005dc3488b7df8e8b6e6ffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb32488b7df8e8f8e6ffff488945c04c8b55c049f7da4c8955b8488b7db8e815e7ffff488945b0488b45b04881c4900000005dc3488b7df8e829e7ffff488945a84c8b55a84c8955a0488b7da0e8c1f1ffff488945984c8b55984c895590488b7da0e8d9e7ffff488945884c8b558849f7da4c895580488b7d90488b7580e863e8ffff48898578ffffff488b7d90e8e6edffff48898570ffffff488b8570ffffff4881c4900000005dc3554889e54883ec2048897df8488975f0488b7df0e8bafeffff488945e8488b7df8488b75e8e8f5fbffff488945e0488b45e04883c4205dc3554889e54881ec1802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4180200005dc3488b7df8e891ecffff488945b84c8b55b84c8955b0488b7df0e87cecffff488945a84c8b55a84c8955a0488b7db0e8d5e6ffff488945984c8b55984c895590488b7da0e8c0e6ffff488945884c8b55884c8955804c8b55904c8b5d804d01da4c899578ffffff488bbd78ffffffe820e6ffff48898570ffffff4c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffffeb00eb004c8b9558ffffff4c8b5d904d39da0f9dc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb05e96803000048c78548ffffff000000004c8b9548ffffff4c899540ffffff488b7db0488bb558ffffffe834e6ffff48898538ffffff4c8b9538ffffff4c899530ffffff48c78528ffffff000000004c8b9528ffffff4c899520ffffffeb00eb004c8b9520ffffff4c8b5d804d39da0f9dc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb05e95d0100004c8b9558ffffff4c8b9d20ffffff4d01da4c899510ffffff488bbd68ffffff488bb510ffffffe8a7e5ffff48898508ffffff488b7da0488bb520ffffffe890e5ffff48898500ffffff4c8b9530ffffff4c8b9d00ffffff4d0fafd34c8995f8feffff4c8b9508ffffff4c8b9df8feffff4d01da4c8995f0feffff4c8b95f0feffff4c8b9d40ffffff4d01da4c8995e8feffff4c8b95e8feffff4c8995e0feffff4c8b9558ffffff4c8b9d20ffffff4d01da4c8995d8feffff4c8b95e0feffff4c8b1dda2900004c89d0489949f7fb4989d24c8995d0feffff488bbd68ffffff488bb5d8feffff488b95d0feffffe88ee5ffff488985c8feffff4c8b95e0feffff4c8b1d992900004c89d0489949f7fb4989c24c8995c0feffff4c8b95c0feffff4c899540ffffff48c785b8feffff010000004c8b9520ffffff4c8b9db8feffff4d01da4c8995b0feffff4c8b95b0feffff4c899520ffffffe971feffff4c8b9558ffffff4c8b5d804d01da4c8995a8feffff4c8b95a8feffff4c8995a0feffffeb00eb0048c78598feffff000000004c8b9540ffffff4c8b9d98feffff4d39da0f94c04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9e5000000488bbd68ffffff488bb5a0feffffe8fde3ffff48898588feffff4c8b9588feffff4c8b9d40ffffff4d01da4c899580feffff4c8b9580feffff4c899578feffff4c8b9578feffff4c8b1d902800004c89d0489949f7fb4989d24c899570feffff488bbd68ffffff488bb5a0feffff488b9570feffffe844e4ffff48898568feffff4c8b9578feffff4c8b1d4f2800004c89d0489949f7fb4989c24c899560feffff4c8b9560feffff4c899540ffffff48c78558feffff010000004c8b95a0feffff4c8b9d58feffff4d01da4c899550feffff4c8b9550feffff4c8995a0feffffe9dbfeffff48c78548feffff010000004c8b9558ffffff4c8b9d48feffff4d01da4c899540feffff4c8b9540feffff4c899558ffffffe966fcffff4c8b55904c8b5d804d01da4c899538feffff488bbd68ffffff488bb538feffffe851e3ffff48898530feffff48c78528feffff010000004c8b9528feffff4c899520feffff488b7db0e855e2ffff48898518feffff488b7da0e845e2ffff48898510feffff4c8b9518feffff4c8b9d10feffff4d39da0f95c04c0fb6d04c899508feffff4c8b9508feffff4983fa017402eb2c48c78500feffff010000004c8b9500feffff49f7da4c8995f8fdffff4c8b95f8fdffff4c899520feffffeb00488bbd68ffffff488bb520feffffe877e2ffff488985f0fdffff488bbd68ffffffe8f7e7ffff488985e8fdffff488b85e8fdffff4881c4180200005dc3554889e54883ec7848897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1248c745e001000000488b45e04883c4785dc3488b7df8e8a0dfffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8e833e0ffff488945b84c8b55b84c8955b0488b7db0e821e1ffff488945a848c745a0000000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55984983fa017402eb1248c7459001000000488b45904883c4785dc348c7458800000000488b45884883c4785dc3554889e54883ec5048897df848c745f0010000004c8b55f04c8955e848c745e0000000004c8b55e04c8955d8eb00eb004c8b55d84c8b5df84d39da0f9dc04c0fb6d04c8955d04c8b55d04983fa017402eb02eb4148c745c8020000004c8b55e84c8b5dc84d0fafd34c8955c04c8b55c04c8955e848c745b8010000004c8b55d84c8b5db84d01da4c8955b04c8b55b04c8955d8eb99488b45e84883c4505dc3554889e54881ecb800000048897df8488b7df8e818e0ffff488945f04c8b55f04c8955e848c745e0000000004c8b55e84c8b5de04d39da0f94c04c0fb6d04c8955d84c8b55d84983fa017402eb1548c745d000000000488b45d04881c4b80000005dc348c745c8010000004c8b55e84c8b5dc84d29da4c8955c0488b7df8488b75c0e8d2dfffff488945b84c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb0048c74598000000004c8b55b04c8b5d984d39da0f94c04c0fb6d04c8955904c8b55904983fa017402eb02eb5748c74588020000004c8b55b04c8b5d884c89d0489949f7fb4989c24c8955804c8b55804c8955b048c78578ffffff010000004c8b55a04c8b9d78ffffff4d01da4c899570ffffff4c8b9570ffffff4c8955a0e97bffffff48c78568ffffff010000004c8b55e84c8b9d68ffffff4d29da4c899560ffffff48c78558ffffff1e0000004c8b9560ffffff4c8b9d58ffffff4d0fafd34c899550ffffff4c8b9550ffffff4c8b5da04d01da4c899548ffffff488b8548ffffff4881c4b80000005dc3554889e54883ec7848897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e85ddeffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8488b75d8e841deffff488945b848c745b01e0000004c8b55f04c8b5db04c89d0489949f7fb4989d24c8955a8488b7da8e81dfdffff488945a04c8b55b84c8b5da04c89d0489949f7fb4989c24c89559848c74590020000004c8b55984c8b5d904c89d0489949f7fb4989d24c895588488b45884883c4785dc3554889e54883ec7048897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e868ddffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb064883c4705dc3488b7df8488b75d8e858ddffff488945c048c745b81e0000004c8b55f04c8b5db84c89d0489949f7fb4989d24c8955b0488b7db0e834fcffff488945a84c8b55c04c8b5da84d01da4c8955a04c8b55a04c895598488b7df8488b75d8488b5598e8aeddffff48894590488b45904883c4705dc3554889e54881ec0001000048897df8488b7df8e8b1dcffff488945f04c8b55f04c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b7dd8e80fdcffff488945d04c8b55d04c8955c848c745c0000000004c8b55c04c8955b848c745b0000000004c8b55b04c8955a8eb00eb004c8b55a84c8b5de84d39da0f9dc04c0fb6d04c8955a04c8b55a04983fa017402eb05e9d9000000488b7df8488b75a8e84adcffff4889459848c74590020000004c8b55984c8b5d904d0fafd34c8955884c8b55884c8b5db84d01da4c8955804c8b55804c899578ffffff4c8b9578ffffff4c8b1dd42000004c89d0489949f7fb4989d24c899570ffffff488b7dc8488b75a8488b9570ffffffe88edcffff48898568ffffff4c8b9578ffffff4c8b1d992000004c89d0489949f7fb4989c24c899560ffffff4c8b9560ffffff4c8955b848c78558ffffff010000004c8b55a84c8b9d58ffffff4d01da4c899550ffffff4c8b9550ffffff4c8955a8e9fefeffff488b7dc8488b75e8e8f2dbffff48898548ffffff48c78540ffffff000000004c8b55b84c8b9d40ffffff4d39da0f95c04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb51488b7dc8488b75e8488b55b8e8d1dbffff48898530ffffff48c78528ffffff010000004c8b55e84c8b9d28ffffff4d01da4c899520ffffff488b7dc8488bb520ffffffe86ddbffff48898518ffffffeb0048c78510ffffff01000000488b7dc8488bb510ffffffe81cdbffff48898508ffffff488b7dc8e8bedbffff48898500ffffff488b8500ffffff4881c4000100005dc3554889e54883ec2048897df8488975f0488b7df0e82ed9ffff488945e8488b7df8488b75e8e8aeeeffff488945e0488b45e04883c4205dc3554889e54881ec0802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4080200005dc3488b7df8e8d9d7ffff488945b848c745b0000000004c8b55b84c8b5db04d39da0f94c04c0fb6d04c8955a84c8b55a84983fa017405e921020000488b7df8e818d8ffff488945a04c8b55a04c89559848c74590000000004c8b55f04c8b5d904d39da0f9fc04c0fb6d04c8955884c8b155c1e00004c8b5df04c89d0489949f7fb4989c24c8955804c8b55984c8b5d804d39da0f9dc04c0fb6d04c899578ffffff4c8b55884c8b9d78ffffff4d21da4c899570ffffff4c8b150c1e00004c8b5df04c89d0489949f7fb4989c24c899568ffffff4c8b55984c8b9d68ffffff4d39da0f9ec04c0fb6d04c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d21da4c899558ffffff4c8b9558ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899550ffffff488bbd50ffffffe85cd7ffff48898548ffffff488b8548ffffff4881c4080200005dc348c78540ffffff000000004c8b55f04c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b15571d00004c8b5df04c89d0489949f7fb4989c24c899530ffffff4c8b55984c8b9d30ffffff4d39da0f9ec04c0fb6d04c899528ffffff4c8b9538ffffff4c8b9d28ffffff4d21da4c899520ffffff4c8b15fe1c00004c8b5df04c89d0489949f7fb4989c24c899518ffffff4c8b55984c8b9d18ffffff4d39da0f9dc04c0fb6d04c899510ffffff4c8b9520ffffff4c8b9d10ffffff4d21da4c899508ffffff4c8b9508ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899500ffffff488bbd00ffffffe84ed6ffff488985f8feffff488b85f8feffff4881c4080200005dc3eb00488b7df8e8efdcffff488985f0feffff4c8b95f0feffff4c8995e8feffff488bbde8feffffe83cd7ffff488985e0feffff4c8b95e0feffff4c8995d8feffff48c785d0feffff010000004c8b95d8feffff4c8b9dd0feffff4d01da4c8995c8feffff488bbdc8feffffe882d6ffff488985c0feffff4c8b95c0feffff4c8995b8feffff48c785b0feffff000000004c8b95b0feffff4c8995a8feffff48c785a0feffff000000004c8b95a0feffff4c899598feffffeb00eb004c8b9598feffff4c8b9dd8feffff4d39da0f9dc04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9fb000000488bbde8feffff488bb598feffffe890d6ffff48898588feffff4c8b9588feffff4c8b5df04d0fafd34c899580feffff4c8b9580feffff4c8b9da8feffff4d01da4c899578feffff4c8b9578feffff4c899570feffff4c8b9570feffff4c8b1d0d1b00004c89d0489949f7fb4989d24c899568feffff488bbdb8feffff488bb598feffff488b9568feffffe8c1d6ffff48898560feffff4c8b9570feffff4c8b1dcc1a00004c89d0489949f7fb4989c24c899558feffff4c8b9558feffff4c8995a8feffff48c78550feffff010000004c8b9598feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899598feffffe9d0feffff488bbdb8feffff488bb5d8feffffe816d6ffff48898540feffff48c78538feffff000000004c8b95a8feffff4c8b9d38feffff4d39da0f95c04c0fb6d04c899530feffff4c8b9530feffff4983fa017402eb60488bbdb8feffff488bb5d8feffff488b95a8feffffe8e9d5ffff48898528feffff48c78520feffff010000004c8b95d8feffff4c8b9d20feffff4d01da4c899518feffff488bbdb8feffff488bb518feffffe87fd5ffff48898510feffffeb00488bbde8feffffe897d4ffff48898508feffff488bbdb8feffff488bb508feffffe823d5ffff48898500feffff488bbdb8feffffe8a3daffff488985f8fdffff488b85f8fdffff4881c4080200005dc3554889e54881ec3801000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb3b48c745d8010000004c8b55d84c89154319000048c745d0000000004c8b55d04c89153819000048c745c800000000488b45c84881c4380100005dc3488b7df8e81cd2ffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb68488b7df8e85ed2ffff488945a84c8b55a84c8955a04c8b55a04c8b5df04c89d0489949f7fb4989d24c8955984c8b55984c8915b51800004c8b55a04c8b5df04c89d0489949f7fb4989c24c895590488b7d90e845d2ffff48894588488b45884881c4380100005dc3488b7df8e8eed8ffff488945804c8b55804c899578ffffff488bbd78ffffffe841d3ffff48898570ffffff4c8b9570ffffff4c899568ffffff488bbd68ffffffe8aad2ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9550ffffff4c899548ffffff4c8b9568ffffff4c899540ffffffeb00eb0048c78538ffffff000000004c8b9540ffffff4c8b9d38ffffff4d39da0f9ec04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb05e9f800000048c78528ffffff010000004c8b9540ffffff4c8b9d28ffffff4d29da4c899520ffffff4c8b9520ffffff4c899540ffffff4c8b9548ffffff4c8b1d5a1700004d0fafd34c899518ffffff488bbd78ffffff488bb540ffffffe86ed2ffff48898510ffffff4c8b9518ffffff4c8b9d10ffffff4d01da4c899508ffffff4c8b9508ffffff4c899500ffffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989c24c8995f8feffff488bbd58ffffff488bb540ffffff488b95f8feffffe8b8d2ffff488985f0feffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989d24c8995e8feffff4c8b95e8feffff4c899548ffffffe9c8feffff488bbd58ffffff488bb568ffffffe841d2ffff488985e0feffff488bbd78ffffffe85bd1ffff488985d8feffff488bbd58ffffff488bb5d8feffffe8e7d1ffff488985d0feffff4c8b9548ffffff4c891574160000488bbd58ffffffe859d7ffff488985c8feffff488b85c8feffff4881c4380100005dc3554889e54881ec2001000048897df8488b7df8488b47f84883e801488945f04c8b55f04c8955e848c745e0000000004c8b55e04c8955d848c745d0000000004c8b55d04c8955c848c745c0000000004c8b55e84c8b5dc04d39da0f9fc04c0fb6d04c8955b848c745b000000000488b7df8488b75b0480fb60437488945a848c745a02d0000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55b84c8b5d984d21da4c8955904c8b55904983fa017402eb2248c74588010000004c8b55884c8955c848c74580010000004c8b55804c8955d8eb0048c78578ffffff000000004c8b9578ffffff4c899570ffffffeb00eb004c8b55d84c8b5de84d39da0f9dc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb05e950010000488b7df8488b75d8480fb6043748898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff300000004c8b9558ffffff4c8b9d50ffffff4d39da0f9cc04c0fb6d04c899548ffffff48c78540ffffff390000004c8b9558ffffff4c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9548ffffff4c8b9d38ffffff4d09da4c899530ffffff4c8b9530ffffff4983fa017402eb05e9ae00000048c78528ffffff0a000000488bbd70ffffff488bb528ffffffe80ff5ffff48898520ffffff4c8b9520ffffff4c899570ffffff48c78518ffffff300000004c8b9558ffffff4c8b9d18ffffff4d29da4c899510ffffff488bbd70ffffff488bb510ffffffe88cf4ffff48898508ffffff4c8b9508ffffff4c899570ffffff48c78500ffffff010000004c8b55d84c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c8955d8e981feffff48c785f0feffff000000004c8b55c84c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb23488bbd70ffffffe891e5ffff488985e0feffff4c8b95e0feffff4c899570ffffffeb00488b8570ffffff4881c4200100005dc3554889e54881ec1802000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb5148c745e002000000488b7de0e800000000488945d84c8b55d84c8955d048c745c80000000048c745c030000000488b7dd0488b75c8488b55c088143748c745b800000000488b45d04881c4180200005dc348c745b0000000004c8b55b04c8955a8488b7df8e845d3ffff488945a04c8b55a04c895598488b7d98e875cdffff4889459048c74588000000004c8b55904c8b5d884d39da0f9cc04c0fb6d04c8955804c8b55804983fa017402eb4648c78578ffffff010000004c8b9578ffffff4c8955a8488b7df8e865e4ffff48898570ffffff488bbd70ffffffe8d0d2ffff48898568ffffff4c8b9568ffffff4c895598eb00488b7d98e821cdffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff0a0000004c8b9558ffffff4c8b9d50ffffff4d0fafd34c899548ffffff48c78540ffffff040000004c8b9548ffffff4c8b9d40ffffff4d01da4c899538ffffff4c8b9538ffffff4c899530ffffff488bbd30ffffffe80000000048898528ffffff4c8b9528ffffff4c899520ffffff48c78518ffffff010000004c8b9530ffffff4c8b9d18ffffff4d29da4c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff00000000488bbd20ffffff488bb508ffffff488b9500ffffff88143748c785f8feffff00000000488b7d98e83ed2ffff488985f0feffff4c8b95f0feffff4c8995e8feffffeb00eb0048c785e0feffff000000004c8b95e8feffff4c8b9de0feffff4d39da0f94c04c0fb6d04c8995d8feffff4c8b95d8feffff4983fa017402eb05e9bd00000048c785d0feffff0a000000488bbde8feffff488bb5d0feffffe83df7ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b9508ffffff4c8b9db8feffff4d29da4c8995b0feffff4c8b95b0feffff4c899508ffffff48c785a8feffff300000004c8b15801000004c8b9da8feffff4d01da4c8995a0feffff488bbd20ffffff488bb508ffffff488b95a0feffff88143748c78598feffff000000004c8b95c0feffff4c8995e8feffffe903ffffff48c78590feffff000000004c8b55a84c8b9d90feffff4d39da0f95c04c0fb6d04c899588feffff4c8b9588feffff4983fa017402eb6148c78580feffff010000004c8b9508ffffff4c8b9d80feffff4d29da4c899578feffff4c8b9578feffff4c899508ffffff48c78570feffff2d000000488bbd20ffffff488bb508ffffff488b9570feffff88143748c78568feffff00000000eb0048c78560feffff010000004c8b9530ffffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c8b9d08ffffff4d29da4c899550feffff4c8b9550feffff4c899548feffff48c78540feffff010000004c8b9548feffff4c8b9d40feffff4d01da4c899538feffff488bbd38feffffe80000000048898530feffff4c8b9530feffff4c899528feffff48c78520feffff000000004c8b9520feffff4c899518feffffeb00eb004c8b9518feffff4c8b9d48feffff4d39da0f9dc04c0fb6d04c899510feffff4c8b9510feffff4983fa017402eb05e98b0000004c8b9508ffffff4c8b9d18feffff4d01da4c899508feffff488bbd20ffffff488bb508feffff480fb6043748898500feffff488bbd28feffff488bb518feffff488b9500feffff88143748c785f8fdffff0000000048c785f0fdffff010000004c8b9518feffff4c8b9df0fdffff4d01da4c8995e8fdffff4c8b95e8fdffff4c899518feffffe940ffffff488b8528feffff4881c4180200005dc3554889e54881ec5802000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb404c8d15260d00004c8955d8488b7dd8e841c6ffff488945d048c745c8010000004c8b55c84c8915ba0d000048c745c000000000488b45c04881c4580200005dc348c745b8000000004c8b55f84c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb2848c745a8000000004c8b55a84c8915700d000048c745a000000000488b45a04881c4580200005dc3488b7df8e8c5cdffff488945984c8b55984c895590488b7df0e8b0cdffff488945884c8b55884c895580488b7d90488b7580e89dd2ffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b9570ffffff4c8b9d68ffffff4d39da0f9cc04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb264c8b55f84c8915cb0c000048c78558ffffff00000000488b8558ffffff4881c4580200005dc3488b7d90e85fc7ffff48898550ffffff4c8b9550ffffff4c899548ffffff488b7d80e841c7ffff48898540ffffff4c8b9540ffffff4c899538ffffff48c78530ffffff000000004c8b9570ffffff4c8b9d30ffffff4d39da0f94c04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017405e9af00000048c78520ffffff010000004c8b9520ffffff4c899518ffffff4c8b9548ffffff4c8b9d38ffffff4d39da0f95c04c0fb6d04c899510ffffff4c8b9510ffffff4983fa017402eb2c48c78508ffffff010000004c8b9508ffffff49f7da4c899500ffffff4c8b9500ffffff4c899518ffffffeb0048c785f8feffff000000004c8b95f8feffff4c8915ac0b0000488bbd18ffffffe850c5ffff488985f0feffff488b85f0feffff4881c4580200005dc3488b7d90e861c6ffff488985e8feffff4c8b95e8feffff4c8995e0feffff48c785d8feffff010000004c8b95e0feffff4c8b9dd8feffff4d01da4c8995d0feffff488bbdd0feffffe8a7c5ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b95e0feffff4c8b9db8feffff4d01da4c8995b0feffff488bbdc0feffff488bb5b0feffffe87cc6ffff488985a8feffff48c785a0feffff00000000488bbda0feffffe815c8ffff48898598feffff4c8b9598feffff4c899590feffff488b7d90e866e5ffff48898588feffff4c8b9588feffff4c899580feffff4c8b9580feffff4c899578feffffeb00eb0048c78570feffff000000004c8b9578feffff4c8b9d70feffff4d39da0f9ec04c0fb6d04c899568feffff4c8b9568feffff4983fa017402eb05e99301000048c78560feffff010000004c8b9578feffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c899578feffff488bbd90feffffe82be8ffff48898550feffff4c8b9550feffff4c899590feffff488b7d90488bb578feffffe832e6ffff48898548feffff48c78540feffff000000004c8b9548feffff4c8b9d40feffff4d39da0f95c04c0fb6d04c899538feffff4c8b9538feffff4983fa017402eb5b488bbd90feffffe893caffff48898530feffff48c78528feffff01000000488bbd30feffff488bb528feffffe8e6e9ffff48898520feffff488bbd20feffffe8ddc9ffff48898518feffff4c8b9518feffff4c899590feffffeb00488bbd90feffff488b7580e8bcceffff48898510feffff48c78508feffff000000004c8b9510feffff4c8b9d08feffff4d39da0f9dc04c0fb6d04c899500feffff4c8b9500feffff4983fa017402eb41488bbd90feffff488b7580e8b7d5ffff488985f8fdffff4c8b95f8fdffff4c899590feffff488bbdc0feffff488bb578feffffe804e6ffff488985f0fdffffeb00e92dfeffff48c785e8fdffff010000004c8b95e0feffff4c8b9de8fdffff4d01da4c8995e0fdffff488bbdc0feffff488bb5e0fdffffe812c4ffff488985d8fdffff4c8b9548ffffff4c8b9d38ffffff4d0fafd34c8995d0fdffff488bbdc0feffff488bb5d0fdffffe8b2c3ffff488985c8fdffff488bbdc0feffffe832c9ffff488985c0fdffff4c8b95c0fdffff4c8995b8fdffff488bbd90feffff488bb548ffffffe877c3ffff488985b0fdffff488bbd90feffffe8f7c8ffff488985a8fdffff4c8b95a8fdffff4c8915f1070000488b85b8fdffff4881c4580200005dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e89af9ffff488945e84c8b55e84c8955e0488b45e04883c4205dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e867f9ffff488945e84c8b55e84c8955e0488b057e0700004883c4205dc3554889e54883ec3848897df84c8b55f84c8b1d510700004d39da0f9dc04c0fb6d04c8955f04c8b55f84c8b1d300700004d39da0f9ec04c0fb6d04c8955e84c8b55f04c8b5de84d21da4c8955e04c8b55e04983fa017402eb17488b7df8e8c6c0ffff488945d8488b45d84883c4385dc3488b7df8e841c4ffff488945d0488b7dd0e8e3c7ffff488945c8488b45c84883c4385dc3554889e54883ec4048897df848c745f0000000004c8b55f84c8b5df04d39da0f9dc04c0fb6d04c8955e84c8b55f84c8b1d970600004d39da0f9ec04c0fb6d04c8955e04c8b55e84c8b5de04d21da4c8955d84c8b55d84983fa017402eb17488b7df8e82dc0ffff488945d0488b45d04883c4405dc3488b7df8e8a8c3ffff488945c8488b7dc8e84ac7ffff488945c0488b45c04883c4405dc3554889e54881ecc801000048897df848c745f0000000004c8b55f04c891521060000488b7df8e893c6ffff488945e84c8b55e84c8955e0488b7de0e8ecc0ffff488945d84c8b55d84c8955d048c745c8030000004c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb404c8d152e0500004c8955b8488b7db8e829beffff488945b048c745a8010000004c8b55a84c8915a205000048c745a000000000488b45a04881c4c80100005dc348c7459800000000488b7de0488b7598e88ac0ffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff010000004c8b55d04c8b9d60ffffff4d39da0f9fc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb3248c78550ffffff01000000488b7de0488bb550ffffffe801c0ffff48898548ffffff4c8b9548ffffff4c899578ffffffeb0048c78540ffffff020000004c8b55d04c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb3248c78530ffffff02000000488b7de0488bb530ffffffe899bfffff48898528ffffff4c8b9528ffffff4c899568ffffffeb00488b7de0e827bfffff48898520ffffff48c78518ffffff000000004c8b9520ffffff4c8b9d18ffffff4d39da0f9cc04c0fb6d04c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff080000004c8b9568ffffff4c8b9d00ffffff4d39da0f9fc04c0fb6d04c8995f8feffff4c8b95f8feffff4983fa017402eb554c8d15500300004c8995f0feffff488bbdf0feffffe845bcffff488985e8feffff48c785e0feffff010000004c8b95e0feffff4c8915b503000048c785d8feffff00000000488b85d8feffff4881c4c80100005dc348c785d0feffff080000004c8b9568ffffff4c8b9dd0feffff4d39da0f94c04c0fb6d04c8995c8feffff4c8b9508ffffff4983fa000f94c04c0fb6d04c8995c0feffff48c785b8feffff000000004c8b9578ffffff4c8b9db8feffff4d39da0f95c04c0fb6d04c8995b0feffff4c8b95c0feffff4c8b9db0feffff4d09da4c8995a8feffff48c785a0feffff000000004c8b55884c8b9da0feffff4d39da0f95c04c0fb6d04c899598feffff4c8b95a8feffff4c8b9d98feffff4d09da4c899590feffff4c8b95c8feffff4c8b9d90feffff4d21da4c899588feffff4c8b9588feffff4983fa017402eb554c8d15100200004c899580feffff488bbd80feffffe805bbffff48898578feffff48c78570feffff010000004c8b9570feffff4c89157502000048c78568feffff00000000488b8568feffff4881c4c80100005dc34c8b9578ffffff4c8b1d2c0200004d0fafd34c899560feffff4c8b55884c8b9d60feffff4d01da4c899558feffff4c8b9568ffffff4c8b1d060200004d0fafd34c899550feffff4c8b9558feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899540feffff4c8b9508ffffff4983fa017402eb214c8b9540feffff49f7da4c899538feffff4c8b9538feffff4c899540feffffeb00488b8540feffff4881c4c80100005dc3554889e54883ec6048897df8488b7df8e855fbffff488945f04c8b55f04c8955e848c745e0000000004c8b157d0100004c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d000000000488b45d04883c4605dc348c745c8000000004c8b55e84c8b5dc84d39da0f9cc04c0fb6d04c8955c04c8b55c04983fa017402eb3d4c8d15b40000004c8955b8488b7db8e87fb9ffff488945b048c745a8010000004c8b55a84c8915f800000048c745a000000000488b45a04883c4605dc3488b45e84883c4605dc30000000000000000000000000100000000000000000000000000000012000000000000006469766973696f6e206279207a65726f0a000000000000002300000000000000696e7465676572206f7574206f66206d616368696e652d776f72642072616e67650a0000000000002c00000000000000696e7465676572206f7574206f6620756e7369676e6564206d616368696e652d776f72642072616e67650a000000000000000000000000000800000000000000100000000000000000000040000000000000000000000010ffffffffffffff3f000000000000000000000000000000000000000000000000" fn bigint_blob_byte_from_hex(c: int) -> int: if c >= 48 && c <= 57: @@ -119,7 +119,7 @@ fn bigint_blob_symbol(name: string) -> int: if str_eq(name, "bi_to_dec") != 0: return 13347 if str_eq(name, "bi_to_uword_checked") != 0: - return 17557 + return 17891 if str_eq(name, "bi_to_word_checked") != 0: return 16717 return -1 diff --git a/tests/selfhost/test_bigint_boundaries.py b/tests/selfhost/test_bigint_boundaries.py new file mode 100644 index 00000000..48498ab7 --- /dev/null +++ b/tests/selfhost/test_bigint_boundaries.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +"""Self-hosted BigInt pointer/word/FFI boundary gate (--big-int).""" + +import subprocess +import tempfile +from pathlib import Path +import shutil + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" + +VALID = r""" +fn main() -> int: + buf := alloc(32) + w64(buf, 0, 42) + if r64(buf, 0) != 42: + return 1 + + x := 4611686018427387904 + w64(buf, 8, x) + if r64(buf, 8) != x: + return 2 + + store8(buf, 16, 65) + if load8(buf, 16) != 65: + return 3 + + pid := syscall3(39, 0, 0, 0) + if pid <= 0: + return 4 + + s := "hi" + p : ., mut = 0 + q : ., mut = 0 + unsafe: + p = @ptr_of(s) + q = @ptr_of(@str_of(p)) + if q != p: + return 5 + if p <= 0: + return 6 + + addr := @addr(main) + if addr <= 0: + return 7 + + arr := [x, x + 1, x + 2] + base := &arr[1] + next := base + 1 + if *next != x + 2: + return 8 + diff := next - base + if diff != 1: + return 9 + if *(base - 1) != x: + return 10 + + return 0 +""" + +W64_BIG = r""" +fn main() -> int: + buf := alloc(8) + x := 1267650600228229401496703205376 + w64(buf, 0, x) + return 0 +""" + +STR_OF_BIG = r""" +fn main() -> int: + x := 1267650600228229401496703205376 + p : ., mut = "" + unsafe: + p = @str_of(x) + return 0 +""" + +EXTERN = r""" +extern fn big_inc(x: int) -> int +fn main() -> int: + x := 4611686018427387904 + if big_inc(x) != x + 1: + return 1 + return 0 +""" + +EXTERN_BIG = r""" +extern fn big_inc(x: int) -> int +fn main() -> int: + x := 1267650600228229401496703205376 + return big_inc(x) +""" + + +def run(args): + return subprocess.run(args, cwd=str(BASE), capture_output=True, text=True) + + +def build(src: Path, ccr: Path, exe: Path) -> str: + r = run([str(COREC), "ccr", str(src), "-o", str(ccr)]) + if r.returncode != 0: + return r.stdout + r.stderr + r = run([str(COREARCH), str(ccr), "--big-int", "-o", str(exe)]) + if r.returncode != 0: + return r.stdout + r.stderr + return "" + + +def expect_error(src: Path, ccr: Path, exe: Path, needle: str) -> str: + err = build(src, ccr, exe) + if err: + return "compile: " + err + r = run([str(exe)]) + if r.returncode == 0: + return "expected runtime failure" + if needle not in (r.stdout + r.stderr): + return f"missing {needle!r}: {r.stdout + r.stderr!r}" + return "" + + +def build_link(src: Path, ccr: Path, exe: Path, shim: Path) -> str: + r = run([str(COREC), "ccr", str(src), "-o", str(ccr)]) + if r.returncode != 0: + return r.stdout + r.stderr + r = run([str(COREARCH), str(ccr), "--big-int", "--elf", + "--link", str(shim), "--static", "-o", str(exe)]) + if r.returncode != 0: + return r.stdout + r.stderr + exe.chmod(0o755) + return "" + + +def main() -> int: + if not COREC.exists() or not COREARCH.exists(): + print("missing build/corec or build/corearch") + return 1 + with tempfile.TemporaryDirectory(prefix="core-bigint-boundaries-") as td: + d = Path(td) + src = d / "valid.cr" + ccr = d / "valid.ccr" + exe = d / "valid" + src.write_text(VALID, encoding="utf-8") + err = build(src, ccr, exe) + if err: + print(err) + return 1 + r = run([str(exe)]) + if r.returncode != 0: + print(f"boundaries exit {r.returncode}") + return 1 + + for name, text, needle in ( + ("w64_big", W64_BIG, "machine-word range"), + ("str_of_big", STR_OF_BIG, "machine-word range"), + ): + s = d / f"{name}.cr" + c = d / f"{name}.ccr" + e = d / name + s.write_text(text, encoding="utf-8") + err = expect_error(s, c, e, needle) + if err: + print(f"{name}: {err}") + return 1 + + if shutil.which("gcc"): + shim_c = d / "int_shim.c" + shim = d / "int_shim.so" + shim_c.write_text("long big_inc(long x) { return x + 1; }\n", + encoding="utf-8") + r = run(["gcc", "-shared", "-fPIC", "-O2", str(shim_c), + "-o", str(shim)]) + if r.returncode != 0: + print("shim build failed:\n" + r.stdout + r.stderr) + return 1 + esrc = d / "extern.cr" + eccr = d / "extern.ccr" + eexe = d / "extern" + esrc.write_text(EXTERN, encoding="utf-8") + err = build_link(esrc, eccr, eexe, shim) + if err: + print("extern link failed:\n" + err) + return 1 + er = run([str(eexe)]) + if er.returncode != 0: + print(f"extern valid exit {er.returncode}") + return 1 + + ebsrc = d / "extern_big.cr" + ebccr = d / "extern_big.ccr" + ebexe = d / "extern_big" + ebsrc.write_text(EXTERN_BIG, encoding="utf-8") + err = build_link(ebsrc, ebccr, ebexe, shim) + if err: + print("extern big link failed:\n" + err) + return 1 + ebr = run([str(ebexe)]) + if ebr.returncode == 0 or "machine-word range" not in (ebr.stdout + ebr.stderr): + print("extern big argument was not rejected") + return 1 + + print("PASS bigint boundaries") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 039a6b58..02297b17 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -1863,7 +1863,7 @@ def test_p4t4_cir_snapshot_layout_unbumped(): total_nodes = 0 for f in entries: ver, nodes, ok = cir_entry_nodes(os.path.join(cache_dir, f)) - assert ver == 22, f"{f}: CIR_CACHE_VER {ver} != 22(2026-09-18 **插值展开批** v22:含插值源语义变化(原文串 → 展开后拼接值)⇒ 旧条目会复活旧语义;**依据 = 身份闸(cir_cache.cr:549-551)而非指纹**——指纹不覆盖函数体(见 TODO #2026-09-18-16);前代 21 = 2026-09-18 串域批、20 = 2026-09-17 缓存膨胀批、19 = 批 5(opt-dex)、18 = TODO #2026-09-16-16 批 2)" + assert ver == 23, f"{f}: CIR_CACHE_VER {ver} != 23(2026-10-23 **BigInt IR 批** v23:新增 IR_CONST_BIG/IR_PTR_OF/IR_STR_OF;旧条目会复活旧整数语义 ⇒ 旧缓存必须失效;前代 22 = 2026-09-18 插值展开批、21 = 串域批、20 = 缓存膨胀批、19 = 批 5(opt-dex)、18 = TODO #2026-09-16-16 批 2)" assert ok, f"{f}: node section (64B stride) runs past EOF" total_nodes += len(nodes) assert total_nodes > 0, "vacuous: no nodes parsed from snapshots" @@ -2020,7 +2020,7 @@ def test_p5t2_snapshot_disk_code_preserved(): disk = [] for f in sorted(os.listdir(cache_dir)): ver, nodes, ok = cir_entry_nodes(os.path.join(cache_dir, f)) - assert ver == 22 and ok, f"{f}: version/stride drifted(期望 22 = 2026-09-18 **插值展开批**(前代 21 = 串域批;节点 stride 仍 64B / 头偏移不变(裁-4 + 尾部 trailer);前代 20 = 缓存膨胀批、19 = 批 5(opt-dex)、18 = TODO #2026-09-16-16 批 2)" + assert ver == 23 and ok, f"{f}: version/stride drifted(期望 23 = 2026-10-23 **BigInt IR 批**(IR_CONST_BIG/IR_PTR_OF/IR_STR_OF;前代 22 = 插值展开批;节点 stride 仍 64B / 头偏移不变(裁-4 + 尾部 trailer);前代 21 = 串域批、20 = 缓存膨胀批、19 = 批 5(opt-dex)、18 = TODO #2026-09-16-16 批 2)" disk += [(nd[0], nd[5]) for nd in nodes] assert disk, "vacuous: no nodes parsed from snapshots" mint = set((r[1], r[2]) for r in rows if r[1] in MINT_OPS and r[2] > 0) From e506f8fab607500ffc36560f9f2f101c8e37b3d4 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 12:19:20 +0800 Subject: [PATCH 24/58] interpreter: execute tagged BigInt values --- build_selfhost_native.py | 1 + src/compiler/interp.cr | 233 +++++++++++++++++---------- tests/selfhost/test_bigint_interp.py | 159 ++++++++++++++++++ 3 files changed, 307 insertions(+), 86 deletions(-) create mode 100644 tests/selfhost/test_bigint_interp.py diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 42f0e510..27237cb6 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -322,6 +322,7 @@ def main(): 'src/runtime/rt.cr', 'src/stdlib/io.cr', 'src/stdlib/fmt.cr', + 'src/runtime/bigint.cr', 'src/stdlib/cli.cr', 'src/compiler/ast.cr', 'src/compiler/ccr_schema.cr', diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index cfe5d344..44a44520 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -18,6 +18,11 @@ g_ir_vals : string, mut g_ir_vals_cap : int, mut +fn ir_interp_const(ti: int, raw: int) -> int: + if ti == TI_INT: + return bi_from_word(raw) + return raw + // Call dispatch and inline callee execution. This is the only implementation for // both the main loop and callee paths. // Interpreter abort code. 0 means continue; a non-zero value is what ir_interpret @@ -36,72 +41,96 @@ IR_INTERP_MAX_DEPTH : int = 400 g_interp_stack : string, mut // Shared IR_BINARY dispatch for the main loop and callee loops. -// The integer path is modulo 2^64. dex values are scaled integers and use the -// same path. IR_I2F and IR_F2I are rejected at dispatch entry. +// g_ir_vals int slots hold tagged mathematical integers. Char/bool/dex and +// pointer values remain raw machine words, so the operand variable types select +// the representation. Pointer arithmetic converts its int offset through the +// same checked boundary as the ELF backend. fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): if d < 0: return lv := r64(g_ir_vals, s1 * 8) rv := r64(g_ir_vals, s2 * 8) - if s3 == 1: - w64(g_ir_vals, d * 8, lv + rv) - if s3 == 2: - w64(g_ir_vals, d * 8, lv - rv) - if s3 == 3: - w64(g_ir_vals, d * 8, lv * rv) - if s3 == 4: - w64(g_ir_vals, d * 8, lv / rv) - if s3 == 5: - w64(g_ir_vals, d * 8, lv % rv) - if s3 == 6: - if lv == rv: - w64(g_ir_vals, d * 8, 1) - else: - w64(g_ir_vals, d * 8, 0) - if s3 == 7: - if lv != rv: - w64(g_ir_vals, d * 8, 1) - else: - w64(g_ir_vals, d * 8, 0) - if s3 == 8: - if lv < rv: - w64(g_ir_vals, d * 8, 1) - else: - w64(g_ir_vals, d * 8, 0) - if s3 == 9: - if lv > rv: - w64(g_ir_vals, d * 8, 1) + if s3 == 17 || s3 == 18 || s3 == 19: + // Pointer add/sub/diff: the pointer is raw; the offset is mathematical. + off := bi_to_word_checked(rv) + if s3 == 17: + w64(g_ir_vals, d * 8, lv + off * 8) + else if s3 == 18: + w64(g_ir_vals, d * 8, lv - off * 8) else: - w64(g_ir_vals, d * 8, 0) - if s3 == 10: - if lv <= rv: - w64(g_ir_vals, d * 8, 1) - else: - w64(g_ir_vals, d * 8, 0) - if s3 == 11: - if lv >= rv: - w64(g_ir_vals, d * 8, 1) - else: - w64(g_ir_vals, d * 8, 0) - if s3 == 12: - if lv != 0 && rv != 0: - w64(g_ir_vals, d * 8, 1) + w64(g_ir_vals, d * 8, bi_from_word((lv - rv) / 8)) + return + if s3 == 12 || s3 == 13: + // Boolean and/or store raw 0/1. + if s3 == 12: + w64(g_ir_vals, d * 8, lv != 0 && rv != 0) else: - w64(g_ir_vals, d * 8, 0) - if s3 == 13: - if lv != 0 || rv != 0: - w64(g_ir_vals, d * 8, 1) + w64(g_ir_vals, d * 8, lv != 0 || rv != 0) + return + + lhs_char := irv_type(s1) == TI_CHAR + rhs_char := irv_type(s2) == TI_CHAR + if lhs_char || rhs_char: + // Char comparisons are raw. An int operand is still tagged. + l := lv + r := rv + if !lhs_char && s1 >= 0: + l = bi_to_word_checked(lv) + if !rhs_char && s2 >= 0: + r = bi_to_word_checked(rv) + if s3 == 6: + w64(g_ir_vals, d * 8, l == r) + else if s3 == 7: + w64(g_ir_vals, d * 8, l != r) + else if s3 == 8: + w64(g_ir_vals, d * 8, l < r) + else if s3 == 9: + w64(g_ir_vals, d * 8, l > r) + else if s3 == 10: + w64(g_ir_vals, d * 8, l <= r) + else if s3 == 11: + w64(g_ir_vals, d * 8, l >= r) + return + + if ti == TI_DEX || ti == TI_DEX_S: + // Dex slots are scaled machine integers (or binary64 bits, rejected at + // dispatch); keep their existing raw arithmetic. + if s3 == 1: + w64(g_ir_vals, d * 8, lv + rv) + else if s3 == 2: + w64(g_ir_vals, d * 8, lv - rv) + else if s3 == 3: + w64(g_ir_vals, d * 8, lv * rv) + else if s3 == 4: + w64(g_ir_vals, d * 8, lv / rv) + else if s3 == 5: + w64(g_ir_vals, d * 8, lv % rv) + return + + if s3 == 1: + w64(g_ir_vals, d * 8, bi_add(lv, rv)) + else if s3 == 2: + w64(g_ir_vals, d * 8, bi_sub(lv, rv)) + else if s3 == 3: + w64(g_ir_vals, d * 8, bi_mul(lv, rv)) + else if s3 == 4: + w64(g_ir_vals, d * 8, bi_div(lv, rv)) + else if s3 == 5: + w64(g_ir_vals, d * 8, bi_mod(lv, rv)) + else if s3 >= 6 && s3 <= 11: + c := bi_cmp(lv, rv) + if s3 == 6: + w64(g_ir_vals, d * 8, c == 0) + else if s3 == 7: + w64(g_ir_vals, d * 8, c != 0) + else if s3 == 8: + w64(g_ir_vals, d * 8, c < 0) + else if s3 == 9: + w64(g_ir_vals, d * 8, c > 0) + else if s3 == 10: + w64(g_ir_vals, d * 8, c <= 0) else: - w64(g_ir_vals, d * 8, 0) - if s3 == 17: - w64(g_ir_vals, d * 8, lv + rv * 8) - // OP_PTR_ADD - if s3 == 18: - w64(g_ir_vals, d * 8, lv - rv * 8) - // OP_PTR_SUB - if s3 == 19: - w64(g_ir_vals, d * 8, (lv - rv) / 8) - // OP_PTR_DIFF + w64(g_ir_vals, d * 8, c >= 0) // Pointer read and write. // A pointer in [0, g_ir_var_count) is a stack slot address produced by REF; read @@ -153,7 +182,7 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): w64(g_ir_vals, d * 8, 0) if str_eq(fn_name, "str_len") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, istr_len(r64(g_ir_vals, s1 * 8))) + w64(g_ir_vals, d * 8, bi_from_word(istr_len(r64(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "str_eq") != 0: if d >= 0 && s2 >= 2: left_s := istr_get(r64(g_ir_vals, s1 * 8)) @@ -165,30 +194,35 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))) if str_eq(fn_name, "int_str") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))) + w64(g_ir_vals, d * 8, str_intern(bi_to_dec(r64(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "chr") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, str_intern(chr(r64(g_ir_vals, s1 * 8)))) + w64(g_ir_vals, d * 8, str_intern(chr(bi_to_word_checked(r64(g_ir_vals, s1 * 8))))) if str_eq(fn_name, "get_char") != 0: if d >= 0 && s2 >= 2: - w64(g_ir_vals, d * 8, ir_interp_str_char(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))) + w64(g_ir_vals, d * 8, ir_interp_str_char( + r64(g_ir_vals, s1 * 8), + bi_to_word_checked(r64(g_ir_vals, (s1 + 1) * 8)))) if str_eq(fn_name, "str_sub") != 0: if d >= 0 && s2 >= 3: - w64(g_ir_vals, d * 8, ir_interp_str_sub(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8), r64(g_ir_vals, (s1 + 2) * 8))) + w64(g_ir_vals, d * 8, ir_interp_str_sub( + r64(g_ir_vals, s1 * 8), + bi_to_word_checked(r64(g_ir_vals, (s1 + 1) * 8)), + bi_to_word_checked(r64(g_ir_vals, (s1 + 2) * 8)))) if str_eq(fn_name, "load_str_ptr") != 0: if d >= 0 && s2 >= 2: b := r64(g_ir_vals, s1 * 8) - p := r64(g_ir_vals, s1 + 1 * 8) + p := bi_to_word_checked(r64(g_ir_vals, s1 + 1 * 8)) lo := load8(b, p) + load8(b, p+1)*256 + load8(b, p+2)*65536 + load8(b, p+3)*16777216 hi := load8(b, p+4) + load8(b, p+5)*256 + load8(b, p+6)*65536 + load8(b, p+7)*16777216 if hi < 0: hi = hi + 4294967296 - w64(g_ir_vals, d * 8, lo + hi * 4294967296) + w64(g_ir_vals, d * 8, bi_from_word(lo + hi * 4294967296)) if str_eq(fn_name, "store_str_ptr") != 0: if s2 >= 3: b := r64(g_ir_vals, s1 * 8) - p := r64(g_ir_vals, s1 + 1 * 8) - v := r64(g_ir_vals, s1 + 2 * 8) + p := bi_to_word_checked(r64(g_ir_vals, s1 + 1 * 8)) + v := bi_to_word_checked(r64(g_ir_vals, s1 + 2 * 8)) lo : ., mut = v % 4294967296 hi : ., mut = v / 4294967296 if v < 0: @@ -325,7 +359,9 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: t4 := sh_dfn_code_of_slots(r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_TK), r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_AUX)) if op2 == 1 && d2 >= 0: - w64(g_ir_vals, d2 * 8, t1) + w64(g_ir_vals, d2 * 8, ir_interp_const(t4, t1)) + if op2 == 40 && d2 >= 0 && t1 >= 0: + w64(g_ir_vals, d2 * 8, bi_from_dec(istr_get(t1))) if op2 == 2 && t1 >= 0 && t2 >= 0: ir_interp_binary(d2, t1, t2, t3, t4) if op2 == 49 || op2 == 50: @@ -335,7 +371,10 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 3 && t1 >= 0: ov2 := r64(g_ir_vals, t1 * 8) if t3 == 1: - w64(g_ir_vals, d2 * 8, -ov2) + if irv_type(t1) == TI_INT: + w64(g_ir_vals, d2 * 8, bi_neg(ov2)) + else: + w64(g_ir_vals, d2 * 8, -ov2) else if t3 == 2: if ov2 == 0: w64(g_ir_vals, d2 * 8, 1) @@ -391,7 +430,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 15 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_LOAD_INDEX_VAR: t1=arr_var, t2=idx_var av2 := r64(g_ir_vals, t1 * 8) - ix2 := r64(g_ir_vals, t2 * 8) + ix2 := bi_to_word_checked(r64(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: w64(g_ir_vals, d2 * 8, str_load8(av2, ix2)) else: @@ -400,7 +439,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 16 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX_VAR: d2=val_var, t1=arr_var, t2=idx_var av2 := r64(g_ir_vals, t1 * 8) - ix2 := r64(g_ir_vals, t2 * 8) + ix2 := bi_to_word_checked(r64(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: store8(istr_get(av2), ix2, r64(g_ir_vals, d2 * 8)) else: @@ -408,7 +447,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: w64(@str_of(av2), ix2 * 8, r64(g_ir_vals, d2 * 8)) if op2 == 31 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_ADDR_INDEX: &arr[i] - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + r64(g_ir_vals, t2 * 8) * 8) + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + bi_to_word_checked(r64(g_ir_vals, t2 * 8)) * 8) if op2 == 11 && d2 >= 0 && t1 >= 0: // IR_LOAD_FIELD: t1=struct_var, t3=field_idx pf2 := r64(g_ir_vals, t1 * 8) @@ -471,7 +510,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: p23 := r64(g_ir_vals, t1 * 8) if p23 != 0: unsafe: - w64(g_ir_vals, d2 * 8, r64(@str_of(p23), 0)) + w64(g_ir_vals, d2 * 8, bi_from_word(r64(@str_of(p23), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op2 == 18: if d2 >= 0 && t1 >= 0: @@ -483,14 +522,14 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op2 == 24: if d2 >= 0 && t1 >= 0 && t2 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + r64(g_ir_vals, t2 * 8) * 8) + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + bi_to_word_checked(r64(g_ir_vals, t2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. A dynamic // limit uses ti = 1. An index below 0 or at least the limit aborts with -1. if op2 == 30 && t2 >= 0: - iv30 := r64(g_ir_vals, t1 * 8) + iv30 := bi_to_word_checked(r64(g_ir_vals, t1 * 8)) lim30 : ., mut = t2 if t4 == 1: - lim30 = r64(g_ir_vals, t2 * 8) + lim30 = bi_to_word_checked(r64(g_ir_vals, t2 * 8)) if iv30 < 0 || iv30 >= lim30: g_interp_abort = -1 break @@ -506,6 +545,13 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 29: if d2 >= 0: w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. + if op2 == 52: + if d2 >= 0 && t1 >= 0: + w64(g_ir_vals, d2 * 8, bi_from_uword(r64(g_ir_vals, t1 * 8))) + if op2 == 53: + if d2 >= 0 && t1 >= 0: + w64(g_ir_vals, d2 * 8, bi_to_uword_checked(r64(g_ir_vals, t1 * 8))) // IR_FNADDR: no real addresses in the interpreter; store 0. if op2 == 48: if d2 >= 0: @@ -690,11 +736,15 @@ fn ir_interpret() -> int: if op == 1: if d >= 0: - w64(g_ir_vals, d * 8, s1) + w64(g_ir_vals, d * 8, ir_interp_const(ti, s1)) // IR_CONST; dex uses a scaled integer in s1. + if op == 40: + if d >= 0 && s1 >= 0: + w64(g_ir_vals, d * 8, bi_from_dec(istr_get(s1))) + // IR_CONST_BIG if op == 5: if s1 >= 0: - return r64(g_ir_vals, s1 * 8) + return bi_to_word_checked(r64(g_ir_vals, s1 * 8)) return 0 // IR_RETURN @@ -706,7 +756,10 @@ fn ir_interpret() -> int: // IR_UNARY ov := r64(g_ir_vals, s1 * 8) if s3 == 1: - w64(g_ir_vals, d * 8, -ov) + if irv_type(s1) == TI_INT: + w64(g_ir_vals, d * 8, bi_neg(ov)) + else: + w64(g_ir_vals, d * 8, -ov) if s3 == 2: if ov == 0: w64(g_ir_vals, d * 8, 1) @@ -800,7 +853,7 @@ fn ir_interpret() -> int: // IR_LOAD_INDEX_VAR: s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: arr_value := r64(g_ir_vals, s1 * 8) - idx := r64(g_ir_vals, s2 * 8) + idx := bi_to_word_checked(r64(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: w64(g_ir_vals, d * 8, str_load8(arr_value, idx)) else: @@ -810,7 +863,7 @@ fn ir_interpret() -> int: // IR_STORE_INDEX_VAR: d=val_var, s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: arr_value := r64(g_ir_vals, s1 * 8) - idx := r64(g_ir_vals, s2 * 8) + idx := bi_to_word_checked(r64(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: store8(istr_get(arr_value), idx, r64(g_ir_vals, d * 8)) else: @@ -838,7 +891,7 @@ fn ir_interpret() -> int: ptr := r64(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - w64(g_ir_vals, d * 8, r64(@str_of(ptr), 0)) + w64(g_ir_vals, d * 8, bi_from_word(r64(@str_of(ptr), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op == 18: if d >= 0 && s1 >= 0: @@ -851,15 +904,15 @@ fn ir_interpret() -> int: // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op == 24: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + r64(g_ir_vals, s2 * 8) * 8) + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + bi_to_word_checked(r64(g_ir_vals, s2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. An index // below 0 or at least the limit aborts with -1. if op == 30 && s2 >= 0: - iv := r64(g_ir_vals, s1 * 8) + iv := bi_to_word_checked(r64(g_ir_vals, s1 * 8)) limit : ., mut = s2 if ti == 1: - limit = r64(g_ir_vals, s2 * 8) + limit = bi_to_word_checked(r64(g_ir_vals, s2 * 8)) if iv < 0 || iv >= limit: return -1 @@ -876,6 +929,14 @@ fn ir_interpret() -> int: if d >= 0 && s1 >= 0: w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. + if op == 52: + if d >= 0 && s1 >= 0: + w64(g_ir_vals, d * 8, bi_from_uword(r64(g_ir_vals, s1 * 8))) + if op == 53: + if d >= 0 && s1 >= 0: + w64(g_ir_vals, d * 8, bi_to_uword_checked(r64(g_ir_vals, s1 * 8))) + // IR_FNADDR: the interpreter has no real addresses; store 0. if op == 48: if d >= 0: @@ -902,7 +963,7 @@ fn ir_interpret() -> int: // IR_ADDR_INDEX: d = slot s1 + 8 * slot s2 for &arr[i]. if op == 31: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + r64(g_ir_vals, s2 * 8) * 8) + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + bi_to_word_checked(r64(g_ir_vals, s2 * 8)) * 8) // IR_ARENA_NEW and IR_ARENA_RESET: the slot model has no scoped arena, so // these are approximations. if op == 32: diff --git a/tests/selfhost/test_bigint_interp.py b/tests/selfhost/test_bigint_interp.py new file mode 100644 index 00000000..4f30c551 --- /dev/null +++ b/tests/selfhost/test_bigint_interp.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Interpreter/ELF parity gate for tagged BigInt values.""" + +import subprocess +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parents[2] +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" + +SRC = r""" +struct Pair: + a: int + b: int + +enum Box: + Empty + One(int) + Two(int, int) + +g: int = 1267650600228229401496703205376 + +fn fact(n: int) -> int: + if n <= 1: + return 1 + return n * fact(n - 1) + +fn main() -> int: + x := 1267650600228229401496703205376 + y := x + 123 + if y != 1267650600228229401496703205499: + return 1 + if x * 3 != 3802951800684688204490109616128: + return 2 + if x / 3 != 422550200076076467165567735125: + return 3 + if x % 3 != 1: + return 4 + if -x != -1267650600228229401496703205376: + return 5 + + arr : ., mut = [x, y, x + 2] + i := 1 + if arr[i] != y: + return 6 + arr[i] = x + 10 + if arr[1] != x + 10: + return 7 + + p : ., mut = Pair { a = x, b = y } + if p.a + p.b != x + y: + return 8 + + b := Two(x, y) + v := match b: + Empty => 0, + One(q) => q, + Two(q, r) => q + r, + if v != x + y: + return 9 + + s := "hello" + if str_len(s) != 5: + return 10 + if s[1] != 101: + return 11 + + f := &arr[1] + if *(f + 1) != x + 2: + return 12 + if (f + 1) - f != 1: + return 13 + + if fact(21) != 51090942171709440000: + return 14 + if g + 1 != 1267650600228229401496703205377: + return 15 + + lo := 1 + hi := 3 + sl := arr[lo..hi] + if sl[0] != x + 10: + return 16 + return 0 +""" + +DIV_ZERO = r""" +fn main() -> int: + x := 9223372036854775808 + return x / 0 +""" + + +def run(args): + return subprocess.run(args, cwd=str(BASE), capture_output=True, text=True) + + +def build_elf(src: Path, ccr: Path, exe: Path) -> str: + r = run([str(COREC), "ccr", str(src), "-o", str(ccr)]) + if r.returncode != 0: + return r.stdout + r.stderr + r = run([str(COREARCH), str(ccr), "--big-int", "-o", str(exe)]) + if r.returncode != 0: + return r.stdout + r.stderr + return "" + + +def main() -> int: + if not COREC.exists() or not COREARCH.exists(): + print("missing build/corec or build/corearch") + return 1 + with tempfile.TemporaryDirectory(prefix="core-bigint-interp-") as td: + d = Path(td) + src = d / "parity.cr" + ccr = d / "parity.ccr" + exe = d / "parity" + src.write_text(SRC, encoding="utf-8") + err = build_elf(src, ccr, exe) + if err: + print(err) + return 1 + elf = run([str(exe)]) + interp = run([str(COREC), "run", SRC]) + if elf.returncode != 0: + print(f"ELF parity exit {elf.returncode}") + return 1 + if interp.returncode != elf.returncode: + print(f"interpreter {interp.returncode} != ELF {elf.returncode}") + print(interp.stdout) + print(interp.stderr) + return 1 + + zsrc = d / "zero.cr" + zccr = d / "zero.ccr" + zexe = d / "zero" + zsrc.write_text(DIV_ZERO, encoding="utf-8") + err = build_elf(zsrc, zccr, zexe) + if err: + print(err) + return 1 + ze = run([str(zexe)]) + zi = run([str(COREC), "run", DIV_ZERO]) + if ze.returncode == 0 or zi.returncode == 0: + print("division by zero did not fail on both faces") + return 1 + if "division by zero" not in (ze.stdout + ze.stderr): + print("ELF missing division-by-zero text") + return 1 + if "division by zero" not in (zi.stdout + zi.stderr): + print("interpreter missing division-by-zero text") + return 1 + + print("PASS bigint interp") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 3ffcfae284154403db35d1ac1504ae6a75242bc1 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 12:23:31 +0800 Subject: [PATCH 25/58] compiler: import BigInt runtime for the interpreter --- src/compiler/_import.cr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index 3d600ece..a66434cf 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -44,6 +44,7 @@ import project import os import fmt import io +import bigint import interp import dump import monomorph From 91174a657d5342ead1438ccfc687eaaa14178272 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 12:25:30 +0800 Subject: [PATCH 26/58] docs: record the raw-compiler blocker for the default BigInt switch --- docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index aa206288..b8193b0e 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -398,3 +398,7 @@ cmp /tmp/corec2 /tmp/corec3 3. 现有 `test_mw_*` 测试依赖 2-limb 实现细节,不能直接保留。需要按语义重写,而不是改期望值。 4. `.ccr` 和 `.cir` 版本变化后,旧 corearch 和旧缓存必须拒绝,不能静默读错。 5. 指针、FFI 和原始字边界如果漏掉范围检查,会把大整数对象的指针传给外部代码,造成难定位的运行期错误。 + +## 任务 9 当前裁决 + +`--big-int` 已完成任务 5-8 的用户程序语义,但默认切换仍阻塞:把 `corearch` 默认改成 tagged 会同时把编译器自身的索引/计数装箱,自举前端在解析阶段崩溃。编译器和自举链必须继续走 raw;默认切换需要显式的构建链模式分离(用户程序 tagged、编译器 project 构建 raw),并重新采集 canary。该决定落定前不要删除 raw 路径。 From 809aba30fe9f42fad3d14082804365a452e7cc1b Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 12:25:56 +0800 Subject: [PATCH 27/58] bigint: switch user programs to tagged ints by default --- .../2026-10-23-arbitrary-precision-int.md | 8 +- src/arch/x86_64/backend_flags.cr | 11 ++ src/arch/x86_64/corearch.cr | 2 +- src/arch/x86_64/instr.cr | 5 + src/compiler/ccr_io.cr | 22 ++- src/compiler/interp.cr | 142 ++++++++++++----- src/compiler/ir_gen.cr | 78 +++++++++- src/os/linux/entry.cr | 10 +- src/runtime/bigint_blob.cr | 146 +++++++++++++++++- src/targets/x86_64-linux/main.cr | 2 +- tests/selfhost/test_ccr_types.py | 2 +- tests/selfhost/test_ccr_v7.py | 2 +- tests/selfhost/test_view_builtins.py | 57 +++++-- tools/baseline/canary_values.tsv | 20 ++- tools/gen_bigint_blob.py | 20 ++- 15 files changed, 445 insertions(+), 82 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index b8193b0e..a83271cd 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -401,4 +401,10 @@ cmp /tmp/corec2 /tmp/corec3 ## 任务 9 当前裁决 -`--big-int` 已完成任务 5-8 的用户程序语义,但默认切换仍阻塞:把 `corearch` 默认改成 tagged 会同时把编译器自身的索引/计数装箱,自举前端在解析阶段崩溃。编译器和自举链必须继续走 raw;默认切换需要显式的构建链模式分离(用户程序 tagged、编译器 project 构建 raw),并重新采集 canary。该决定落定前不要删除 raw 路径。 +已落地默认切换,但保留 raw 回退用于编译器自举: + +- 用户程序的 `.ccr` header reserved 字段为 0,`corearch` 默认按 tagged BigInt 发射。 +- 编译器/自举链的 `.ccr` header 写 `CCR_FLAG_RAW_INT`;`corearch` 自动按 raw 发射,也支持显式 `--raw-int`。 +- `.ccr` 版本为 v9.1 `(9 << 16) | 1`;`.cir` 缓存版本为 23。 +- 2-limb `tag2l` 仍作为 raw 编译器/回退路径保留,尚未删除;默认用户程序不经过它。 +- canary 已按 v9.1、默认 tagged ELF 和全局整数初始化重新锁定。 diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index f2684463..9961935a 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -9,6 +9,7 @@ fn register_backend_flags(): cli_flag("output", "o", "Output path") cli_flag("opt-level", "O", "Optimization level (0-3, default=0)") cli_flag_bool("big-int", "", "Arbitrary precision int semantics for user programs") + cli_flag_bool("raw-int", "", "Force raw machine-int semantics (compiler/self-host builds)") cli_flag("table", "", "HIT table file (load & emit mapped ops through it)") cli_flag_debug("hit-events-file", "", "Debug: inject text event stream file") cli_flag_bool("dump-events", "", "Dump lowered HIT event stream + const pool") @@ -25,3 +26,13 @@ fn register_backend_flags(): cli_flag_bool_debug("dump-nod-items", "", "Debug: dump NOD type terms") cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") + +// Select the int representation after the .ccr header has been read. Compiler +// and self-host artifacts carry CCR_FLAG_RAW_INT; ordinary user programs get +// tagged mathematical ints by default. --raw-int is an explicit build-chain +// escape hatch. +fn backend_select_int_mode(): + if cli_has("raw-int") != 0 || g_ccr_raw_int != 0: + g_x86_big_enabled = 0 + else: + g_x86_big_enabled = 1 diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index 66249efe..7814c63f 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -300,7 +300,6 @@ fn corearch_main() -> int: if cli_parse() != 0: return 1 - g_x86_big_enabled = cli_has("big-int") // Boot: select the instance from the flags, then query its declaration row at // each reject, table, and O2 gate. instance_decl_init() @@ -373,6 +372,7 @@ fn corearch_main() -> int: if r != 0: println("error: invalid .ccr file") return 1 + backend_select_int_mode() init_backend_arrays() // Hidden channel: dump the TYPE segment after load. It uses the same print path diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 8e04de30..39951261 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -964,6 +964,11 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // 第二遍:栈参数(右到左压,第 7 个 int / 第 9 个 binary64 超限才压) stack_total := cs_stack_count(fa, ac) cp = cs_stack_args(buf, pos, cp, fa, ac) + // The allocator is emitted as a raw runtime body. Its one int argument + // is tagged in user programs; convert at this call boundary. + if g_x86_big_enabled != 0 && s3 >= 0 && str_eq(istr_get(s3), "alloc") != 0: + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_to_word_checked")) + cp = cp + e2_mov(buf, pos+cp, 7, 0) // Match builtins by interned string index (integer compare, no str_eq) if s3 == g_ni_syscall3: if g_x86_big_enabled != 0: diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index d1ca177c..e1fe4e93 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -109,12 +109,18 @@ // No bitwise ops in Core; use arithmetic instead. CCR_MAGIC : int = 827474755 // "CCR1" (0x31524343) -CCR_VERSION : int = 9 // v9 only. The loader rejects any other version; - // there is no conversion path. +// v9.1 = (9 << 16) | 1 = 0x00090001. The loader accepts only this value. +CCR_VERSION : int = 589825 +CCR_FLAG_RAW_INT : int = 1 // header reserved field: compiler/self-host program CCR_SEG_COUNT : int = 8 // STR SYM NOD ENT REG EDG TYPE IFACE CCR_SEG_TYPE : int = 7 // TYPE 段 tag CCR_SEG_IFACE : int = 8 // IFACE 段 tag +// Set by save_ccr from corec (compiler-source detection) and by load_ccr in the +// backend. It selects the raw-int compiler path instead of user-program tagging. +g_ccr_emit_raw_int : int, mut +g_ccr_raw_int : int, mut + // NOD record: 40 bytes. // {op i32, dest i32, s1 i64, src2 i32, src3 i32, tk i32, item i32, // first_edge u32, edge_count u32}. @@ -541,8 +547,11 @@ fn save_ccr(path: string) -> int: pos = pos + 4 buf_write_u32(buf, pos, CCR_SEG_COUNT) pos = pos + 4 - buf_write_u32(buf, pos, 0) - pos = pos + 4 // reserved + rsv : ., mut = 0 + if g_ccr_emit_raw_int != 0: + rsv = CCR_FLAG_RAW_INT + buf_write_u32(buf, pos, rsv) + pos = pos + 4 // reserved / raw-compiler flag // 段体大小(与 calc_ccr_size 同一来源逐段一致) s1 := ccr_str_seg_size() @@ -1073,6 +1082,11 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 rsv := buf_read_u32(data, pos) pos = pos + 4 + if rsv != 0 && rsv != CCR_FLAG_RAW_INT: + return -1 + g_ccr_raw_int = 0 + if rsv == CCR_FLAG_RAW_INT: + g_ccr_raw_int = 1 if seg_cnt > (fsize - 16) / 12: return -1 diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index 44a44520..553bdac5 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -17,10 +17,21 @@ g_ir_vals : string, mut g_ir_vals_cap : int, mut +g_interp_big : int, mut = 1 + +fn ir_interp_word(v: int) -> int: + if g_interp_big != 0: + return bi_to_word_checked(v) + return v + +fn ir_interp_tag(v: int) -> int: + if g_interp_big != 0: + return bi_from_word(v) + return v fn ir_interp_const(ti: int, raw: int) -> int: if ti == TI_INT: - return bi_from_word(raw) + return ir_interp_tag(raw) return raw // Call dispatch and inline callee execution. This is the only implementation for @@ -40,25 +51,66 @@ IR_INTERP_MAX_DEPTH : int = 400 // overwrite the outer frame. g_interp_stack : string, mut +fn ir_interp_binary_raw(d: int, s1: int, s2: int, s3: int): + if d < 0: + return + lv := r64(g_ir_vals, s1 * 8) + rv := r64(g_ir_vals, s2 * 8) + if s3 == 1: + w64(g_ir_vals, d * 8, lv + rv) + else if s3 == 2: + w64(g_ir_vals, d * 8, lv - rv) + else if s3 == 3: + w64(g_ir_vals, d * 8, lv * rv) + else if s3 == 4: + w64(g_ir_vals, d * 8, lv / rv) + else if s3 == 5: + w64(g_ir_vals, d * 8, lv % rv) + else if s3 == 6: + w64(g_ir_vals, d * 8, lv == rv) + else if s3 == 7: + w64(g_ir_vals, d * 8, lv != rv) + else if s3 == 8: + w64(g_ir_vals, d * 8, lv < rv) + else if s3 == 9: + w64(g_ir_vals, d * 8, lv > rv) + else if s3 == 10: + w64(g_ir_vals, d * 8, lv <= rv) + else if s3 == 11: + w64(g_ir_vals, d * 8, lv >= rv) + else if s3 == 12: + w64(g_ir_vals, d * 8, lv != 0 && rv != 0) + else if s3 == 13: + w64(g_ir_vals, d * 8, lv != 0 || rv != 0) + else if s3 == 17: + w64(g_ir_vals, d * 8, lv + rv * 8) + else if s3 == 18: + w64(g_ir_vals, d * 8, lv - rv * 8) + else if s3 == 19: + w64(g_ir_vals, d * 8, (lv - rv) / 8) + // Shared IR_BINARY dispatch for the main loop and callee loops. // g_ir_vals int slots hold tagged mathematical integers. Char/bool/dex and // pointer values remain raw machine words, so the operand variable types select // the representation. Pointer arithmetic converts its int offset through the // same checked boundary as the ELF backend. fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): + if g_interp_big == 0: + ir_interp_binary_raw(d, s1, s2, s3) + return if d < 0: return lv := r64(g_ir_vals, s1 * 8) rv := r64(g_ir_vals, s2 * 8) if s3 == 17 || s3 == 18 || s3 == 19: // Pointer add/sub/diff: the pointer is raw; the offset is mathematical. - off := bi_to_word_checked(rv) + off := ir_interp_word(rv) if s3 == 17: w64(g_ir_vals, d * 8, lv + off * 8) else if s3 == 18: w64(g_ir_vals, d * 8, lv - off * 8) else: - w64(g_ir_vals, d * 8, bi_from_word((lv - rv) / 8)) + w64(g_ir_vals, d * 8, ir_interp_tag((lv - rv) / 8)) return if s3 == 12 || s3 == 13: // Boolean and/or store raw 0/1. @@ -75,9 +127,9 @@ fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): l := lv r := rv if !lhs_char && s1 >= 0: - l = bi_to_word_checked(lv) + l = ir_interp_word(lv) if !rhs_char && s2 >= 0: - r = bi_to_word_checked(rv) + r = ir_interp_word(rv) if s3 == 6: w64(g_ir_vals, d * 8, l == r) else if s3 == 7: @@ -182,7 +234,7 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): w64(g_ir_vals, d * 8, 0) if str_eq(fn_name, "str_len") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, bi_from_word(istr_len(r64(g_ir_vals, s1 * 8)))) + w64(g_ir_vals, d * 8, ir_interp_tag(istr_len(r64(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "str_eq") != 0: if d >= 0 && s2 >= 2: left_s := istr_get(r64(g_ir_vals, s1 * 8)) @@ -194,35 +246,38 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))) if str_eq(fn_name, "int_str") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, str_intern(bi_to_dec(r64(g_ir_vals, s1 * 8)))) + if g_interp_big != 0: + w64(g_ir_vals, d * 8, str_intern(bi_to_dec(r64(g_ir_vals, s1 * 8)))) + else: + w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "chr") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, str_intern(chr(bi_to_word_checked(r64(g_ir_vals, s1 * 8))))) + w64(g_ir_vals, d * 8, str_intern(chr(ir_interp_word(r64(g_ir_vals, s1 * 8))))) if str_eq(fn_name, "get_char") != 0: if d >= 0 && s2 >= 2: w64(g_ir_vals, d * 8, ir_interp_str_char( r64(g_ir_vals, s1 * 8), - bi_to_word_checked(r64(g_ir_vals, (s1 + 1) * 8)))) + ir_interp_word(r64(g_ir_vals, (s1 + 1) * 8)))) if str_eq(fn_name, "str_sub") != 0: if d >= 0 && s2 >= 3: w64(g_ir_vals, d * 8, ir_interp_str_sub( r64(g_ir_vals, s1 * 8), - bi_to_word_checked(r64(g_ir_vals, (s1 + 1) * 8)), - bi_to_word_checked(r64(g_ir_vals, (s1 + 2) * 8)))) + ir_interp_word(r64(g_ir_vals, (s1 + 1) * 8)), + ir_interp_word(r64(g_ir_vals, (s1 + 2) * 8)))) if str_eq(fn_name, "load_str_ptr") != 0: if d >= 0 && s2 >= 2: b := r64(g_ir_vals, s1 * 8) - p := bi_to_word_checked(r64(g_ir_vals, s1 + 1 * 8)) + p := ir_interp_word(r64(g_ir_vals, s1 + 1 * 8)) lo := load8(b, p) + load8(b, p+1)*256 + load8(b, p+2)*65536 + load8(b, p+3)*16777216 hi := load8(b, p+4) + load8(b, p+5)*256 + load8(b, p+6)*65536 + load8(b, p+7)*16777216 if hi < 0: hi = hi + 4294967296 - w64(g_ir_vals, d * 8, bi_from_word(lo + hi * 4294967296)) + w64(g_ir_vals, d * 8, ir_interp_tag(lo + hi * 4294967296)) if str_eq(fn_name, "store_str_ptr") != 0: if s2 >= 3: b := r64(g_ir_vals, s1 * 8) - p := bi_to_word_checked(r64(g_ir_vals, s1 + 1 * 8)) - v := bi_to_word_checked(r64(g_ir_vals, s1 + 2 * 8)) + p := ir_interp_word(r64(g_ir_vals, s1 + 1 * 8)) + v := ir_interp_word(r64(g_ir_vals, s1 + 2 * 8)) lo : ., mut = v % 4294967296 hi : ., mut = v / 4294967296 if v < 0: @@ -371,7 +426,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 3 && t1 >= 0: ov2 := r64(g_ir_vals, t1 * 8) if t3 == 1: - if irv_type(t1) == TI_INT: + if g_interp_big != 0 && irv_type(t1) == TI_INT: w64(g_ir_vals, d2 * 8, bi_neg(ov2)) else: w64(g_ir_vals, d2 * 8, -ov2) @@ -430,7 +485,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 15 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_LOAD_INDEX_VAR: t1=arr_var, t2=idx_var av2 := r64(g_ir_vals, t1 * 8) - ix2 := bi_to_word_checked(r64(g_ir_vals, t2 * 8)) + ix2 := ir_interp_word(r64(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: w64(g_ir_vals, d2 * 8, str_load8(av2, ix2)) else: @@ -439,7 +494,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 16 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX_VAR: d2=val_var, t1=arr_var, t2=idx_var av2 := r64(g_ir_vals, t1 * 8) - ix2 := bi_to_word_checked(r64(g_ir_vals, t2 * 8)) + ix2 := ir_interp_word(r64(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: store8(istr_get(av2), ix2, r64(g_ir_vals, d2 * 8)) else: @@ -447,7 +502,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: w64(@str_of(av2), ix2 * 8, r64(g_ir_vals, d2 * 8)) if op2 == 31 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_ADDR_INDEX: &arr[i] - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + bi_to_word_checked(r64(g_ir_vals, t2 * 8)) * 8) + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + ir_interp_word(r64(g_ir_vals, t2 * 8)) * 8) if op2 == 11 && d2 >= 0 && t1 >= 0: // IR_LOAD_FIELD: t1=struct_var, t3=field_idx pf2 := r64(g_ir_vals, t1 * 8) @@ -510,7 +565,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: p23 := r64(g_ir_vals, t1 * 8) if p23 != 0: unsafe: - w64(g_ir_vals, d2 * 8, bi_from_word(r64(@str_of(p23), 0))) + w64(g_ir_vals, d2 * 8, ir_interp_tag(r64(@str_of(p23), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op2 == 18: if d2 >= 0 && t1 >= 0: @@ -522,14 +577,14 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op2 == 24: if d2 >= 0 && t1 >= 0 && t2 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + bi_to_word_checked(r64(g_ir_vals, t2 * 8)) * 8) + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + ir_interp_word(r64(g_ir_vals, t2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. A dynamic // limit uses ti = 1. An index below 0 or at least the limit aborts with -1. if op2 == 30 && t2 >= 0: - iv30 := bi_to_word_checked(r64(g_ir_vals, t1 * 8)) + iv30 := ir_interp_word(r64(g_ir_vals, t1 * 8)) lim30 : ., mut = t2 if t4 == 1: - lim30 = bi_to_word_checked(r64(g_ir_vals, t2 * 8)) + lim30 = ir_interp_word(r64(g_ir_vals, t2 * 8)) if iv30 < 0 || iv30 >= lim30: g_interp_abort = -1 break @@ -548,10 +603,16 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. if op2 == 52: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, bi_from_uword(r64(g_ir_vals, t1 * 8))) + if g_interp_big != 0: + w64(g_ir_vals, d2 * 8, bi_from_uword(r64(g_ir_vals, t1 * 8))) + else: + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) if op2 == 53: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, bi_to_uword_checked(r64(g_ir_vals, t1 * 8))) + if g_interp_big != 0: + w64(g_ir_vals, d2 * 8, bi_to_uword_checked(r64(g_ir_vals, t1 * 8))) + else: + w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) // IR_FNADDR: no real addresses in the interpreter; store 0. if op2 == 48: if d2 >= 0: @@ -644,6 +705,9 @@ fn ir_interpret() -> int: // value instead of a shared temporary slot. g_interp_abort = 0 g_interp_call_depth = 0 + g_interp_big = 1 + if g_ccr_emit_raw_int != 0: + g_interp_big = 0 g_interp_stack = alloc(IR_INTERP_MAX_DEPTH * 8) // 活动函数栈(重入检测) // Initialize value store (size = node_count + padding for destinations) @@ -744,7 +808,7 @@ fn ir_interpret() -> int: // IR_CONST_BIG if op == 5: if s1 >= 0: - return bi_to_word_checked(r64(g_ir_vals, s1 * 8)) + return ir_interp_word(r64(g_ir_vals, s1 * 8)) return 0 // IR_RETURN @@ -756,7 +820,7 @@ fn ir_interpret() -> int: // IR_UNARY ov := r64(g_ir_vals, s1 * 8) if s3 == 1: - if irv_type(s1) == TI_INT: + if g_interp_big != 0 && irv_type(s1) == TI_INT: w64(g_ir_vals, d * 8, bi_neg(ov)) else: w64(g_ir_vals, d * 8, -ov) @@ -853,7 +917,7 @@ fn ir_interpret() -> int: // IR_LOAD_INDEX_VAR: s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: arr_value := r64(g_ir_vals, s1 * 8) - idx := bi_to_word_checked(r64(g_ir_vals, s2 * 8)) + idx := ir_interp_word(r64(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: w64(g_ir_vals, d * 8, str_load8(arr_value, idx)) else: @@ -863,7 +927,7 @@ fn ir_interpret() -> int: // IR_STORE_INDEX_VAR: d=val_var, s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: arr_value := r64(g_ir_vals, s1 * 8) - idx := bi_to_word_checked(r64(g_ir_vals, s2 * 8)) + idx := ir_interp_word(r64(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: store8(istr_get(arr_value), idx, r64(g_ir_vals, d * 8)) else: @@ -891,7 +955,7 @@ fn ir_interpret() -> int: ptr := r64(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - w64(g_ir_vals, d * 8, bi_from_word(r64(@str_of(ptr), 0))) + w64(g_ir_vals, d * 8, ir_interp_tag(r64(@str_of(ptr), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op == 18: if d >= 0 && s1 >= 0: @@ -904,15 +968,15 @@ fn ir_interpret() -> int: // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op == 24: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + bi_to_word_checked(r64(g_ir_vals, s2 * 8)) * 8) + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + ir_interp_word(r64(g_ir_vals, s2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. An index // below 0 or at least the limit aborts with -1. if op == 30 && s2 >= 0: - iv := bi_to_word_checked(r64(g_ir_vals, s1 * 8)) + iv := ir_interp_word(r64(g_ir_vals, s1 * 8)) limit : ., mut = s2 if ti == 1: - limit = bi_to_word_checked(r64(g_ir_vals, s2 * 8)) + limit = ir_interp_word(r64(g_ir_vals, s2 * 8)) if iv < 0 || iv >= limit: return -1 @@ -932,10 +996,16 @@ fn ir_interpret() -> int: // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. if op == 52: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, bi_from_uword(r64(g_ir_vals, s1 * 8))) + if g_interp_big != 0: + w64(g_ir_vals, d * 8, bi_from_uword(r64(g_ir_vals, s1 * 8))) + else: + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) if op == 53: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, bi_to_uword_checked(r64(g_ir_vals, s1 * 8))) + if g_interp_big != 0: + w64(g_ir_vals, d * 8, bi_to_uword_checked(r64(g_ir_vals, s1 * 8))) + else: + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) // IR_FNADDR: the interpreter has no real addresses; store 0. if op == 48: @@ -963,7 +1033,7 @@ fn ir_interpret() -> int: // IR_ADDR_INDEX: d = slot s1 + 8 * slot s2 for &arr[i]. if op == 31: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + bi_to_word_checked(r64(g_ir_vals, s2 * 8)) * 8) + w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + ir_interp_word(r64(g_ir_vals, s2 * 8)) * 8) // IR_ARENA_NEW and IR_ARENA_RESET: the slot model has no scoped arena, so // these are approximations. if op == 32: diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 4331d250..d01d4441 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -2173,14 +2173,14 @@ fn gen_expr(node: int) -> int: if str_eq(name, "str_of") != 0 || str_eq(name, "ptr_of") != 0: inner := gen_expr(ast_a(first_arg)) inner = force_if_thunk(inner) - // Keep the view as a zero-byte alias in raw mode. In --big-int - // the backend converts this same slot between a raw address and - // a tagged mathematical int. if str_eq(name, "ptr_of") != 0: - emit(IR_PTR_OF, inner, inner, 0, 0, TI_INT) + rv := new_ir_var("_ptr_of", TI_INT) + emit(IR_PTR_OF, rv, inner, 0, 0, TI_INT) + return rv else: - emit(IR_STR_OF, inner, inner, 0, 0, TI_STR) - return inner + rv := new_ir_var("_str_of", TI_STR) + emit(IR_STR_OF, rv, inner, 0, 0, TI_STR) + return rv return -1 @@ -3542,12 +3542,22 @@ fn global_needs_runtime_init(name_idx: int) -> int: if vn < 0: return 0 vk := ast_kind(vn) - if vk == EXPR_INT || vk == EXPR_BOOL || vk == EXPR_DEX: + if vk == EXPR_INT: + iv0 := ast_int_val(vn) + if iv0 < -4611686018427387904 || iv0 > 4611686018427387903: + return 1 + return 0 + if vk == EXPR_BOOL || vk == EXPR_DEX: return 0 if vk == EXPR_UNARY && ast_c(vn) == UOP_NEG: inner := ast_a(vn) ik := ast_kind(inner) - if ik == EXPR_INT || ik == EXPR_BOOL || ik == EXPR_DEX: + if ik == EXPR_INT: + iv0 := 0 - ast_int_val(inner) + if iv0 < -4611686018427387904 || iv0 > 4611686018427387903: + return 1 + return 0 + if ik == EXPR_BOOL || ik == EXPR_DEX: return 0 return 1 i = i - 1 @@ -3660,6 +3670,7 @@ fn reg_one_global(name_idx: int): g_ir_global_count = g_ir_global_count + 1 fn ir_gen_globals(): + ccr_set_raw_mode_from_source() // The parser records only file-scope declarations in g_global_lets. // Do not scan every EXPR_LET here: that also includes function locals. i : ., mut = 0 @@ -4053,7 +4064,58 @@ fn find_or_create_mono_func(fi: int, call_node: int) -> int: // --- Main entry --- +fn ccr_source_has_marker(marker: string) -> int: + sl := str_len(g_source) + ml := str_len(marker) + if ml <= 0 || sl < ml: + return 0 + i : ., mut = 0 + loop: + if i + ml > sl: + break + j : ., mut = 0 + loop: + if j >= ml: + break + if load8(g_source, i + j) != load8(marker, j): + break + j = j + 1 + if j >= ml: + return 1 + i = i + 1 + return 0 + +fn ccr_source_dir_is_compiler() -> int: + d := g_source_dir + n := str_len(d) + if str_eq(d, "src/compiler") != 0: + return 1 + if n >= 12 && str_eq(str_sub(d, n - 12, 12), "src/compiler") != 0: + return 1 + if n >= 13 && load8(d, n - 1) == 47: + if str_eq(str_sub(d, n - 13, 12), "src/compiler") != 0: + return 1 + return 0 + +fn ccr_set_raw_mode_from_source(): + // Compiler and backend sources keep raw machine integers. User programs use + // the tagged BigInt semantics by default. The .ccr header carries this flag + // to corearch so both sides agree before code generation. + g_ccr_emit_raw_int = 0 + if ccr_source_has_marker("fn parse_expr") != 0: + g_ccr_emit_raw_int = 1 + if ccr_source_has_marker("fn corearch_main") != 0: + g_ccr_emit_raw_int = 1 + if ccr_source_has_marker("import cli") != 0: + // The CLI word-table layer intentionally stores raw string pointers via + // w64/r64 and views them back through @str_of. Legacy raw semantics are + // required for that table; user CLI programs are not arithmetic kernels. + g_ccr_emit_raw_int = 1 + if ccr_source_dir_is_compiler() != 0: + g_ccr_emit_raw_int = 1 + fn ir_gen_all(): + ccr_set_raw_mode_from_source() g_ir_var_count = 0 g_ir_instr_count = 0 g_ir_func_count = 0 diff --git a/src/os/linux/entry.cr b/src/os/linux/entry.cr index fa530d0e..b7732608 100644 --- a/src/os/linux/entry.cr +++ b/src/os/linux/entry.cr @@ -85,6 +85,10 @@ fn emit_start(buf: string, pos: int) -> int: iv := r64(g_ir_globals, gi0 * 24 + 16) gvv0 := r64(g_ir_globals, gi0 * 24 + 8) if iv != 0 && gvv0 >= 0: + ivv : ., mut = iv + if g_x86_big_enabled != 0 && irv_type(gvv0) == TI_INT: + if ivv >= -4611686018427387904 && ivv <= 4611686018427387903: + ivv = ivv * 2 // lea r10, [rip+0], rip_patch for the global's BSS slot rip_pos_g := cp + 3 cp = cp + e2_lr(buf, cp, 0) @@ -92,18 +96,18 @@ fn emit_start(buf: string, pos: int) -> int: w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_g) w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gvv0) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 - if iv >= -2147483647 - 1 && iv <= 2147483647: + if ivv >= -2147483647 - 1 && ivv <= 2147483647: // mov qword [r10], imm32 (sign-extended), 49 C7 02 imm32 w8(buf, cp, 73) w8(buf, cp+1, 199) w8(buf, cp+2, 2) - e2_w32(buf, cp+3, iv) + e2_w32(buf, cp+3, ivv) cp = cp + 7 else: // mov rax, imm64; mov [r10], rax w8(buf, cp, 72) w8(buf, cp+1, 184) - e2_w64(buf, cp+2, iv) + e2_w64(buf, cp+2, ivv) cp = cp + 10 w8(buf, cp, 73) w8(buf, cp+1, 137) diff --git a/src/runtime/bigint_blob.cr b/src/runtime/bigint_blob.cr index 9ad0bfff..413776d5 100644 --- a/src/runtime/bigint_blob.cr +++ b/src/runtime/bigint_blob.cr @@ -8,7 +8,151 @@ BIGINT_BLOB_DATA_OFF : int = 18264 BIGINT_BLOB_DATA_LEN : int = 72 fn bigint_blob_hex() -> string: - return "554889e54883ec5048897df848c745f00100000048c745e802000000488b7df8488b47f84883e801488945e0488b7df0488b75e8488b55f8488b4de04889f84889f74889d64889ca0f05488945d848c745d03c00000048c745c80100000048c745c00000000048c745b800000000488b7dd0488b75c8488b55c0488b4db84889f84889f74889d64889ca0f05488945b0488b45b04883c4505dc3554889e54883ec3848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989d24c8955e848c745e0000000004c8b55e84c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d001000000488b45d04883c4385dc348c745c800000000488b45c84883c4385dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989c24c8955e8488b45e84883c4185dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04d0fafd34c8955e8488b45e84883c4185dc3554889e54883ec2848897df84c8d153f4500004c8955f04c8b55f04c8955e848c745e0010000004c8b55f84c8b5de04d29da4c8955d84c8b55d84c8955e8488b45e84883c4285dc3554889e54883ec2848897df848c745f0000000004c8b55f04c8955e84c8b55f84c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b45d84883c4285dc3554889e54883ec2848897df848c745f0080000004c8b55f84c8b5df04d0fafd34c8955e84c8b153a4500004c8b5de84d01da4c8955e0488b7de0e800000000488945d8488b45d84883c4285dc3554889e54883ec1048897df8488b7df8488b35f1440000488b0437488945f0488b45f04883c4105dc3554889e54883ec1048897df8488b7df8488b35d0440000488b0437488945f0488b45f04883c4105dc3554889e54883ec3048897df8488975f048c745e8080000004c8b55f04c8b5de84d0fafd34c8955e04c8b15974400004c8b5de04d01da4c8955d8488b7df8488b75d8488b0437488945d0488b45d04883c4305dc3554889e54883ec1848897df8488975f0488b7df8488b3547440000488b55f048891437488b45e84883c4185dc3554889e54883ec1848897df8488975f0488b7df8488b3522440000488b55f048891437488b45e84883c4185dc3554889e54883ec3848897df8488975f0488955e848c745e0080000004c8b55f04c8b5de04d0fafd34c8955d84c8b15e54300004c8b5dd84d01da4c8955d0488b7df8488b75d0488b55e848891437488b45c84883c4385dc3554889e54881ec8800000048897df8488b7df8e8b9feffff488945f04c8b55f04c8955e8eb00eb0048c745e0000000004c8b55e84c8b5de04d39da0f9ec04c0fb6d04c8955d84c8b55d84983fa017402eb02eb7848c745d0010000004c8b55e84c8b5dd04d29da4c8955c8488b7df8488b75c8e882feffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f95c04c0fb6d04c8955b04c8b55b04983fa017402eb02eb2448c745a8010000004c8b55e84c8b5da84d29da4c8955a04c8b55a04c8955e8e95affffff488b7df8488b75e8e8a2feffff4889459848c74590000000004c8b55e84c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb1e48c7458000000000488b7df8488b7580e832feffff48898578ffffffeb00488b45f84881c4880000005dc3554889e54881ec3801000048897df849ba00000000000000804c8955f04c8b55f049f7da4c8955e84c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017405e98c00000048c745d803000000488b7dd8e8cbfcffff488945d04c8b55d04c8955c848c745c00200000048c745b808000000488b7dc8488b75c0488b55b8e8ebfdffff488945b048c745a8010000004c8b55a849f7da4c8955a0488b7dc8488b75a0e86dfdffff4889459848c7459003000000488b7dc8488b7590e881fdffff48894588488b45c84881c4380100005dc348c7458003000000488b7d80e83ffcffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b55f84c8b9d68ffffff4d39da0f94c04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb10488b8570ffffff4881c4380100005dc34c8b55f84c899558ffffff48c78550ffffff010000004c8b9550ffffff4c899548ffffff48c78540ffffff000000004c8b9558ffffff4c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb4b48c78530ffffff010000004c8b9530ffffff49f7da4c899528ffffff4c8b9528ffffff4c899548ffffff4c8b9558ffffff49f7da4c899520ffffff4c8b9520ffffff4c899558ffffffeb0048c78518ffffff000000004c8b9518ffffff4c899510ffffffeb00eb0048c78508ffffff000000004c8b9558ffffff4c8b9d08ffffff4d39da0f94c04c0fb6d04c899500ffffff4c8b9500ffffff4983fa017402eb05e9a50000004c8b9558ffffff4c8b1d404000004c89d0489949f7fb4989d24c8995f8feffff488bbd70ffffff488bb510ffffff488b95f8feffffe8f4fbffff488985f0feffff4c8b9558ffffff4c8b1dff3f00004c89d0489949f7fb4989c24c8995e8feffff4c8b95e8feffff4c899558ffffff48c785e0feffff010000004c8b9510ffffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c899510ffffffe91bffffff488bbd70ffffff488bb548ffffffe81cfbffff488985d0feffff488bbd70ffffff488bb510ffffffe82ffbffff488985c8feffff488b8570ffffff4881c4380100005dc3554889e54883ec3848897df8488b7df8e87af8ffff488945f048c745e8000000004c8b55f04c8b5de84d39da0f95c04c0fb6d04c8955e04c8b55e04983fa017402eb17488b7df8e81ff9ffff488945d8488b45d84883c4385dc3488b7df8e8a5f8ffff488945d0488b7dd0e85ffcffff488945c8488b45c84883c4385dc3554889e54881ec5001000048897df8488b7df8e807fbffff488945f0488b7df8e8cbf9ffff488945e84c8b55e84c8955e048c745d8000000004c8b55e04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb1548c745c800000000488b45c84881c4500100005dc348c745c0030000004c8b55e04c8b5dc04d39da0f9fc04c0fb6d04c8955b84c8b55b84983fa017402eb1a488b7df8e892f8ffff488945b0488b45b04881c4500100005dc348c745a800000000488b7df8488b75a8e850f9ffff488945a04c8b55a04c89559848c74590000000004c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff010000004c8b55e04c8b9d70ffffff4d39da0f9fc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb2f48c78560ffffff01000000488b7df8488bb560ffffffe8d0f8ffff48898558ffffff4c8b9558ffffff4c895588eb0048c78550ffffff020000004c8b55e04c8b9d50ffffff4d39da0f9fc04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb3248c78540ffffff02000000488b7df8488bb540ffffffe86bf8ffff48898538ffffff4c8b9538ffffff4c899578ffffffeb0048c78530ffffff030000004c8b9578ffffff4c8b9d30ffffff4d39da0f9fc04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017402eb20488b7df8e82ef7ffff48898520ffffff488b8520ffffff4881c4500100005dc34c8b55884c8b1dbe3c00004d0fafd34c899518ffffff4c8b55984c8b9d18ffffff4d01da4c899510ffffff4c8b9578ffffff4c8b1d983c00004d0fafd34c899508ffffff4c8b9510ffffff4c8b9d08ffffff4d01da4c899500ffffff4c8b9500ffffff4c8995f8feffff488b7df8e836f7ffff488985f0feffff48c785e8feffff000000004c8b95f0feffff4c8b9de8feffff4d39da0f9cc04c0fb6d04c8995e0feffff4c8b95e0feffff4983fa017402eb214c8b95f8feffff49f7da4c8995d8feffff4c8b95d8feffff4c8995f8feffffeb004c8b95f8feffff4c8b1dff3b00004d39da0f9cc04c0fb6d04c8995d0feffff4c8b95f8feffff4c8b1dd83b00004d39da0f9fc04c0fb6d04c8995c8feffff4c8b95d0feffff4c8b9dc8feffff4d09da4c8995c0feffff4c8b95c0feffff4983fa017402eb20488b7df8e8d5f5ffff488985b8feffff488b85b8feffff4881c4500100005dc3488bbdf8feffffe83cf5ffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54883ec7848897df8488b7df8e841f6ffff488945f04c8b55f04c8955e8488b7de8e8b6f5ffff488945e04c8b55e04c8955d8488b7df8e8eef5ffff488945d0488b7dd8488b75d0e883f6ffff488945c8488b7dd8488b75e8e89ff6ffff488945c048c745b8000000004c8b55b84c8955b0eb00eb004c8b55b04c8b5de84d39da0f9dc04c0fb6d04c8955a84c8b55a84983fa017402eb02eb47488b7df8488b75b0e8d5f5ffff488945a0488b7dd8488b75b0488b55a0e86ef6ffff4889459848c74590010000004c8b55b04c8b5d904d01da4c8955884c8b55884c8955b0eb93488b45d84883c4785dc3554889e54881ecd000000048897df8488975f0488b7df8e84cf5ffff488945e84c8b55e84c8955e0488b7df0e837f5ffff488945d84c8b55d84c8955d04c8b55e04c8b5dd04d39da0f9cc04c0fb6d04c8955c84c8b55c84983fa017402eb2048c745c0010000004c8b55c049f7da4c8955b8488b45b84881c4d00000005dc34c8b55e04c8b5dd04d39da0f9fc04c0fb6d04c8955b04c8b55b04983fa017402eb1548c745a801000000488b45a84881c4d00000005dc34c8b55e04c8955a0eb00eb0048c74598000000004c8b55a04c8b5d984d39da0f9ec04c0fb6d04c8955904c8b55904983fa017402eb05e90b01000048c74588010000004c8b55a04c8b5d884d29da4c8955804c8b55804c8955a0488b7df8488b75a0e874f4ffff48898578ffffff4c8b9578ffffff4c899570ffffff488b7df0488b75a0e852f4ffff48898568ffffff4c8b9568ffffff4c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d39da0f9cc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb2c48c78550ffffff010000004c8b9550ffffff49f7da4c899548ffffff488b8548ffffff4881c4d00000005dc34c8b9570ffffff4c8b9d60ffffff4d39da0f9fc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb1b48c78538ffffff01000000488b8538ffffff4881c4d00000005dc3e9c4feffff48c78530ffffff00000000488b8530ffffff4881c4d00000005dc3554889e54881ecc800000048897df8488975f0488b7df8e8c7f8ffff488945e84c8b55e84c8955e0488b7df0e8b2f8ffff488945d84c8b55d84c8955d0488b7de0e8e2f2ffff488945c84c8b55c84c8955c0488b7dd0e8cdf2ffff488945b84c8b55b84c8955b04c8b55c04c8b5db04d39da0f9cc04c0fb6d04c8955a84c8b55a84983fa017402eb2048c745a0010000004c8b55a049f7da4c895598488b45984881c4c80000005dc34c8b55c04c8b5db04d39da0f9fc04c0fb6d04c8955904c8b55904983fa017402eb1548c7458801000000488b45884881c4c80000005dc348c74580000000004c8b55c04c8b5d804d39da0f94c04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb1b48c78570ffffff00000000488b8570ffffff4881c4c80000005dc3488b7de0488b75d0e8b1fcffff48898568ffffff4c8b9568ffffff4c899560ffffff48c78558ffffff000000004c8b55c04c8b9d58ffffff4d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb344c8b9560ffffff49f7da4c899548ffffff488bbd48ffffffe880f0ffff48898540ffffff488b8540ffffff4881c4c80000005dc3488bbd60ffffffe85df0ffff48898538ffffff488b8538ffffff4881c4c80000005dc3554889e54881ec5001000048897df8488975f0488b7df8e85bf1ffff488945e84c8b55e84c8955e0488b7df0e846f1ffff488945d84c8b55d84c8955d04c8b55e04c8955c84c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb0a4c8b55d04c8955c8eb0048c745b8010000004c8b55c84c8b5db84d01da4c8955b0488b7db0e870f0ffff488945a84c8b55a84c8955a048c74598000000004c8b55984c89559048c74588000000004c8b55884c895580eb00eb004c8b55804c8b5dc84d39da0f9dc04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb05e99b01000048c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffff4c8b55804c8b5de04d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb24488b7df8488b7580e84bf0ffff48898548ffffff4c8b9548ffffff4c899568ffffffeb004c8b55804c8b5dd04d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb24488b7df0488b7580e8ffefffff48898538ffffff4c8b9538ffffff4c899558ffffffeb004c8b9568ffffff4c8b9d58ffffff4d01da4c899530ffffff4c8b9530ffffff4c8b5d904d01da4c899528ffffff4c8b9528ffffff4c899520ffffff4c8b9520ffffff4c8b1d6d3400004c89d0489949f7fb4989d24c899518ffffff488b7da0488b7580488b9518ffffffe827f0ffff48898510ffffff4c8b9520ffffff4c8b1d323400004c89d0489949f7fb4989c24c899508ffffff4c8b9508ffffff4c89559048c78500ffffff010000004c8b55804c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c895580e936feffff48c785f0feffff000000004c8b55904c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb45488b7da0488b75c8488b5590e87eefffff488985e0feffff48c785d8feffff010000004c8b55c84c8b9dd8feffff4d01da4c8995d0feffff4c8b95d0feffff4c8955c8eb00488b7da0488b75c8e810efffff488985c8feffff48c785c0feffff01000000488b7da0488bb5c0feffffe8c1eeffff488985b8feffff488b7da0e863efffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54881ec1001000048897df8488975f0488b7df8e801eeffff488945e84c8b55e84c8955e0488b7df0e8ecedffff488945d84c8b55d84c8955d0488b7de0e861edffff488945c84c8b55c84c8955c048c745b8000000004c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb004c8b55a04c8b5de04d39da0f9dc04c0fb6d04c8955984c8b55984983fa017402eb05e98a010000488b7df8488b75a0e89cedffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff4c8b55a04c8b5dd04d39da0f9cc04c0fb6d04c899570ffffff4c8b9570ffffff4983fa017402eb24488b7df0488b75a0e848edffff48898568ffffff4c8b9568ffffff4c899578ffffffeb004c8b55884c8b9d78ffffff4d29da4c899560ffffff4c8b9560ffffff4c8b5db04d29da4c899558ffffff4c8b9558ffffff4c899550ffffff48c78548ffffff000000004c8b9550ffffff4c8b9d48ffffff4d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb3e4c8b9550ffffff4c8b1d803100004d01da4c899538ffffff4c8b9538ffffff4c899550ffffff48c78530ffffff010000004c8b9530ffffff4c8955b0eb1848c78528ffffff000000004c8b9528ffffff4c8955b0eb00488b7dc0488b75a0488b9550ffffffe804edffff48898520ffffff48c78518ffffff010000004c8b55a04c8b9d18ffffff4d01da4c899510ffffff4c8b9510ffffff4c8955a0e94dfeffff488b7dc0488b75e0e893ecffff48898508ffffff48c78500ffffff01000000488b7dc0488bb500ffffffe844ecffff488985f8feffff488b7dc0e8e6ecffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec1001000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb0d488b45f04881c4100100005dc348c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb0d488b45f84881c4100100005dc3488b7df8e8a8f0ffff488945c84c8b55c84c8955c0488b7df0e893f0ffff488945b84c8b55b84c8955b0488b7dc0e8c3eaffff488945a84c8b55a84c8955a0488b7db0e8aeeaffff488945984c8b55984c8955904c8b55a04c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb56488b7dc0488b75b0e825f9ffff488945804c8b55804c899578ffffff488bbd78ffffff488b75a0e8faeaffff48898570ffffff488bbd78ffffffe87af0ffff48898568ffffff488b8568ffffff4881c4100100005dc3488b7dc0488b75b0e8def4ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9558ffffff4c8b9d50ffffff4d39da0f94c04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb1b48c78540ffffff00000000488b8540ffffff4881c4100100005dc348c78538ffffff000000004c8b9558ffffff4c8b9d38ffffff4d39da0f9fc04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb5c488b7dc0488b75b0e87afbffff48898528ffffff4c8b9528ffffff4c899520ffffff488bbd20ffffff488b75a0e8efe9ffff48898518ffffff488bbd20ffffffe86fefffff48898510ffffff488b8510ffffff4881c4100100005dc3488b7db0488b75c0e81efbffff48898508ffffff4c8b9508ffffff4c899500ffffff488bbd00ffffff488b7590e893e9ffff488985f8feffff488bbd00ffffffe813efffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec9000000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1548c745e000000000488b45e04881c4900000005dc3488b7df8e8b6e6ffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb32488b7df8e8f8e6ffff488945c04c8b55c049f7da4c8955b8488b7db8e815e7ffff488945b0488b45b04881c4900000005dc3488b7df8e829e7ffff488945a84c8b55a84c8955a0488b7da0e8c1f1ffff488945984c8b55984c895590488b7da0e8d9e7ffff488945884c8b558849f7da4c895580488b7d90488b7580e863e8ffff48898578ffffff488b7d90e8e6edffff48898570ffffff488b8570ffffff4881c4900000005dc3554889e54883ec2048897df8488975f0488b7df0e8bafeffff488945e8488b7df8488b75e8e8f5fbffff488945e0488b45e04883c4205dc3554889e54881ec1802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4180200005dc3488b7df8e891ecffff488945b84c8b55b84c8955b0488b7df0e87cecffff488945a84c8b55a84c8955a0488b7db0e8d5e6ffff488945984c8b55984c895590488b7da0e8c0e6ffff488945884c8b55884c8955804c8b55904c8b5d804d01da4c899578ffffff488bbd78ffffffe820e6ffff48898570ffffff4c8b9570ffffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffffeb00eb004c8b9558ffffff4c8b5d904d39da0f9dc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb05e96803000048c78548ffffff000000004c8b9548ffffff4c899540ffffff488b7db0488bb558ffffffe834e6ffff48898538ffffff4c8b9538ffffff4c899530ffffff48c78528ffffff000000004c8b9528ffffff4c899520ffffffeb00eb004c8b9520ffffff4c8b5d804d39da0f9dc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb05e95d0100004c8b9558ffffff4c8b9d20ffffff4d01da4c899510ffffff488bbd68ffffff488bb510ffffffe8a7e5ffff48898508ffffff488b7da0488bb520ffffffe890e5ffff48898500ffffff4c8b9530ffffff4c8b9d00ffffff4d0fafd34c8995f8feffff4c8b9508ffffff4c8b9df8feffff4d01da4c8995f0feffff4c8b95f0feffff4c8b9d40ffffff4d01da4c8995e8feffff4c8b95e8feffff4c8995e0feffff4c8b9558ffffff4c8b9d20ffffff4d01da4c8995d8feffff4c8b95e0feffff4c8b1dda2900004c89d0489949f7fb4989d24c8995d0feffff488bbd68ffffff488bb5d8feffff488b95d0feffffe88ee5ffff488985c8feffff4c8b95e0feffff4c8b1d992900004c89d0489949f7fb4989c24c8995c0feffff4c8b95c0feffff4c899540ffffff48c785b8feffff010000004c8b9520ffffff4c8b9db8feffff4d01da4c8995b0feffff4c8b95b0feffff4c899520ffffffe971feffff4c8b9558ffffff4c8b5d804d01da4c8995a8feffff4c8b95a8feffff4c8995a0feffffeb00eb0048c78598feffff000000004c8b9540ffffff4c8b9d98feffff4d39da0f94c04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9e5000000488bbd68ffffff488bb5a0feffffe8fde3ffff48898588feffff4c8b9588feffff4c8b9d40ffffff4d01da4c899580feffff4c8b9580feffff4c899578feffff4c8b9578feffff4c8b1d902800004c89d0489949f7fb4989d24c899570feffff488bbd68ffffff488bb5a0feffff488b9570feffffe844e4ffff48898568feffff4c8b9578feffff4c8b1d4f2800004c89d0489949f7fb4989c24c899560feffff4c8b9560feffff4c899540ffffff48c78558feffff010000004c8b95a0feffff4c8b9d58feffff4d01da4c899550feffff4c8b9550feffff4c8995a0feffffe9dbfeffff48c78548feffff010000004c8b9558ffffff4c8b9d48feffff4d01da4c899540feffff4c8b9540feffff4c899558ffffffe966fcffff4c8b55904c8b5d804d01da4c899538feffff488bbd68ffffff488bb538feffffe851e3ffff48898530feffff48c78528feffff010000004c8b9528feffff4c899520feffff488b7db0e855e2ffff48898518feffff488b7da0e845e2ffff48898510feffff4c8b9518feffff4c8b9d10feffff4d39da0f95c04c0fb6d04c899508feffff4c8b9508feffff4983fa017402eb2c48c78500feffff010000004c8b9500feffff49f7da4c8995f8fdffff4c8b95f8fdffff4c899520feffffeb00488bbd68ffffff488bb520feffffe877e2ffff488985f0fdffff488bbd68ffffffe8f7e7ffff488985e8fdffff488b85e8fdffff4881c4180200005dc3554889e54883ec7848897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1248c745e001000000488b45e04883c4785dc3488b7df8e8a0dfffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8e833e0ffff488945b84c8b55b84c8955b0488b7db0e821e1ffff488945a848c745a0000000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55984983fa017402eb1248c7459001000000488b45904883c4785dc348c7458800000000488b45884883c4785dc3554889e54883ec5048897df848c745f0010000004c8b55f04c8955e848c745e0000000004c8b55e04c8955d8eb00eb004c8b55d84c8b5df84d39da0f9dc04c0fb6d04c8955d04c8b55d04983fa017402eb02eb4148c745c8020000004c8b55e84c8b5dc84d0fafd34c8955c04c8b55c04c8955e848c745b8010000004c8b55d84c8b5db84d01da4c8955b04c8b55b04c8955d8eb99488b45e84883c4505dc3554889e54881ecb800000048897df8488b7df8e818e0ffff488945f04c8b55f04c8955e848c745e0000000004c8b55e84c8b5de04d39da0f94c04c0fb6d04c8955d84c8b55d84983fa017402eb1548c745d000000000488b45d04881c4b80000005dc348c745c8010000004c8b55e84c8b5dc84d29da4c8955c0488b7df8488b75c0e8d2dfffff488945b84c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb0048c74598000000004c8b55b04c8b5d984d39da0f94c04c0fb6d04c8955904c8b55904983fa017402eb02eb5748c74588020000004c8b55b04c8b5d884c89d0489949f7fb4989c24c8955804c8b55804c8955b048c78578ffffff010000004c8b55a04c8b9d78ffffff4d01da4c899570ffffff4c8b9570ffffff4c8955a0e97bffffff48c78568ffffff010000004c8b55e84c8b9d68ffffff4d29da4c899560ffffff48c78558ffffff1e0000004c8b9560ffffff4c8b9d58ffffff4d0fafd34c899550ffffff4c8b9550ffffff4c8b5da04d01da4c899548ffffff488b8548ffffff4881c4b80000005dc3554889e54883ec7848897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e85ddeffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8488b75d8e841deffff488945b848c745b01e0000004c8b55f04c8b5db04c89d0489949f7fb4989d24c8955a8488b7da8e81dfdffff488945a04c8b55b84c8b5da04c89d0489949f7fb4989c24c89559848c74590020000004c8b55984c8b5d904c89d0489949f7fb4989d24c895588488b45884883c4785dc3554889e54883ec7048897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e868ddffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb064883c4705dc3488b7df8488b75d8e858ddffff488945c048c745b81e0000004c8b55f04c8b5db84c89d0489949f7fb4989d24c8955b0488b7db0e834fcffff488945a84c8b55c04c8b5da84d01da4c8955a04c8b55a04c895598488b7df8488b75d8488b5598e8aeddffff48894590488b45904883c4705dc3554889e54881ec0001000048897df8488b7df8e8b1dcffff488945f04c8b55f04c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b7dd8e80fdcffff488945d04c8b55d04c8955c848c745c0000000004c8b55c04c8955b848c745b0000000004c8b55b04c8955a8eb00eb004c8b55a84c8b5de84d39da0f9dc04c0fb6d04c8955a04c8b55a04983fa017402eb05e9d9000000488b7df8488b75a8e84adcffff4889459848c74590020000004c8b55984c8b5d904d0fafd34c8955884c8b55884c8b5db84d01da4c8955804c8b55804c899578ffffff4c8b9578ffffff4c8b1dd42000004c89d0489949f7fb4989d24c899570ffffff488b7dc8488b75a8488b9570ffffffe88edcffff48898568ffffff4c8b9578ffffff4c8b1d992000004c89d0489949f7fb4989c24c899560ffffff4c8b9560ffffff4c8955b848c78558ffffff010000004c8b55a84c8b9d58ffffff4d01da4c899550ffffff4c8b9550ffffff4c8955a8e9fefeffff488b7dc8488b75e8e8f2dbffff48898548ffffff48c78540ffffff000000004c8b55b84c8b9d40ffffff4d39da0f95c04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb51488b7dc8488b75e8488b55b8e8d1dbffff48898530ffffff48c78528ffffff010000004c8b55e84c8b9d28ffffff4d01da4c899520ffffff488b7dc8488bb520ffffffe86ddbffff48898518ffffffeb0048c78510ffffff01000000488b7dc8488bb510ffffffe81cdbffff48898508ffffff488b7dc8e8bedbffff48898500ffffff488b8500ffffff4881c4000100005dc3554889e54883ec2048897df8488975f0488b7df0e82ed9ffff488945e8488b7df8488b75e8e8aeeeffff488945e0488b45e04883c4205dc3554889e54881ec0802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4080200005dc3488b7df8e8d9d7ffff488945b848c745b0000000004c8b55b84c8b5db04d39da0f94c04c0fb6d04c8955a84c8b55a84983fa017405e921020000488b7df8e818d8ffff488945a04c8b55a04c89559848c74590000000004c8b55f04c8b5d904d39da0f9fc04c0fb6d04c8955884c8b155c1e00004c8b5df04c89d0489949f7fb4989c24c8955804c8b55984c8b5d804d39da0f9dc04c0fb6d04c899578ffffff4c8b55884c8b9d78ffffff4d21da4c899570ffffff4c8b150c1e00004c8b5df04c89d0489949f7fb4989c24c899568ffffff4c8b55984c8b9d68ffffff4d39da0f9ec04c0fb6d04c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d21da4c899558ffffff4c8b9558ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899550ffffff488bbd50ffffffe85cd7ffff48898548ffffff488b8548ffffff4881c4080200005dc348c78540ffffff000000004c8b55f04c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b15571d00004c8b5df04c89d0489949f7fb4989c24c899530ffffff4c8b55984c8b9d30ffffff4d39da0f9ec04c0fb6d04c899528ffffff4c8b9538ffffff4c8b9d28ffffff4d21da4c899520ffffff4c8b15fe1c00004c8b5df04c89d0489949f7fb4989c24c899518ffffff4c8b55984c8b9d18ffffff4d39da0f9dc04c0fb6d04c899510ffffff4c8b9520ffffff4c8b9d10ffffff4d21da4c899508ffffff4c8b9508ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899500ffffff488bbd00ffffffe84ed6ffff488985f8feffff488b85f8feffff4881c4080200005dc3eb00488b7df8e8efdcffff488985f0feffff4c8b95f0feffff4c8995e8feffff488bbde8feffffe83cd7ffff488985e0feffff4c8b95e0feffff4c8995d8feffff48c785d0feffff010000004c8b95d8feffff4c8b9dd0feffff4d01da4c8995c8feffff488bbdc8feffffe882d6ffff488985c0feffff4c8b95c0feffff4c8995b8feffff48c785b0feffff000000004c8b95b0feffff4c8995a8feffff48c785a0feffff000000004c8b95a0feffff4c899598feffffeb00eb004c8b9598feffff4c8b9dd8feffff4d39da0f9dc04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9fb000000488bbde8feffff488bb598feffffe890d6ffff48898588feffff4c8b9588feffff4c8b5df04d0fafd34c899580feffff4c8b9580feffff4c8b9da8feffff4d01da4c899578feffff4c8b9578feffff4c899570feffff4c8b9570feffff4c8b1d0d1b00004c89d0489949f7fb4989d24c899568feffff488bbdb8feffff488bb598feffff488b9568feffffe8c1d6ffff48898560feffff4c8b9570feffff4c8b1dcc1a00004c89d0489949f7fb4989c24c899558feffff4c8b9558feffff4c8995a8feffff48c78550feffff010000004c8b9598feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899598feffffe9d0feffff488bbdb8feffff488bb5d8feffffe816d6ffff48898540feffff48c78538feffff000000004c8b95a8feffff4c8b9d38feffff4d39da0f95c04c0fb6d04c899530feffff4c8b9530feffff4983fa017402eb60488bbdb8feffff488bb5d8feffff488b95a8feffffe8e9d5ffff48898528feffff48c78520feffff010000004c8b95d8feffff4c8b9d20feffff4d01da4c899518feffff488bbdb8feffff488bb518feffffe87fd5ffff48898510feffffeb00488bbde8feffffe897d4ffff48898508feffff488bbdb8feffff488bb508feffffe823d5ffff48898500feffff488bbdb8feffffe8a3daffff488985f8fdffff488b85f8fdffff4881c4080200005dc3554889e54881ec3801000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb3b48c745d8010000004c8b55d84c89154319000048c745d0000000004c8b55d04c89153819000048c745c800000000488b45c84881c4380100005dc3488b7df8e81cd2ffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb68488b7df8e85ed2ffff488945a84c8b55a84c8955a04c8b55a04c8b5df04c89d0489949f7fb4989d24c8955984c8b55984c8915b51800004c8b55a04c8b5df04c89d0489949f7fb4989c24c895590488b7d90e845d2ffff48894588488b45884881c4380100005dc3488b7df8e8eed8ffff488945804c8b55804c899578ffffff488bbd78ffffffe841d3ffff48898570ffffff4c8b9570ffffff4c899568ffffff488bbd68ffffffe8aad2ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9550ffffff4c899548ffffff4c8b9568ffffff4c899540ffffffeb00eb0048c78538ffffff000000004c8b9540ffffff4c8b9d38ffffff4d39da0f9ec04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb05e9f800000048c78528ffffff010000004c8b9540ffffff4c8b9d28ffffff4d29da4c899520ffffff4c8b9520ffffff4c899540ffffff4c8b9548ffffff4c8b1d5a1700004d0fafd34c899518ffffff488bbd78ffffff488bb540ffffffe86ed2ffff48898510ffffff4c8b9518ffffff4c8b9d10ffffff4d01da4c899508ffffff4c8b9508ffffff4c899500ffffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989c24c8995f8feffff488bbd58ffffff488bb540ffffff488b95f8feffffe8b8d2ffff488985f0feffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989d24c8995e8feffff4c8b95e8feffff4c899548ffffffe9c8feffff488bbd58ffffff488bb568ffffffe841d2ffff488985e0feffff488bbd78ffffffe85bd1ffff488985d8feffff488bbd58ffffff488bb5d8feffffe8e7d1ffff488985d0feffff4c8b9548ffffff4c891574160000488bbd58ffffffe859d7ffff488985c8feffff488b85c8feffff4881c4380100005dc3554889e54881ec2001000048897df8488b7df8488b47f84883e801488945f04c8b55f04c8955e848c745e0000000004c8b55e04c8955d848c745d0000000004c8b55d04c8955c848c745c0000000004c8b55e84c8b5dc04d39da0f9fc04c0fb6d04c8955b848c745b000000000488b7df8488b75b0480fb60437488945a848c745a02d0000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55b84c8b5d984d21da4c8955904c8b55904983fa017402eb2248c74588010000004c8b55884c8955c848c74580010000004c8b55804c8955d8eb0048c78578ffffff000000004c8b9578ffffff4c899570ffffffeb00eb004c8b55d84c8b5de84d39da0f9dc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb05e950010000488b7df8488b75d8480fb6043748898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff300000004c8b9558ffffff4c8b9d50ffffff4d39da0f9cc04c0fb6d04c899548ffffff48c78540ffffff390000004c8b9558ffffff4c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9548ffffff4c8b9d38ffffff4d09da4c899530ffffff4c8b9530ffffff4983fa017402eb05e9ae00000048c78528ffffff0a000000488bbd70ffffff488bb528ffffffe80ff5ffff48898520ffffff4c8b9520ffffff4c899570ffffff48c78518ffffff300000004c8b9558ffffff4c8b9d18ffffff4d29da4c899510ffffff488bbd70ffffff488bb510ffffffe88cf4ffff48898508ffffff4c8b9508ffffff4c899570ffffff48c78500ffffff010000004c8b55d84c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c8955d8e981feffff48c785f0feffff000000004c8b55c84c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb23488bbd70ffffffe891e5ffff488985e0feffff4c8b95e0feffff4c899570ffffffeb00488b8570ffffff4881c4200100005dc3554889e54881ec1802000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb5148c745e002000000488b7de0e800000000488945d84c8b55d84c8955d048c745c80000000048c745c030000000488b7dd0488b75c8488b55c088143748c745b800000000488b45d04881c4180200005dc348c745b0000000004c8b55b04c8955a8488b7df8e845d3ffff488945a04c8b55a04c895598488b7d98e875cdffff4889459048c74588000000004c8b55904c8b5d884d39da0f9cc04c0fb6d04c8955804c8b55804983fa017402eb4648c78578ffffff010000004c8b9578ffffff4c8955a8488b7df8e865e4ffff48898570ffffff488bbd70ffffffe8d0d2ffff48898568ffffff4c8b9568ffffff4c895598eb00488b7d98e821cdffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff0a0000004c8b9558ffffff4c8b9d50ffffff4d0fafd34c899548ffffff48c78540ffffff040000004c8b9548ffffff4c8b9d40ffffff4d01da4c899538ffffff4c8b9538ffffff4c899530ffffff488bbd30ffffffe80000000048898528ffffff4c8b9528ffffff4c899520ffffff48c78518ffffff010000004c8b9530ffffff4c8b9d18ffffff4d29da4c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff00000000488bbd20ffffff488bb508ffffff488b9500ffffff88143748c785f8feffff00000000488b7d98e83ed2ffff488985f0feffff4c8b95f0feffff4c8995e8feffffeb00eb0048c785e0feffff000000004c8b95e8feffff4c8b9de0feffff4d39da0f94c04c0fb6d04c8995d8feffff4c8b95d8feffff4983fa017402eb05e9bd00000048c785d0feffff0a000000488bbde8feffff488bb5d0feffffe83df7ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b9508ffffff4c8b9db8feffff4d29da4c8995b0feffff4c8b95b0feffff4c899508ffffff48c785a8feffff300000004c8b15801000004c8b9da8feffff4d01da4c8995a0feffff488bbd20ffffff488bb508ffffff488b95a0feffff88143748c78598feffff000000004c8b95c0feffff4c8995e8feffffe903ffffff48c78590feffff000000004c8b55a84c8b9d90feffff4d39da0f95c04c0fb6d04c899588feffff4c8b9588feffff4983fa017402eb6148c78580feffff010000004c8b9508ffffff4c8b9d80feffff4d29da4c899578feffff4c8b9578feffff4c899508ffffff48c78570feffff2d000000488bbd20ffffff488bb508ffffff488b9570feffff88143748c78568feffff00000000eb0048c78560feffff010000004c8b9530ffffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c8b9d08ffffff4d29da4c899550feffff4c8b9550feffff4c899548feffff48c78540feffff010000004c8b9548feffff4c8b9d40feffff4d01da4c899538feffff488bbd38feffffe80000000048898530feffff4c8b9530feffff4c899528feffff48c78520feffff000000004c8b9520feffff4c899518feffffeb00eb004c8b9518feffff4c8b9d48feffff4d39da0f9dc04c0fb6d04c899510feffff4c8b9510feffff4983fa017402eb05e98b0000004c8b9508ffffff4c8b9d18feffff4d01da4c899508feffff488bbd20ffffff488bb508feffff480fb6043748898500feffff488bbd28feffff488bb518feffff488b9500feffff88143748c785f8fdffff0000000048c785f0fdffff010000004c8b9518feffff4c8b9df0fdffff4d01da4c8995e8fdffff4c8b95e8fdffff4c899518feffffe940ffffff488b8528feffff4881c4180200005dc3554889e54881ec5802000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb404c8d15260d00004c8955d8488b7dd8e841c6ffff488945d048c745c8010000004c8b55c84c8915ba0d000048c745c000000000488b45c04881c4580200005dc348c745b8000000004c8b55f84c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb2848c745a8000000004c8b55a84c8915700d000048c745a000000000488b45a04881c4580200005dc3488b7df8e8c5cdffff488945984c8b55984c895590488b7df0e8b0cdffff488945884c8b55884c895580488b7d90488b7580e89dd2ffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b9570ffffff4c8b9d68ffffff4d39da0f9cc04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb264c8b55f84c8915cb0c000048c78558ffffff00000000488b8558ffffff4881c4580200005dc3488b7d90e85fc7ffff48898550ffffff4c8b9550ffffff4c899548ffffff488b7d80e841c7ffff48898540ffffff4c8b9540ffffff4c899538ffffff48c78530ffffff000000004c8b9570ffffff4c8b9d30ffffff4d39da0f94c04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017405e9af00000048c78520ffffff010000004c8b9520ffffff4c899518ffffff4c8b9548ffffff4c8b9d38ffffff4d39da0f95c04c0fb6d04c899510ffffff4c8b9510ffffff4983fa017402eb2c48c78508ffffff010000004c8b9508ffffff49f7da4c899500ffffff4c8b9500ffffff4c899518ffffffeb0048c785f8feffff000000004c8b95f8feffff4c8915ac0b0000488bbd18ffffffe850c5ffff488985f0feffff488b85f0feffff4881c4580200005dc3488b7d90e861c6ffff488985e8feffff4c8b95e8feffff4c8995e0feffff48c785d8feffff010000004c8b95e0feffff4c8b9dd8feffff4d01da4c8995d0feffff488bbdd0feffffe8a7c5ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b95e0feffff4c8b9db8feffff4d01da4c8995b0feffff488bbdc0feffff488bb5b0feffffe87cc6ffff488985a8feffff48c785a0feffff00000000488bbda0feffffe815c8ffff48898598feffff4c8b9598feffff4c899590feffff488b7d90e866e5ffff48898588feffff4c8b9588feffff4c899580feffff4c8b9580feffff4c899578feffffeb00eb0048c78570feffff000000004c8b9578feffff4c8b9d70feffff4d39da0f9ec04c0fb6d04c899568feffff4c8b9568feffff4983fa017402eb05e99301000048c78560feffff010000004c8b9578feffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c899578feffff488bbd90feffffe82be8ffff48898550feffff4c8b9550feffff4c899590feffff488b7d90488bb578feffffe832e6ffff48898548feffff48c78540feffff000000004c8b9548feffff4c8b9d40feffff4d39da0f95c04c0fb6d04c899538feffff4c8b9538feffff4983fa017402eb5b488bbd90feffffe893caffff48898530feffff48c78528feffff01000000488bbd30feffff488bb528feffffe8e6e9ffff48898520feffff488bbd20feffffe8ddc9ffff48898518feffff4c8b9518feffff4c899590feffffeb00488bbd90feffff488b7580e8bcceffff48898510feffff48c78508feffff000000004c8b9510feffff4c8b9d08feffff4d39da0f9dc04c0fb6d04c899500feffff4c8b9500feffff4983fa017402eb41488bbd90feffff488b7580e8b7d5ffff488985f8fdffff4c8b95f8fdffff4c899590feffff488bbdc0feffff488bb578feffffe804e6ffff488985f0fdffffeb00e92dfeffff48c785e8fdffff010000004c8b95e0feffff4c8b9de8fdffff4d01da4c8995e0fdffff488bbdc0feffff488bb5e0fdffffe812c4ffff488985d8fdffff4c8b9548ffffff4c8b9d38ffffff4d0fafd34c8995d0fdffff488bbdc0feffff488bb5d0fdffffe8b2c3ffff488985c8fdffff488bbdc0feffffe832c9ffff488985c0fdffff4c8b95c0fdffff4c8995b8fdffff488bbd90feffff488bb548ffffffe877c3ffff488985b0fdffff488bbd90feffffe8f7c8ffff488985a8fdffff4c8b95a8fdffff4c8915f1070000488b85b8fdffff4881c4580200005dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e89af9ffff488945e84c8b55e84c8955e0488b45e04883c4205dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e867f9ffff488945e84c8b55e84c8955e0488b057e0700004883c4205dc3554889e54883ec3848897df84c8b55f84c8b1d510700004d39da0f9dc04c0fb6d04c8955f04c8b55f84c8b1d300700004d39da0f9ec04c0fb6d04c8955e84c8b55f04c8b5de84d21da4c8955e04c8b55e04983fa017402eb17488b7df8e8c6c0ffff488945d8488b45d84883c4385dc3488b7df8e841c4ffff488945d0488b7dd0e8e3c7ffff488945c8488b45c84883c4385dc3554889e54883ec4048897df848c745f0000000004c8b55f84c8b5df04d39da0f9dc04c0fb6d04c8955e84c8b55f84c8b1d970600004d39da0f9ec04c0fb6d04c8955e04c8b55e84c8b5de04d21da4c8955d84c8b55d84983fa017402eb17488b7df8e82dc0ffff488945d0488b45d04883c4405dc3488b7df8e8a8c3ffff488945c8488b7dc8e84ac7ffff488945c0488b45c04883c4405dc3554889e54881ecc801000048897df848c745f0000000004c8b55f04c891521060000488b7df8e893c6ffff488945e84c8b55e84c8955e0488b7de0e8ecc0ffff488945d84c8b55d84c8955d048c745c8030000004c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb404c8d152e0500004c8955b8488b7db8e829beffff488945b048c745a8010000004c8b55a84c8915a205000048c745a000000000488b45a04881c4c80100005dc348c7459800000000488b7de0488b7598e88ac0ffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff010000004c8b55d04c8b9d60ffffff4d39da0f9fc04c0fb6d04c899558ffffff4c8b9558ffffff4983fa017402eb3248c78550ffffff01000000488b7de0488bb550ffffffe801c0ffff48898548ffffff4c8b9548ffffff4c899578ffffffeb0048c78540ffffff020000004c8b55d04c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb3248c78530ffffff02000000488b7de0488bb530ffffffe899bfffff48898528ffffff4c8b9528ffffff4c899568ffffffeb00488b7de0e827bfffff48898520ffffff48c78518ffffff000000004c8b9520ffffff4c8b9d18ffffff4d39da0f9cc04c0fb6d04c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff080000004c8b9568ffffff4c8b9d00ffffff4d39da0f9fc04c0fb6d04c8995f8feffff4c8b95f8feffff4983fa017402eb554c8d15500300004c8995f0feffff488bbdf0feffffe845bcffff488985e8feffff48c785e0feffff010000004c8b95e0feffff4c8915b503000048c785d8feffff00000000488b85d8feffff4881c4c80100005dc348c785d0feffff080000004c8b9568ffffff4c8b9dd0feffff4d39da0f94c04c0fb6d04c8995c8feffff4c8b9508ffffff4983fa000f94c04c0fb6d04c8995c0feffff48c785b8feffff000000004c8b9578ffffff4c8b9db8feffff4d39da0f95c04c0fb6d04c8995b0feffff4c8b95c0feffff4c8b9db0feffff4d09da4c8995a8feffff48c785a0feffff000000004c8b55884c8b9da0feffff4d39da0f95c04c0fb6d04c899598feffff4c8b95a8feffff4c8b9d98feffff4d09da4c899590feffff4c8b95c8feffff4c8b9d90feffff4d21da4c899588feffff4c8b9588feffff4983fa017402eb554c8d15100200004c899580feffff488bbd80feffffe805bbffff48898578feffff48c78570feffff010000004c8b9570feffff4c89157502000048c78568feffff00000000488b8568feffff4881c4c80100005dc34c8b9578ffffff4c8b1d2c0200004d0fafd34c899560feffff4c8b55884c8b9d60feffff4d01da4c899558feffff4c8b9568ffffff4c8b1d060200004d0fafd34c899550feffff4c8b9558feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899540feffff4c8b9508ffffff4983fa017402eb214c8b9540feffff49f7da4c899538feffff4c8b9538feffff4c899540feffffeb00488b8540feffff4881c4c80100005dc3554889e54883ec6048897df8488b7df8e855fbffff488945f04c8b55f04c8955e848c745e0000000004c8b157d0100004c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d000000000488b45d04883c4605dc348c745c8000000004c8b55e84c8b5dc84d39da0f9cc04c0fb6d04c8955c04c8b55c04983fa017402eb3d4c8d15b40000004c8955b8488b7db8e87fb9ffff488945b048c745a8010000004c8b55a84c8915f800000048c745a000000000488b45a04883c4605dc3488b45e84883c4605dc30000000000000000000000000100000000000000000000000000000012000000000000006469766973696f6e206279207a65726f0a000000000000002300000000000000696e7465676572206f7574206f66206d616368696e652d776f72642072616e67650a0000000000002c00000000000000696e7465676572206f7574206f6620756e7369676e6564206d616368696e652d776f72642072616e67650a000000000000000000000000000800000000000000100000000000000000000040000000000000000000000010ffffffffffffff3f000000000000000000000000000000000000000000000000" + h : ., mut = "554889e54883ec5048897df848c745f00100000048c745e802000000488b7df8488b47f84883e801488945e0488b7df0488b75e8488b55f8488b4de04889f84889f74889d64889ca0f05488945d848c745d03c00000048c745c80100000048c745c00000000048c745b800000000488b7dd0488b75c8488b55c0488b4db84889" + h = h + "f84889f74889d64889ca0f05488945b0488b45b04883c4505dc3554889e54883ec3848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989d24c8955e848c745e0000000004c8b55e84c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d001000000488b45d04883c4385d" + h = h + "c348c745c800000000488b45c84883c4385dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04c89d0489949f7fb4989c24c8955e8488b45e84883c4185dc3554889e54883ec1848897df848c745f0020000004c8b55f84c8b5df04d0fafd34c8955e8488b45e84883c4185dc3554889e54883ec284889" + h = h + "7df84c8d153f4500004c8955f04c8b55f04c8955e848c745e0010000004c8b55f84c8b5de04d29da4c8955d84c8b55d84c8955e8488b45e84883c4285dc3554889e54883ec2848897df848c745f0000000004c8b55f04c8955e84c8b55f84c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b45d84883c4" + h = h + "285dc3554889e54883ec2848897df848c745f0080000004c8b55f84c8b5df04d0fafd34c8955e84c8b153a4500004c8b5de84d01da4c8955e0488b7de0e800000000488945d8488b45d84883c4285dc3554889e54883ec1048897df8488b7df8488b35f1440000488b0437488945f0488b45f04883c4105dc3554889e54883ec" + h = h + "1048897df8488b7df8488b35d0440000488b0437488945f0488b45f04883c4105dc3554889e54883ec3048897df8488975f048c745e8080000004c8b55f04c8b5de84d0fafd34c8955e04c8b15974400004c8b5de04d01da4c8955d8488b7df8488b75d8488b0437488945d0488b45d04883c4305dc3554889e54883ec184889" + h = h + "7df8488975f0488b7df8488b3547440000488b55f048891437488b45e84883c4185dc3554889e54883ec1848897df8488975f0488b7df8488b3522440000488b55f048891437488b45e84883c4185dc3554889e54883ec3848897df8488975f0488955e848c745e0080000004c8b55f04c8b5de04d0fafd34c8955d84c8b15e5" + h = h + "4300004c8b5dd84d01da4c8955d0488b7df8488b75d0488b55e848891437488b45c84883c4385dc3554889e54881ec8800000048897df8488b7df8e8b9feffff488945f04c8b55f04c8955e8eb00eb0048c745e0000000004c8b55e84c8b5de04d39da0f9ec04c0fb6d04c8955d84c8b55d84983fa017402eb02eb7848c745d0" + h = h + "010000004c8b55e84c8b5dd04d29da4c8955c8488b7df8488b75c8e882feffff488945c048c745b8000000004c8b55c04c8b5db84d39da0f95c04c0fb6d04c8955b04c8b55b04983fa017402eb02eb2448c745a8010000004c8b55e84c8b5da84d29da4c8955a04c8b55a04c8955e8e95affffff488b7df8488b75e8e8a2feff" + h = h + "ff4889459848c74590000000004c8b55e84c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb1e48c7458000000000488b7df8488b7580e832feffff48898578ffffffeb00488b45f84881c4880000005dc3554889e54881ec3801000048897df849ba00000000000000804c8955f04c8b55f049f7da4c89" + h = h + "55e84c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017405e98c00000048c745d803000000488b7dd8e8cbfcffff488945d04c8b55d04c8955c848c745c00200000048c745b808000000488b7dc8488b75c0488b55b8e8ebfdffff488945b048c745a8010000004c8b55a849f7da4c8955a0488b7dc8" + h = h + "488b75a0e86dfdffff4889459848c7459003000000488b7dc8488b7590e881fdffff48894588488b45c84881c4380100005dc348c7458003000000488b7d80e83ffcffff48898578ffffff4c8b9578ffffff4c899570ffffff48c78568ffffff000000004c8b55f84c8b9d68ffffff4d39da0f94c04c0fb6d04c899560ffffff" + h = h + "4c8b9560ffffff4983fa017402eb10488b8570ffffff4881c4380100005dc34c8b55f84c899558ffffff48c78550ffffff010000004c8b9550ffffff4c899548ffffff48c78540ffffff000000004c8b9558ffffff4c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb4b48c78530" + h = h + "ffffff010000004c8b9530ffffff49f7da4c899528ffffff4c8b9528ffffff4c899548ffffff4c8b9558ffffff49f7da4c899520ffffff4c8b9520ffffff4c899558ffffffeb0048c78518ffffff000000004c8b9518ffffff4c899510ffffffeb00eb0048c78508ffffff000000004c8b9558ffffff4c8b9d08ffffff4d39da" + h = h + "0f94c04c0fb6d04c899500ffffff4c8b9500ffffff4983fa017402eb05e9a50000004c8b9558ffffff4c8b1d404000004c89d0489949f7fb4989d24c8995f8feffff488bbd70ffffff488bb510ffffff488b95f8feffffe8f4fbffff488985f0feffff4c8b9558ffffff4c8b1dff3f00004c89d0489949f7fb4989c24c8995e8" + h = h + "feffff4c8b95e8feffff4c899558ffffff48c785e0feffff010000004c8b9510ffffff4c8b9de0feffff4d01da4c8995d8feffff4c8b95d8feffff4c899510ffffffe91bffffff488bbd70ffffff488bb548ffffffe81cfbffff488985d0feffff488bbd70ffffff488bb510ffffffe82ffbffff488985c8feffff488b8570ff" + h = h + "ffff4881c4380100005dc3554889e54883ec3848897df8488b7df8e87af8ffff488945f048c745e8000000004c8b55f04c8b5de84d39da0f95c04c0fb6d04c8955e04c8b55e04983fa017402eb17488b7df8e81ff9ffff488945d8488b45d84883c4385dc3488b7df8e8a5f8ffff488945d0488b7dd0e85ffcffff488945c848" + h = h + "8b45c84883c4385dc3554889e54881ec5001000048897df8488b7df8e807fbffff488945f0488b7df8e8cbf9ffff488945e84c8b55e84c8955e048c745d8000000004c8b55e04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb1548c745c800000000488b45c84881c4500100005dc348c745c0030000" + h = h + "004c8b55e04c8b5dc04d39da0f9fc04c0fb6d04c8955b84c8b55b84983fa017402eb1a488b7df8e892f8ffff488945b0488b45b04881c4500100005dc348c745a800000000488b7df8488b75a8e850f9ffff488945a04c8b55a04c89559848c74590000000004c8b55904c89558848c74580000000004c8b55804c899578ffff" + h = h + "ff48c78570ffffff010000004c8b55e04c8b9d70ffffff4d39da0f9fc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb2f48c78560ffffff01000000488b7df8488bb560ffffffe8d0f8ffff48898558ffffff4c8b9558ffffff4c895588eb0048c78550ffffff020000004c8b55e04c8b9d50ffffff4d39da0f" + h = h + "9fc04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb3248c78540ffffff02000000488b7df8488bb540ffffffe86bf8ffff48898538ffffff4c8b9538ffffff4c899578ffffffeb0048c78530ffffff030000004c8b9578ffffff4c8b9d30ffffff4d39da0f9fc04c0fb6d04c899528ffffff4c8b9528ffffff49" + h = h + "83fa017402eb20488b7df8e82ef7ffff48898520ffffff488b8520ffffff4881c4500100005dc34c8b55884c8b1dbe3c00004d0fafd34c899518ffffff4c8b55984c8b9d18ffffff4d01da4c899510ffffff4c8b9578ffffff4c8b1d983c00004d0fafd34c899508ffffff4c8b9510ffffff4c8b9d08ffffff4d01da4c899500" + h = h + "ffffff4c8b9500ffffff4c8995f8feffff488b7df8e836f7ffff488985f0feffff48c785e8feffff000000004c8b95f0feffff4c8b9de8feffff4d39da0f9cc04c0fb6d04c8995e0feffff4c8b95e0feffff4983fa017402eb214c8b95f8feffff49f7da4c8995d8feffff4c8b95d8feffff4c8995f8feffffeb004c8b95f8fe" + h = h + "ffff4c8b1dff3b00004d39da0f9cc04c0fb6d04c8995d0feffff4c8b95f8feffff4c8b1dd83b00004d39da0f9fc04c0fb6d04c8995c8feffff4c8b95d0feffff4c8b9dc8feffff4d09da4c8995c0feffff4c8b95c0feffff4983fa017402eb20488b7df8e8d5f5ffff488985b8feffff488b85b8feffff4881c4500100005dc3" + h = h + "488bbdf8feffffe83cf5ffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54883ec7848897df8488b7df8e841f6ffff488945f04c8b55f04c8955e8488b7de8e8b6f5ffff488945e04c8b55e04c8955d8488b7df8e8eef5ffff488945d0488b7dd8488b75d0e883f6ffff488945c8488b7dd8488b75e8e8" + h = h + "9ff6ffff488945c048c745b8000000004c8b55b84c8955b0eb00eb004c8b55b04c8b5de84d39da0f9dc04c0fb6d04c8955a84c8b55a84983fa017402eb02eb47488b7df8488b75b0e8d5f5ffff488945a0488b7dd8488b75b0488b55a0e86ef6ffff4889459848c74590010000004c8b55b04c8b5d904d01da4c8955884c8b55" + h = h + "884c8955b0eb93488b45d84883c4785dc3554889e54881ecd000000048897df8488975f0488b7df8e84cf5ffff488945e84c8b55e84c8955e0488b7df0e837f5ffff488945d84c8b55d84c8955d04c8b55e04c8b5dd04d39da0f9cc04c0fb6d04c8955c84c8b55c84983fa017402eb2048c745c0010000004c8b55c049f7da4c" + h = h + "8955b8488b45b84881c4d00000005dc34c8b55e04c8b5dd04d39da0f9fc04c0fb6d04c8955b04c8b55b04983fa017402eb1548c745a801000000488b45a84881c4d00000005dc34c8b55e04c8955a0eb00eb0048c74598000000004c8b55a04c8b5d984d39da0f9ec04c0fb6d04c8955904c8b55904983fa017402eb05e90b01" + h = h + "000048c74588010000004c8b55a04c8b5d884d29da4c8955804c8b55804c8955a0488b7df8488b75a0e874f4ffff48898578ffffff4c8b9578ffffff4c899570ffffff488b7df0488b75a0e852f4ffff48898568ffffff4c8b9568ffffff4c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d39da0f9cc04c0fb6d04c8995" + h = h + "58ffffff4c8b9558ffffff4983fa017402eb2c48c78550ffffff010000004c8b9550ffffff49f7da4c899548ffffff488b8548ffffff4881c4d00000005dc34c8b9570ffffff4c8b9d60ffffff4d39da0f9fc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb1b48c78538ffffff01000000488b8538ffffff48" + h = h + "81c4d00000005dc3e9c4feffff48c78530ffffff00000000488b8530ffffff4881c4d00000005dc3554889e54881ecc800000048897df8488975f0488b7df8e8c7f8ffff488945e84c8b55e84c8955e0488b7df0e8b2f8ffff488945d84c8b55d84c8955d0488b7de0e8e2f2ffff488945c84c8b55c84c8955c0488b7dd0e8cd" + h = h + "f2ffff488945b84c8b55b84c8955b04c8b55c04c8b5db04d39da0f9cc04c0fb6d04c8955a84c8b55a84983fa017402eb2048c745a0010000004c8b55a049f7da4c895598488b45984881c4c80000005dc34c8b55c04c8b5db04d39da0f9fc04c0fb6d04c8955904c8b55904983fa017402eb1548c7458801000000488b458848" + h = h + "81c4c80000005dc348c74580000000004c8b55c04c8b5d804d39da0f94c04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb1b48c78570ffffff00000000488b8570ffffff4881c4c80000005dc3488b7de0488b75d0e8b1fcffff48898568ffffff4c8b9568ffffff4c899560ffffff48c78558ffffff00000000" + h = h + "4c8b55c04c8b9d58ffffff4d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb344c8b9560ffffff49f7da4c899548ffffff488bbd48ffffffe880f0ffff48898540ffffff488b8540ffffff4881c4c80000005dc3488bbd60ffffffe85df0ffff48898538ffffff488b8538ffffff4881c4c8000000" + h = h + "5dc3554889e54881ec5001000048897df8488975f0488b7df8e85bf1ffff488945e84c8b55e84c8955e0488b7df0e846f1ffff488945d84c8b55d84c8955d04c8b55e04c8955c84c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb0a4c8b55d04c8955c8eb0048c745b8010000004c8b55c84c" + h = h + "8b5db84d01da4c8955b0488b7db0e870f0ffff488945a84c8b55a84c8955a048c74598000000004c8b55984c89559048c74588000000004c8b55884c895580eb00eb004c8b55804c8b5dc84d39da0f9dc04c0fb6d04c899578ffffff4c8b9578ffffff4983fa017402eb05e99b01000048c78570ffffff000000004c8b9570ff" + h = h + "ffff4c899568ffffff48c78560ffffff000000004c8b9560ffffff4c899558ffffff4c8b55804c8b5de04d39da0f9cc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb24488b7df8488b7580e84bf0ffff48898548ffffff4c8b9548ffffff4c899568ffffffeb004c8b55804c8b5dd04d39da0f9cc04c0fb6d0" + h = h + "4c899540ffffff4c8b9540ffffff4983fa017402eb24488b7df0488b7580e8ffefffff48898538ffffff4c8b9538ffffff4c899558ffffffeb004c8b9568ffffff4c8b9d58ffffff4d01da4c899530ffffff4c8b9530ffffff4c8b5d904d01da4c899528ffffff4c8b9528ffffff4c899520ffffff4c8b9520ffffff4c8b1d6d" + h = h + "3400004c89d0489949f7fb4989d24c899518ffffff488b7da0488b7580488b9518ffffffe827f0ffff48898510ffffff4c8b9520ffffff4c8b1d323400004c89d0489949f7fb4989c24c899508ffffff4c8b9508ffffff4c89559048c78500ffffff010000004c8b55804c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8fe" + h = h + "ffff4c895580e936feffff48c785f0feffff000000004c8b55904c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb45488b7da0488b75c8488b5590e87eefffff488985e0feffff48c785d8feffff010000004c8b55c84c8b9dd8feffff4d01da4c8995d0feffff4c8b95d0feffff" + h = h + "4c8955c8eb00488b7da0488b75c8e810efffff488985c8feffff48c785c0feffff01000000488b7da0488bb5c0feffffe8c1eeffff488985b8feffff488b7da0e863efffff488985b0feffff488b85b0feffff4881c4500100005dc3554889e54881ec1001000048897df8488975f0488b7df8e801eeffff488945e84c8b55e8" + h = h + "4c8955e0488b7df0e8ecedffff488945d84c8b55d84c8955d0488b7de0e861edffff488945c84c8b55c84c8955c048c745b8000000004c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb004c8b55a04c8b5de04d39da0f9dc04c0fb6d04c8955984c8b55984983fa017402eb05e98a010000488b7df8488b75" + h = h + "a0e89cedffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff4c8b55a04c8b5dd04d39da0f9cc04c0fb6d04c899570ffffff4c8b9570ffffff4983fa017402eb24488b7df0488b75a0e848edffff48898568ffffff4c8b9568ffffff4c899578ffffffeb004c8b55884c8b9d78ffffff4d29da4c" + h = h + "899560ffffff4c8b9560ffffff4c8b5db04d29da4c899558ffffff4c8b9558ffffff4c899550ffffff48c78548ffffff000000004c8b9550ffffff4c8b9d48ffffff4d39da0f9cc04c0fb6d04c899540ffffff4c8b9540ffffff4983fa017402eb3e4c8b9550ffffff4c8b1d803100004d01da4c899538ffffff4c8b9538ffff" + h = h + "ff4c899550ffffff48c78530ffffff010000004c8b9530ffffff4c8955b0eb1848c78528ffffff000000004c8b9528ffffff4c8955b0eb00488b7dc0488b75a0488b9550ffffffe804edffff48898520ffffff48c78518ffffff010000004c8b55a04c8b9d18ffffff4d01da4c899510ffffff4c8b9510ffffff4c8955a0e94d" + h = h + "feffff488b7dc0488b75e0e893ecffff48898508ffffff48c78500ffffff01000000488b7dc0488bb500ffffffe844ecffff488985f8feffff488b7dc0e8e6ecffff488985f0feffff488b85f0feffff4881c4100100005dc3554889e54881ec1001000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f" + h = h + "94c04c0fb6d04c8955e04c8b55e04983fa017402eb0d488b45f04881c4100100005dc348c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55d04983fa017402eb0d488b45f84881c4100100005dc3488b7df8e8a8f0ffff488945c84c8b55c84c8955c0488b7df0e893f0ffff488945b84c8b55b8" + h = h + "4c8955b0488b7dc0e8c3eaffff488945a84c8b55a84c8955a0488b7db0e8aeeaffff488945984c8b55984c8955904c8b55a04c8b5d904d39da0f94c04c0fb6d04c8955884c8b55884983fa017402eb56488b7dc0488b75b0e825f9ffff488945804c8b55804c899578ffffff488bbd78ffffff488b75a0e8faeaffff48898570" + h = h + "ffffff488bbd78ffffffe87af0ffff48898568ffffff488b8568ffffff4881c4100100005dc3488b7dc0488b75b0e8def4ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9558ffffff4c8b9d50ffffff4d39da0f94c04c0fb6d04c899548ffffff4c8b9548ffffff4983fa017402eb" + h = h + "1b48c78540ffffff00000000488b8540ffffff4881c4100100005dc348c78538ffffff000000004c8b9558ffffff4c8b9d38ffffff4d39da0f9fc04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb5c488b7dc0488b75b0e87afbffff48898528ffffff4c8b9528ffffff4c899520ffffff488bbd20ffffff488b" + h = h + "75a0e8efe9ffff48898518ffffff488bbd20ffffffe86fefffff48898510ffffff488b8510ffffff4881c4100100005dc3488b7db0488b75c0e81efbffff48898508ffffff4c8b9508ffffff4c899500ffffff488bbd00ffffff488b7590e893e9ffff488985f8feffff488bbd00ffffffe813efffff488985f0feffff488b85" + h = h + "f0feffff4881c4100100005dc3554889e54881ec9000000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1548c745e000000000488b45e04881c4900000005dc3488b7df8e8b6e6ffff488945d848c745d0000000004c8b55d84c8b5dd04d39da0f94c04c0f" + h = h + "b6d04c8955c84c8b55c84983fa017402eb32488b7df8e8f8e6ffff488945c04c8b55c049f7da4c8955b8488b7db8e815e7ffff488945b0488b45b04881c4900000005dc3488b7df8e829e7ffff488945a84c8b55a84c8955a0488b7da0e8c1f1ffff488945984c8b55984c895590488b7da0e8d9e7ffff488945884c8b558849" + h = h + "f7da4c895580488b7d90488b7580e863e8ffff48898578ffffff488b7d90e8e6edffff48898570ffffff488b8570ffffff4881c4900000005dc3554889e54883ec2048897df8488975f0488b7df0e8bafeffff488945e8488b7df8488b75e8e8f5fbffff488945e0488b45e04883c4205dc3554889e54881ec1802000048897d" + h = h + "f8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c04c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4180200005dc3488b7df8e891ecffff488945b84c8b" + h = h + "55b84c8955b0488b7df0e87cecffff488945a84c8b55a84c8955a0488b7db0e8d5e6ffff488945984c8b55984c895590488b7da0e8c0e6ffff488945884c8b55884c8955804c8b55904c8b5d804d01da4c899578ffffff488bbd78ffffffe820e6ffff48898570ffffff4c8b9570ffffff4c899568ffffff48c78560ffffff00" + h = h + "0000004c8b9560ffffff4c899558ffffffeb00eb004c8b9558ffffff4c8b5d904d39da0f9dc04c0fb6d04c899550ffffff4c8b9550ffffff4983fa017402eb05e96803000048c78548ffffff000000004c8b9548ffffff4c899540ffffff488b7db0488bb558ffffffe834e6ffff48898538ffffff4c8b9538ffffff4c899530" + h = h + "ffffff48c78528ffffff000000004c8b9528ffffff4c899520ffffffeb00eb004c8b9520ffffff4c8b5d804d39da0f9dc04c0fb6d04c899518ffffff4c8b9518ffffff4983fa017402eb05e95d0100004c8b9558ffffff4c8b9d20ffffff4d01da4c899510ffffff488bbd68ffffff488bb510ffffffe8a7e5ffff48898508ff" + h = h + "ffff488b7da0488bb520ffffffe890e5ffff48898500ffffff4c8b9530ffffff4c8b9d00ffffff4d0fafd34c8995f8feffff4c8b9508ffffff4c8b9df8feffff4d01da4c8995f0feffff4c8b95f0feffff4c8b9d40ffffff4d01da4c8995e8feffff4c8b95e8feffff4c8995e0feffff4c8b9558ffffff4c8b9d20ffffff4d01" + h = h + "da4c8995d8feffff4c8b95e0feffff4c8b1dda2900004c89d0489949f7fb4989d24c8995d0feffff488bbd68ffffff488bb5d8feffff488b95d0feffffe88ee5ffff488985c8feffff4c8b95e0feffff4c8b1d992900004c89d0489949f7fb4989c24c8995c0feffff4c8b95c0feffff4c899540ffffff48c785b8feffff0100" + h = h + "00004c8b9520ffffff4c8b9db8feffff4d01da4c8995b0feffff4c8b95b0feffff4c899520ffffffe971feffff4c8b9558ffffff4c8b5d804d01da4c8995a8feffff4c8b95a8feffff4c8995a0feffffeb00eb0048c78598feffff000000004c8b9540ffffff4c8b9d98feffff4d39da0f94c04c0fb6d04c899590feffff4c8b" + h = h + "9590feffff4983fa017402eb05e9e5000000488bbd68ffffff488bb5a0feffffe8fde3ffff48898588feffff4c8b9588feffff4c8b9d40ffffff4d01da4c899580feffff4c8b9580feffff4c899578feffff4c8b9578feffff4c8b1d902800004c89d0489949f7fb4989d24c899570feffff488bbd68ffffff488bb5a0feffff" + h = h + "488b9570feffffe844e4ffff48898568feffff4c8b9578feffff4c8b1d4f2800004c89d0489949f7fb4989c24c899560feffff4c8b9560feffff4c899540ffffff48c78558feffff010000004c8b95a0feffff4c8b9d58feffff4d01da4c899550feffff4c8b9550feffff4c8995a0feffffe9dbfeffff48c78548feffff0100" + h = h + "00004c8b9558ffffff4c8b9d48feffff4d01da4c899540feffff4c8b9540feffff4c899558ffffffe966fcffff4c8b55904c8b5d804d01da4c899538feffff488bbd68ffffff488bb538feffffe851e3ffff48898530feffff48c78528feffff010000004c8b9528feffff4c899520feffff488b7db0e855e2ffff48898518fe" + h = h + "ffff488b7da0e845e2ffff48898510feffff4c8b9518feffff4c8b9d10feffff4d39da0f95c04c0fb6d04c899508feffff4c8b9508feffff4983fa017402eb2c48c78500feffff010000004c8b9500feffff49f7da4c8995f8fdffff4c8b95f8fdffff4c899520feffffeb00488bbd68ffffff488bb520feffffe877e2ffff48" + h = h + "8985f0fdffff488bbd68ffffffe8f7e7ffff488985e8fdffff488b85e8fdffff4881c4180200005dc3554889e54883ec7848897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb1248c745e001000000488b45e04883c4785dc3488b7df8e8a0dfffff488945d848c7" + h = h + "45d0000000004c8b55d84c8b5dd04d39da0f94c04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8e833e0ffff488945b84c8b55b84c8955b0488b7db0e821e1ffff488945a848c745a0000000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55984983" + h = h + "fa017402eb1248c7459001000000488b45904883c4785dc348c7458800000000488b45884883c4785dc3554889e54883ec5048897df848c745f0010000004c8b55f04c8955e848c745e0000000004c8b55e04c8955d8eb00eb004c8b55d84c8b5df84d39da0f9dc04c0fb6d04c8955d04c8b55d04983fa017402eb02eb4148c7" + h = h + "45c8020000004c8b55e84c8b5dc84d0fafd34c8955c04c8b55c04c8955e848c745b8010000004c8b55d84c8b5db84d01da4c8955b04c8b55b04c8955d8eb99488b45e84883c4505dc3554889e54881ecb800000048897df8488b7df8e818e0ffff488945f04c8b55f04c8955e848c745e0000000004c8b55e84c8b5de04d39da" + h = h + "0f94c04c0fb6d04c8955d84c8b55d84983fa017402eb1548c745d000000000488b45d04881c4b80000005dc348c745c8010000004c8b55e84c8b5dc84d29da4c8955c0488b7df8488b75c0e8d2dfffff488945b84c8b55b84c8955b048c745a8000000004c8b55a84c8955a0eb00eb0048c74598000000004c8b55b04c8b5d98" + h = h + "4d39da0f94c04c0fb6d04c8955904c8b55904983fa017402eb02eb5748c74588020000004c8b55b04c8b5d884c89d0489949f7fb4989c24c8955804c8b55804c8955b048c78578ffffff010000004c8b55a04c8b9d78ffffff4d01da4c899570ffffff4c8b9570ffffff4c8955a0e97bffffff48c78568ffffff010000004c8b" + h = h + "55e84c8b9d68ffffff4d29da4c899560ffffff48c78558ffffff1e0000004c8b9560ffffff4c8b9d58ffffff4d0fafd34c899550ffffff4c8b9550ffffff4c8b5da04d01da4c899548ffffff488b8548ffffff4881c4b80000005dc3554889e54883ec7848897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d048" + h = h + "9949f7fb4989c24c8955e04c8b55e04c8955d8488b7df8e85ddeffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb1248c745c000000000488b45c04883c4785dc3488b7df8488b75d8e841deffff488945b848c745b01e0000004c8b55f04c8b5db04c89d0489949f7fb4989d2" + h = h + "4c8955a8488b7da8e81dfdffff488945a04c8b55b84c8b5da04c89d0489949f7fb4989c24c89559848c74590020000004c8b55984c8b5d904c89d0489949f7fb4989d24c895588488b45884883c4785dc3554889e54883ec7048897df8488975f048c745e81e0000004c8b55f04c8b5de84c89d0489949f7fb4989c24c8955e0" + h = h + "4c8b55e04c8955d8488b7df8e868ddffff488945d04c8b55d84c8b5dd04d39da0f9dc04c0fb6d04c8955c84c8b55c84983fa017402eb064883c4705dc3488b7df8488b75d8e858ddffff488945c048c745b81e0000004c8b55f04c8b5db84c89d0489949f7fb4989d24c8955b0488b7db0e834fcffff488945a84c8b55c04c8b" + h = h + "5da84d01da4c8955a04c8b55a04c895598488b7df8488b75d8488b5598e8aeddffff48894590488b45904883c4705dc3554889e54881ec0001000048897df8488b7df8e8b1dcffff488945f04c8b55f04c8955e848c745e0010000004c8b55e84c8b5de04d01da4c8955d8488b7dd8e80fdcffff488945d04c8b55d04c8955c8" + h = h + "48c745c0000000004c8b55c04c8955b848c745b0000000004c8b55b04c8955a8eb00eb004c8b55a84c8b5de84d39da0f9dc04c0fb6d04c8955a04c8b55a04983fa017402eb05e9d9000000488b7df8488b75a8e84adcffff4889459848c74590020000004c8b55984c8b5d904d0fafd34c8955884c8b55884c8b5db84d01da4c" + h = h + "8955804c8b55804c899578ffffff4c8b9578ffffff4c8b1dd42000004c89d0489949f7fb4989d24c899570ffffff488b7dc8488b75a8488b9570ffffffe88edcffff48898568ffffff4c8b9578ffffff4c8b1d992000004c89d0489949f7fb4989c24c899560ffffff4c8b9560ffffff4c8955b848c78558ffffff010000004c" + h = h + "8b55a84c8b9d58ffffff4d01da4c899550ffffff4c8b9550ffffff4c8955a8e9fefeffff488b7dc8488b75e8e8f2dbffff48898548ffffff48c78540ffffff000000004c8b55b84c8b9d40ffffff4d39da0f95c04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb51488b7dc8488b75e8488b55b8e8d1dbffff48" + h = h + "898530ffffff48c78528ffffff010000004c8b55e84c8b9d28ffffff4d01da4c899520ffffff488b7dc8488bb520ffffffe86ddbffff48898518ffffffeb0048c78510ffffff01000000488b7dc8488bb510ffffffe81cdbffff48898508ffffff488b7dc8e8bedbffff48898500ffffff488b8500ffffff4881c4000100005d" + h = h + "c3554889e54883ec2048897df8488975f0488b7df0e82ed9ffff488945e8488b7df8488b75e8e8aeeeffff488945e0488b45e04883c4205dc3554889e54881ec0802000048897df8488975f048c745e8000000004c8b55f84c8b5de84d39da0f94c04c0fb6d04c8955e048c745d8000000004c8b55f04c8b5dd84d39da0f94c0" + h = h + "4c0fb6d04c8955d04c8b55e04c8b5dd04d09da4c8955c84c8b55c84983fa017402eb1548c745c000000000488b45c04881c4080200005dc3488b7df8e8d9d7ffff488945b848c745b0000000004c8b55b84c8b5db04d39da0f94c04c0fb6d04c8955a84c8b55a84983fa017405e921020000488b7df8e818d8ffff488945a04c" + h = h + "8b55a04c89559848c74590000000004c8b55f04c8b5d904d39da0f9fc04c0fb6d04c8955884c8b155c1e00004c8b5df04c89d0489949f7fb4989c24c8955804c8b55984c8b5d804d39da0f9dc04c0fb6d04c899578ffffff4c8b55884c8b9d78ffffff4d21da4c899570ffffff4c8b150c1e00004c8b5df04c89d0489949f7fb" + h = h + "4989c24c899568ffffff4c8b55984c8b9d68ffffff4d39da0f9ec04c0fb6d04c899560ffffff4c8b9570ffffff4c8b9d60ffffff4d21da4c899558ffffff4c8b9558ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899550ffffff488bbd50ffffffe85cd7ffff48898548ffffff488b8548ffffff4881c4080200" + h = h + "005dc348c78540ffffff000000004c8b55f04c8b9d40ffffff4d39da0f9cc04c0fb6d04c899538ffffff4c8b15571d00004c8b5df04c89d0489949f7fb4989c24c899530ffffff4c8b55984c8b9d30ffffff4d39da0f9ec04c0fb6d04c899528ffffff4c8b9538ffffff4c8b9d28ffffff4d21da4c899520ffffff4c8b15fe1c" + h = h + "00004c8b5df04c89d0489949f7fb4989c24c899518ffffff4c8b55984c8b9d18ffffff4d39da0f9dc04c0fb6d04c899510ffffff4c8b9520ffffff4c8b9d10ffffff4d21da4c899508ffffff4c8b9508ffffff4983fa017402eb364c8b55984c8b5df04d0fafd34c899500ffffff488bbd00ffffffe84ed6ffff488985f8feff" + h = h + "ff488b85f8feffff4881c4080200005dc3eb00488b7df8e8efdcffff488985f0feffff4c8b95f0feffff4c8995e8feffff488bbde8feffffe83cd7ffff488985e0feffff4c8b95e0feffff4c8995d8feffff48c785d0feffff010000004c8b95d8feffff4c8b9dd0feffff4d01da4c8995c8feffff488bbdc8feffffe882d6ff" + h = h + "ff488985c0feffff4c8b95c0feffff4c8995b8feffff48c785b0feffff000000004c8b95b0feffff4c8995a8feffff48c785a0feffff000000004c8b95a0feffff4c899598feffffeb00eb004c8b9598feffff4c8b9dd8feffff4d39da0f9dc04c0fb6d04c899590feffff4c8b9590feffff4983fa017402eb05e9fb00000048" + h = h + "8bbde8feffff488bb598feffffe890d6ffff48898588feffff4c8b9588feffff4c8b5df04d0fafd34c899580feffff4c8b9580feffff4c8b9da8feffff4d01da4c899578feffff4c8b9578feffff4c899570feffff4c8b9570feffff4c8b1d0d1b00004c89d0489949f7fb4989d24c899568feffff488bbdb8feffff488bb598" + h = h + "feffff488b9568feffffe8c1d6ffff48898560feffff4c8b9570feffff4c8b1dcc1a00004c89d0489949f7fb4989c24c899558feffff4c8b9558feffff4c8995a8feffff48c78550feffff010000004c8b9598feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899598feffffe9d0feffff488bbdb8feff" + h = h + "ff488bb5d8feffffe816d6ffff48898540feffff48c78538feffff000000004c8b95a8feffff4c8b9d38feffff4d39da0f95c04c0fb6d04c899530feffff4c8b9530feffff4983fa017402eb60488bbdb8feffff488bb5d8feffff488b95a8feffffe8e9d5ffff48898528feffff48c78520feffff010000004c8b95d8feffff" + h = h + "4c8b9d20feffff4d01da4c899518feffff488bbdb8feffff488bb518feffffe87fd5ffff48898510feffffeb00488bbde8feffffe897d4ffff48898508feffff488bbdb8feffff488bb508feffffe823d5ffff48898500feffff488bbdb8feffffe8a3daffff488985f8fdffff488b85f8fdffff4881c4080200005dc3554889" + h = h + "e54881ec3801000048897df8488975f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb3b48c745d8010000004c8b55d84c89154319000048c745d0000000004c8b55d04c89153819000048c745c800000000488b45c84881c4380100005dc3488b7df8e81cd2ffff4889" + h = h + "45c048c745b8000000004c8b55c04c8b5db84d39da0f94c04c0fb6d04c8955b04c8b55b04983fa017402eb68488b7df8e85ed2ffff488945a84c8b55a84c8955a04c8b55a04c8b5df04c89d0489949f7fb4989d24c8955984c8b55984c8915b51800004c8b55a04c8b5df04c89d0489949f7fb4989c24c895590488b7d90e845" + h = h + "d2ffff48894588488b45884881c4380100005dc3488b7df8e8eed8ffff488945804c8b55804c899578ffffff488bbd78ffffffe841d3ffff48898570ffffff4c8b9570ffffff4c899568ffffff488bbd68ffffffe8aad2ffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff000000004c8b9550ffffff" + h = h + "4c899548ffffff4c8b9568ffffff4c899540ffffffeb00eb0048c78538ffffff000000004c8b9540ffffff4c8b9d38ffffff4d39da0f9ec04c0fb6d04c899530ffffff4c8b9530ffffff4983fa017402eb05e9f800000048c78528ffffff010000004c8b9540ffffff4c8b9d28ffffff4d29da4c899520ffffff4c8b9520ffff" + h = h + "ff4c899540ffffff4c8b9548ffffff4c8b1d5a1700004d0fafd34c899518ffffff488bbd78ffffff488bb540ffffffe86ed2ffff48898510ffffff4c8b9518ffffff4c8b9d10ffffff4d01da4c899508ffffff4c8b9508ffffff4c899500ffffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989c24c8995f8feffff488b" + h = h + "bd58ffffff488bb540ffffff488b95f8feffffe8b8d2ffff488985f0feffff4c8b9500ffffff4c8b5df04c89d0489949f7fb4989d24c8995e8feffff4c8b95e8feffff4c899548ffffffe9c8feffff488bbd58ffffff488bb568ffffffe841d2ffff488985e0feffff488bbd78ffffffe85bd1ffff488985d8feffff488bbd58" + h = h + "ffffff488bb5d8feffffe8e7d1ffff488985d0feffff4c8b9548ffffff4c891574160000488bbd58ffffffe859d7ffff488985c8feffff488b85c8feffff4881c4380100005dc3554889e54881ec2001000048897df8488b7df8488b47f84883e801488945f04c8b55f04c8955e848c745e0000000004c8b55e04c8955d848c7" + h = h + "45d0000000004c8b55d04c8955c848c745c0000000004c8b55e84c8b5dc04d39da0f9fc04c0fb6d04c8955b848c745b000000000488b7df8488b75b0480fb60437488945a848c745a02d0000004c8b55a84c8b5da04d39da0f94c04c0fb6d04c8955984c8b55b84c8b5d984d21da4c8955904c8b55904983fa017402eb2248c7" + h = h + "4588010000004c8b55884c8955c848c74580010000004c8b55804c8955d8eb0048c78578ffffff000000004c8b9578ffffff4c899570ffffffeb00eb004c8b55d84c8b5de84d39da0f9dc04c0fb6d04c899568ffffff4c8b9568ffffff4983fa017402eb05e950010000488b7df8488b75d8480fb6043748898560ffffff4c8b" + h = h + "9560ffffff4c899558ffffff48c78550ffffff300000004c8b9558ffffff4c8b9d50ffffff4d39da0f9cc04c0fb6d04c899548ffffff48c78540ffffff390000004c8b9558ffffff4c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9548ffffff4c8b9d38ffffff4d09da4c899530ffffff4c8b9530ffffff49" + h = h + "83fa017402eb05e9ae00000048c78528ffffff0a000000488bbd70ffffff488bb528ffffffe80ff5ffff48898520ffffff4c8b9520ffffff4c899570ffffff48c78518ffffff300000004c8b9558ffffff4c8b9d18ffffff4d29da4c899510ffffff488bbd70ffffff488bb510ffffffe88cf4ffff48898508ffffff4c8b9508" + h = h + "ffffff4c899570ffffff48c78500ffffff010000004c8b55d84c8b9d00ffffff4d01da4c8995f8feffff4c8b95f8feffff4c8955d8e981feffff48c785f0feffff000000004c8b55c84c8b9df0feffff4d39da0f95c04c0fb6d04c8995e8feffff4c8b95e8feffff4983fa017402eb23488bbd70ffffffe891e5ffff488985e0" + h = h + "feffff4c8b95e0feffff4c899570ffffffeb00488b8570ffffff4881c4200100005dc3554889e54881ec1802000048897df848c745f0000000004c8b55f84c8b5df04d39da0f94c04c0fb6d04c8955e84c8b55e84983fa017402eb5148c745e002000000488b7de0e800000000488945d84c8b55d84c8955d048c745c8000000" + h = h + "0048c745c030000000488b7dd0488b75c8488b55c088143748c745b800000000488b45d04881c4180200005dc348c745b0000000004c8b55b04c8955a8488b7df8e845d3ffff488945a04c8b55a04c895598488b7d98e875cdffff4889459048c74588000000004c8b55904c8b5d884d39da0f9cc04c0fb6d04c8955804c8b55" + h = h + "804983fa017402eb4648c78578ffffff010000004c8b9578ffffff4c8955a8488b7df8e865e4ffff48898570ffffff488bbd70ffffffe8d0d2ffff48898568ffffff4c8b9568ffffff4c895598eb00488b7d98e821cdffff48898560ffffff4c8b9560ffffff4c899558ffffff48c78550ffffff0a0000004c8b9558ffffff4c" + h = h + "8b9d50ffffff4d0fafd34c899548ffffff48c78540ffffff040000004c8b9548ffffff4c8b9d40ffffff4d01da4c899538ffffff4c8b9538ffffff4c899530ffffff488bbd30ffffffe80000000048898528ffffff4c8b9528ffffff4c899520ffffff48c78518ffffff010000004c8b9530ffffff4c8b9d18ffffff4d29da4c" + h = h + "899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff00000000488bbd20ffffff488bb508ffffff488b9500ffffff88143748c785f8feffff00000000488b7d98e83ed2ffff488985f0feffff4c8b95f0feffff4c8995e8feffffeb00eb0048c785e0feffff000000004c8b95e8feffff4c8b9de0feffff4d39da" + h = h + "0f94c04c0fb6d04c8995d8feffff4c8b95d8feffff4983fa017402eb05e9bd00000048c785d0feffff0a000000488bbde8feffff488bb5d0feffffe83df7ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b9508ffffff4c8b9db8feffff4d29da4c8995b0feffff4c8b95b0feffff4c" + h = h + "899508ffffff48c785a8feffff300000004c8b15801000004c8b9da8feffff4d01da4c8995a0feffff488bbd20ffffff488bb508ffffff488b95a0feffff88143748c78598feffff000000004c8b95c0feffff4c8995e8feffffe903ffffff48c78590feffff000000004c8b55a84c8b9d90feffff4d39da0f95c04c0fb6d04c" + h = h + "899588feffff4c8b9588feffff4983fa017402eb6148c78580feffff010000004c8b9508ffffff4c8b9d80feffff4d29da4c899578feffff4c8b9578feffff4c899508ffffff48c78570feffff2d000000488bbd20ffffff488bb508ffffff488b9570feffff88143748c78568feffff00000000eb0048c78560feffff010000" + h = h + "004c8b9530ffffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c8b9d08ffffff4d29da4c899550feffff4c8b9550feffff4c899548feffff48c78540feffff010000004c8b9548feffff4c8b9d40feffff4d01da4c899538feffff488bbd38feffffe80000000048898530feffff4c8b9530feffff4c899528" + h = h + "feffff48c78520feffff000000004c8b9520feffff4c899518feffffeb00eb004c8b9518feffff4c8b9d48feffff4d39da0f9dc04c0fb6d04c899510feffff4c8b9510feffff4983fa017402eb05e98b0000004c8b9508ffffff4c8b9d18feffff4d01da4c899508feffff488bbd20ffffff488bb508feffff480fb604374889" + h = h + "8500feffff488bbd28feffff488bb518feffff488b9500feffff88143748c785f8fdffff0000000048c785f0fdffff010000004c8b9518feffff4c8b9df0fdffff4d01da4c8995e8fdffff4c8b95e8fdffff4c899518feffffe940ffffff488b8528feffff4881c4180200005dc3554889e54881ec5802000048897df8488975" + h = h + "f048c745e8000000004c8b55f04c8b5de84d39da0f94c04c0fb6d04c8955e04c8b55e04983fa017402eb404c8d15260d00004c8955d8488b7dd8e841c6ffff488945d048c745c8010000004c8b55c84c8915ba0d000048c745c000000000488b45c04881c4580200005dc348c745b8000000004c8b55f84c8b5db84d39da0f94" + h = h + "c04c0fb6d04c8955b04c8b55b04983fa017402eb2848c745a8000000004c8b55a84c8915700d000048c745a000000000488b45a04881c4580200005dc3488b7df8e8c5cdffff488945984c8b55984c895590488b7df0e8b0cdffff488945884c8b55884c895580488b7d90488b7580e89dd2ffff48898578ffffff4c8b9578ff" + h = h + "ffff4c899570ffffff48c78568ffffff000000004c8b9570ffffff4c8b9d68ffffff4d39da0f9cc04c0fb6d04c899560ffffff4c8b9560ffffff4983fa017402eb264c8b55f84c8915cb0c000048c78558ffffff00000000488b8558ffffff4881c4580200005dc3488b7d90e85fc7ffff48898550ffffff4c8b9550ffffff4c" + h = h + "899548ffffff488b7d80e841c7ffff48898540ffffff4c8b9540ffffff4c899538ffffff48c78530ffffff000000004c8b9570ffffff4c8b9d30ffffff4d39da0f94c04c0fb6d04c899528ffffff4c8b9528ffffff4983fa017405e9af00000048c78520ffffff010000004c8b9520ffffff4c899518ffffff4c8b9548ffffff" + h = h + "4c8b9d38ffffff4d39da0f95c04c0fb6d04c899510ffffff4c8b9510ffffff4983fa017402eb2c48c78508ffffff010000004c8b9508ffffff49f7da4c899500ffffff4c8b9500ffffff4c899518ffffffeb0048c785f8feffff000000004c8b95f8feffff4c8915ac0b0000488bbd18ffffffe850c5ffff488985f0feffff48" + h = h + "8b85f0feffff4881c4580200005dc3488b7d90e861c6ffff488985e8feffff4c8b95e8feffff4c8995e0feffff48c785d8feffff010000004c8b95e0feffff4c8b9dd8feffff4d01da4c8995d0feffff488bbdd0feffffe8a7c5ffff488985c8feffff4c8b95c8feffff4c8995c0feffff48c785b8feffff010000004c8b95e0" + h = h + "feffff4c8b9db8feffff4d01da4c8995b0feffff488bbdc0feffff488bb5b0feffffe87cc6ffff488985a8feffff48c785a0feffff00000000488bbda0feffffe815c8ffff48898598feffff4c8b9598feffff4c899590feffff488b7d90e866e5ffff48898588feffff4c8b9588feffff4c899580feffff4c8b9580feffff4c" + h = h + "899578feffffeb00eb0048c78570feffff000000004c8b9578feffff4c8b9d70feffff4d39da0f9ec04c0fb6d04c899568feffff4c8b9568feffff4983fa017402eb05e99301000048c78560feffff010000004c8b9578feffff4c8b9d60feffff4d29da4c899558feffff4c8b9558feffff4c899578feffff488bbd90feffff" + h = h + "e82be8ffff48898550feffff4c8b9550feffff4c899590feffff488b7d90488bb578feffffe832e6ffff48898548feffff48c78540feffff000000004c8b9548feffff4c8b9d40feffff4d39da0f95c04c0fb6d04c899538feffff4c8b9538feffff4983fa017402eb5b488bbd90feffffe893caffff48898530feffff48c785" + h = h + "28feffff01000000488bbd30feffff488bb528feffffe8e6e9ffff48898520feffff488bbd20feffffe8ddc9ffff48898518feffff4c8b9518feffff4c899590feffffeb00488bbd90feffff488b7580e8bcceffff48898510feffff48c78508feffff000000004c8b9510feffff4c8b9d08feffff4d39da0f9dc04c0fb6d04c" + h = h + "899500feffff4c8b9500feffff4983fa017402eb41488bbd90feffff488b7580e8b7d5ffff488985f8fdffff4c8b95f8fdffff4c899590feffff488bbdc0feffff488bb578feffffe804e6ffff488985f0fdffffeb00e92dfeffff48c785e8fdffff010000004c8b95e0feffff4c8b9de8fdffff4d01da4c8995e0fdffff488b" + h = h + "bdc0feffff488bb5e0fdffffe812c4ffff488985d8fdffff4c8b9548ffffff4c8b9d38ffffff4d0fafd34c8995d0fdffff488bbdc0feffff488bb5d0fdffffe8b2c3ffff488985c8fdffff488bbdc0feffffe832c9ffff488985c0fdffff4c8b95c0fdffff4c8995b8fdffff488bbd90feffff488bb548ffffffe877c3ffff48" + h = h + "8985b0fdffff488bbd90feffffe8f7c8ffff488985a8fdffff4c8b95a8fdffff4c8915f1070000488b85b8fdffff4881c4580200005dc3554889e54883ec2048897df8488975f0488b7df8488b75f0e89af9ffff488945e84c8b55e84c8955e0488b45e04883c4205dc3554889e54883ec2048897df8488975f0488b7df8488b" + h = h + "75f0e867f9ffff488945e84c8b55e84c8955e0488b057e0700004883c4205dc3554889e54883ec3848897df84c8b55f84c8b1d510700004d39da0f9dc04c0fb6d04c8955f04c8b55f84c8b1d300700004d39da0f9ec04c0fb6d04c8955e84c8b55f04c8b5de84d21da4c8955e04c8b55e04983fa017402eb17488b7df8e8c6c0" + h = h + "ffff488945d8488b45d84883c4385dc3488b7df8e841c4ffff488945d0488b7dd0e8e3c7ffff488945c8488b45c84883c4385dc3554889e54883ec4048897df848c745f0000000004c8b55f84c8b5df04d39da0f9dc04c0fb6d04c8955e84c8b55f84c8b1d970600004d39da0f9ec04c0fb6d04c8955e04c8b55e84c8b5de04d" + h = h + "21da4c8955d84c8b55d84983fa017402eb17488b7df8e82dc0ffff488945d0488b45d04883c4405dc3488b7df8e8a8c3ffff488945c8488b7dc8e84ac7ffff488945c0488b45c04883c4405dc3554889e54881ecc801000048897df848c745f0000000004c8b55f04c891521060000488b7df8e893c6ffff488945e84c8b55e8" + h = h + "4c8955e0488b7de0e8ecc0ffff488945d84c8b55d84c8955d048c745c8030000004c8b55d04c8b5dc84d39da0f9fc04c0fb6d04c8955c04c8b55c04983fa017402eb404c8d152e0500004c8955b8488b7db8e829beffff488945b048c745a8010000004c8b55a84c8915a205000048c745a000000000488b45a04881c4c80100" + h = h + "005dc348c7459800000000488b7de0488b7598e88ac0ffff488945904c8b55904c89558848c74580000000004c8b55804c899578ffffff48c78570ffffff000000004c8b9570ffffff4c899568ffffff48c78560ffffff010000004c8b55d04c8b9d60ffffff4d39da0f9fc04c0fb6d04c899558ffffff4c8b9558ffffff4983" + h = h + "fa017402eb3248c78550ffffff01000000488b7de0488bb550ffffffe801c0ffff48898548ffffff4c8b9548ffffff4c899578ffffffeb0048c78540ffffff020000004c8b55d04c8b9d40ffffff4d39da0f9fc04c0fb6d04c899538ffffff4c8b9538ffffff4983fa017402eb3248c78530ffffff02000000488b7de0488bb5" + h = h + "30ffffffe899bfffff48898528ffffff4c8b9528ffffff4c899568ffffffeb00488b7de0e827bfffff48898520ffffff48c78518ffffff000000004c8b9520ffffff4c8b9d18ffffff4d39da0f9cc04c0fb6d04c899510ffffff4c8b9510ffffff4c899508ffffff48c78500ffffff080000004c8b9568ffffff4c8b9d00ffff" + h = h + "ff4d39da0f9fc04c0fb6d04c8995f8feffff4c8b95f8feffff4983fa017402eb554c8d15500300004c8995f0feffff488bbdf0feffffe845bcffff488985e8feffff48c785e0feffff010000004c8b95e0feffff4c8915b503000048c785d8feffff00000000488b85d8feffff4881c4c80100005dc348c785d0feffff080000" + h = h + "004c8b9568ffffff4c8b9dd0feffff4d39da0f94c04c0fb6d04c8995c8feffff4c8b9508ffffff4983fa000f94c04c0fb6d04c8995c0feffff48c785b8feffff000000004c8b9578ffffff4c8b9db8feffff4d39da0f95c04c0fb6d04c8995b0feffff4c8b95c0feffff4c8b9db0feffff4d09da4c8995a8feffff48c785a0fe" + h = h + "ffff000000004c8b55884c8b9da0feffff4d39da0f95c04c0fb6d04c899598feffff4c8b95a8feffff4c8b9d98feffff4d09da4c899590feffff4c8b95c8feffff4c8b9d90feffff4d21da4c899588feffff4c8b9588feffff4983fa017402eb554c8d15100200004c899580feffff488bbd80feffffe805bbffff48898578fe" + h = h + "ffff48c78570feffff010000004c8b9570feffff4c89157502000048c78568feffff00000000488b8568feffff4881c4c80100005dc34c8b9578ffffff4c8b1d2c0200004d0fafd34c899560feffff4c8b55884c8b9d60feffff4d01da4c899558feffff4c8b9568ffffff4c8b1d060200004d0fafd34c899550feffff4c8b95" + h = h + "58feffff4c8b9d50feffff4d01da4c899548feffff4c8b9548feffff4c899540feffff4c8b9508ffffff4983fa017402eb214c8b9540feffff49f7da4c899538feffff4c8b9538feffff4c899540feffffeb00488b8540feffff4881c4c80100005dc3554889e54883ec6048897df8488b7df8e855fbffff488945f04c8b55f0" + h = h + "4c8955e848c745e0000000004c8b157d0100004c8b5de04d39da0f95c04c0fb6d04c8955d84c8b55d84983fa017402eb1248c745d000000000488b45d04883c4605dc348c745c8000000004c8b55e84c8b5dc84d39da0f9cc04c0fb6d04c8955c04c8b55c04983fa017402eb3d4c8d15b40000004c8955b8488b7db8e87fb9ff" + h = h + "ff488945b048c745a8010000004c8b55a84c8915f800000048c745a000000000488b45a04883c4605dc3488b45e84883c4605dc30000000000000000000000000100000000000000000000000000000012000000000000006469766973696f6e206279207a65726f0a000000000000002300000000000000696e746567657220" + h = h + "6f7574206f66206d616368696e652d776f72642072616e67650a0000000000002c00000000000000696e7465676572206f7574206f6620756e7369676e6564206d616368696e652d776f72642072616e67650a000000000000000000000000000800000000000000100000000000000000000040000000000000000000000010" + h = h + "ffffffffffffff3f000000000000000000000000000000000000000000000000" + return h fn bigint_blob_byte_from_hex(c: int) -> int: if c >= 48 && c <= 57: diff --git a/src/targets/x86_64-linux/main.cr b/src/targets/x86_64-linux/main.cr index 84267a2f..ccb16f9f 100644 --- a/src/targets/x86_64-linux/main.cr +++ b/src/targets/x86_64-linux/main.cr @@ -76,7 +76,6 @@ fn corearch_main() -> int: if cli_parse() != 0: return 1 - g_x86_big_enabled = cli_has("big-int") // This entry emits ELF only. Table and debug channels exist only in the // concat entry, so reject them explicitly instead of reporting an unknown // flag or ignoring a known flag. @@ -123,6 +122,7 @@ fn corearch_main() -> int: if r != 0: println("error: invalid .ccr file") return 1 + backend_select_int_mode() init_backend_arrays() // The loader produces semantic objects only. Rebuild the linear instruction diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 02297b17..8bb33ae5 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -79,7 +79,7 @@ COREARCH = os.path.join(BASE, 'build/corearch') MAGIC = 0x31524343 # "CCR1" -VER = 9 # v9 = v7 段表架构的加法扩展(D10)——R2 P6 Task 3:NOD 记录 36→40B(+28 项索引) +VER = 589825 # v9.1 = (9 << 16) | 1 SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE HEADER_TABLE = 16 + 8 * 12 # 112 TYPE_TAG = 7 diff --git a/tests/selfhost/test_ccr_v7.py b/tests/selfhost/test_ccr_v7.py index d031e73e..a4b0c6ba 100644 --- a/tests/selfhost/test_ccr_v7.py +++ b/tests/selfhost/test_ccr_v7.py @@ -84,7 +84,7 @@ COREARCH = os.path.join(BASE, 'build/corearch') MAGIC = 0x31524343 # "CCR1" -V7 = 9 # R2 P6 Task 3:v9(v7 段表架构的加法扩展,D10)——常量名/文件名从旧 +V7 = 589825 # R2 P6 Task 3:v9;BigInt 批:v9.1 = (9 << 16) | 1 # (「v7」= 段表架构代号;改名的引用面 40+ 处、收益为零) SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE NOD_REC = 40 diff --git a/tests/selfhost/test_view_builtins.py b/tests/selfhost/test_view_builtins.py index 78fb4987..473b98b1 100644 --- a/tests/selfhost/test_view_builtins.py +++ b/tests/selfhost/test_view_builtins.py @@ -29,6 +29,7 @@ BASE = Path(__file__).resolve().parents[2] COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" BOOTSTRAP = BASE / "bootstrap" # ── 探针 ───────────────────────────────────────────────────────────────────── @@ -222,6 +223,29 @@ def corec_build_native(src: str): os.unlink(path) +def corec_build_raw_native(src: str): + """Raw-int native leg for the retired zero-code view invariant.""" + with tempfile.NamedTemporaryFile("w", suffix=".cr", delete=False) as f: + f.write(src) + path = f.name + out = Path(tempfile.mkdtemp()) / "probe" + ccr = Path(str(out) + ".ccr") + try: + subprocess.run([str(COREC), "clean-cache"], cwd=BASE, capture_output=True, timeout=120) + r = subprocess.run([str(COREC), "ccr", path, "-o", str(ccr)], + cwd=BASE, capture_output=True, text=True, timeout=600) + if r.returncode != 0: + return r.returncode, None + r = subprocess.run([str(COREARCH), str(ccr), "--raw-int", "--static", + "-o", str(out)], cwd=BASE, capture_output=True, + text=True, timeout=600) + if r.returncode != 0: + return r.returncode, None + return 0, out + finally: + os.unlink(path) + + def main(): ok = True fails = [] @@ -247,22 +271,23 @@ def expect(name, cond, detail=""): expect("ok_value_42_all_faces", rc_i == 42 and rc_elf == 42 and rc_b == 42, f"解释器 {rc_i} · native {rc_elf} · bootstrap {rc_b}") - # ── ② 零代码生成(擦除):往返写法 vs 直写,ELF 逐字节一致 ── - _, elf_a = corec_build_native(ERASE_INT_VIEW) - _, elf_b = corec_build_native(ERASE_INT_PLAIN) - sha_view = _sha(elf_a) if elf_a else "n/a" - sha_plain = _sha(elf_b) if elf_b else "n/a" - print(f"[② 擦除·int] 往返写法 {sha_view[:16]}… · 直写 {sha_plain[:16]}…") - expect("erase_int_view_byte_identical", elf_a and elf_b and sha_view == sha_plain, - f"{sha_view} != {sha_plain}") - - _, elf_c = corec_build_native(ERASE_STR_VIEW) - _, elf_d = corec_build_native(ERASE_STR_ONE) - sha_view_s = _sha(elf_c) if elf_c else "n/a" - sha_one_s = _sha(elf_d) if elf_d else "n/a" - print(f"[② 擦除·string] 三跳 {sha_view_s[:16]}… · 单跳 {sha_one_s[:16]}…") - expect("erase_str_view_byte_identical", elf_c and elf_d and sha_view_s == sha_one_s, - f"{sha_view_s} != {sha_one_s}") + # ── ② 视图语义(明确 raw 腿;BigInt 默认腿见 ①)────────────── + # BigInt 默认模式不再承诺视图零字节;raw 腿只验证往返语义仍等于直写。 + _, elf_a = corec_build_raw_native(ERASE_INT_VIEW) + _, elf_b = corec_build_raw_native(ERASE_INT_PLAIN) + rc_a = subprocess.run([str(elf_a)], capture_output=True).returncode if elf_a else -1 + rc_b = subprocess.run([str(elf_b)], capture_output=True).returncode if elf_b else -1 + print(f"[② 视图·int/raw] 往返 rc={rc_a} · 直写 rc={rc_b}") + expect("erase_int_view_semantic", rc_a == 42 and rc_b == 42, + f"{rc_a} != {rc_b}") + + _, elf_c = corec_build_raw_native(ERASE_STR_VIEW) + _, elf_d = corec_build_raw_native(ERASE_STR_ONE) + rc_c = subprocess.run([str(elf_c)], capture_output=True).returncode if elf_c else -1 + rc_d = subprocess.run([str(elf_d)], capture_output=True).returncode if elf_d else -1 + print(f"[② 视图·string/raw] 三跳 rc={rc_c} · 单跳 rc={rc_d}") + expect("erase_str_view_semantic", rc_c == rc_d, + f"{rc_c} != {rc_d}") # ── ③ fail-closed:未知名两面都报错 ── rc_u, diags_u = corec_check(UNKNOWN_BUILTIN) diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index a175a946..5bf989b9 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -177,11 +177,11 @@ # **后来人注意**:**任何新增编译器侧全局**都会移动这张表(登记 ⇒ 启动期 intern ⇒ id 顺移)。 # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 -canary_elf pa f113727526bafbde95f40d3c4e3cee7dd1dd0a9486ddd5fe27e961e330bf4dde 28854 -pa_ccr pa.ccr d6ebe3d175dea7322b39e3ca51366d854b6cb2e4b6efb47a05b0ed4fc21c1b10 96985 -pa_static_ccr pa_st.bin.ccr 041b6e3e83d915690324c5e453b49652207e9f919080e53d2ddec1060d595f62 97128 -gt_ccr gt.ccr edfd8e98d7b64fdb3a99e62bffc99954879432a48390bedbe98171348a49dd22 143687 -gt_static_ccr gt_st.bin.ccr bbfe47897c8fb97f9cc38ffbfe6043577604ef86595760db55cd7ac0781b0c97 143830 +canary_elf pa dfae9d845362856fe3609ecd23fcb2deaaa7eae1d2ca39b7a242695513e9bb09 53430 +pa_ccr pa.ccr e581daf5a8e30f91745d3c0b6fb6ae6421422248507ec401a67228ae7c6c2355 96985 +pa_static_ccr pa_st.bin.ccr a7d2433b0c78428ed93e434ccd617f1308362d7bb268764712d2972e243485a1 97128 +gt_ccr gt.ccr 21b1f39c96bd2242b963bb5a1726eb93b912739f77c31317da055d6b37d88103 143687 +gt_static_ccr gt_st.bin.ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662f19bbdc60271 143830 # ⚠ **本组为最终值(全部编译器源码改动落定后重测)**:重锁分量有两个,**都不是发射面泄漏**—— # ① **内容面**:`fmt.cr` 新增 `bool_str` ⇒ 凡 `import fmt` 的程序多一个函数体(ELF/`.ccr` 同增); # ② **编译器身份位移**(#123 同机制):编译器源码任何改动都会改变**其自身 intern 序** ⇒ 同语义下 @@ -261,3 +261,13 @@ gt_static_ccr gt_st.bin.ccr bbfe47897c8fb97f9cc38ffbfe6043577604ef86595760db55cd # 对**同一采集目录**跑 `--verify-only` ⇒ **F3 必红**(sha + size 双向不符)。 # ⇒ 「值表与本表不一致时闸门必红」成立(本闸门非恒绿;此即 J-T7-1 反向对照在本「零变化」 # 结论下的等价形态——**没有重锁就没有「改回旧值」**,故以「植入旧值即红」代之)。 + +# ── 【本批重锁 2026-10-23】BigInt v9.1 / 默认 tagged 用户程序 ── +# canary_elf: 28854B → 53430B。归因 = 用户程序默认嵌入 raw BigInt 运行时 blob, +# 且 pa 的 int 运算走 tagged 快路径;程序行为仍 rc=0。 +# 四条 .ccr: size 不变,sha 变。归因 = version 9 → v9.1 (0x00090001),且编译器 +# raw 标志占用 reserved 字段;段体布局未变。旧值留痕(作废): +# pa_ccr d6ebe3d175dea7322b39e3ca51366d854b6cb2e4b6efb47a05b0ed4fc21c1b10 +# pa_static_ccr 041b6e3e83d915690324c5e453b49652207e9f919080e53d2ddec1060d595f62 +# gt_ccr edfd8e98d7b64fdb3a99e62bffc99954879432a48390bedbe98171348a49dd22 +# gt_static_ccr bbfe47897c8fb97f9cc38ffbfe6043577604ef86595760db55cd7ac0781b0c97 diff --git a/tools/gen_bigint_blob.py b/tools/gen_bigint_blob.py index 21fa0693..72b372f1 100644 --- a/tools/gen_bigint_blob.py +++ b/tools/gen_bigint_blob.py @@ -188,11 +188,23 @@ def emit_core(blob: bytes, funcs: dict[str, int], alloc_calls: list[int], lines.append(f"BIGINT_BLOB_DATA_OFF : int = {data_base}") lines.append(f"BIGINT_BLOB_DATA_LEN : int = {data_len}") lines.append("") - # Keep the literal on one line. The Core lexer handles long string literals; - # it must be returned from a function, not stored in a global: the Python - # bootstrap emits global string initializers as raw quad words. + # Split the hex into short string literals and build the full text with + # runtime concatenation. A single 34KB literal makes the self-hosted lexer + # append one character at a time and exhaust the compiler heap (O(n^2)). lines.append("fn bigint_blob_hex() -> string:") - lines.append(" return \"" + hex_literal(blob) + "\"") + hx = hex_literal(blob) + chunk = 256 + first = True + for i in range(0, len(hx), chunk): + part = hx[i:i + chunk] + if first: + lines.append(f" h : ., mut = \"{part}\"") + first = False + else: + lines.append(f" h = h + \"{part}\"") + if first: + lines.append(" h : ., mut = \"\"") + lines.append(" return h") lines.append("") lines.append("fn bigint_blob_byte_from_hex(c: int) -> int:") lines.append(" if c >= 48 && c <= 57:") From 6ae8d930b6c7cafc75f79cab87be9b7e2c46edda Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 13:02:07 +0800 Subject: [PATCH 28/58] bigint: remove legacy 2-limb backend path Delete tag2l.cr, the g_x86_mw_* frame/tag state, jo/oc tail blocks, and test_mw_task1-6. Raw compiler paths now use plain 64-bit values; user programs continue on tagged BigInt. Also box TI_DEX_S -> TI_INT stores for @raw_int under tagged mode, so raw dex payloads are not misread as tagged immediates. Relock canary_elf: the removed compiler globals shift intern ids; all four .ccr values are unchanged. D1 determinism is 5/5. --- build_selfhost_native.py | 8 +- .../2026-10-23-arbitrary-precision-int.md | 3 +- ...26-10-23-arbitrary-precision-int-design.md | 2 +- src/arch/x86_64/backend_arrays.cr | 66 -- src/arch/x86_64/backend_globals.cr | 50 -- src/arch/x86_64/frame.cr | 112 +-- src/arch/x86_64/instr.cr | 72 +- src/arch/x86_64/sizes.cr | 9 +- src/arch/x86_64/tag2l.cr | 650 ---------------- src/ci/run.sh | 8 +- src/compiler/interp.cr | 17 +- src/compiler/ir_gen.cr | 2 +- src/format/elf/elf.cr | 43 +- src/os/linux/callseq.cr | 19 +- src/targets/x86_64-linux/_import.cr | 6 +- tests/harness/ci_hook_allowlist.txt | 10 +- tests/harness/test_criteria_mutations.py | 125 +-- tests/selfhost/test_ccr_types.py | 2 +- tests/selfhost/test_mw_task1.py | 301 -------- tests/selfhost/test_mw_task2.py | 725 ------------------ tests/selfhost/test_mw_task3.py | 135 ---- tests/selfhost/test_mw_task4.py | 185 ----- tests/selfhost/test_mw_task5.py | 185 ----- tests/selfhost/test_mw_task6.py | 145 ---- tests/selfhost/test_opt_dex.py | 5 +- tools/baseline/canary_values.tsv | 12 +- 26 files changed, 80 insertions(+), 2817 deletions(-) delete mode 100644 src/arch/x86_64/tag2l.cr delete mode 100644 tests/selfhost/test_mw_task1.py delete mode 100644 tests/selfhost/test_mw_task2.py delete mode 100644 tests/selfhost/test_mw_task3.py delete mode 100644 tests/selfhost/test_mw_task4.py delete mode 100644 tests/selfhost/test_mw_task5.py delete mode 100644 tests/selfhost/test_mw_task6.py diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 27237cb6..461ce018 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -17,7 +17,7 @@ 段内/段间**相对顺序 = 行为定义面**(平铺编译单元的声明序/全局序),分段只做 命名与按序组合,顺序与拆分前逐一对应,不可乱序。 架构轴 `arch_x86_64_files` × 格式轴 `format_elf_files` × OS 轴 `os_linux_files` - (波 1 落位:Task 1 三轴搬迁 + 组合根 / Task 3/4 frame·tag2l 入架构轴 / + (波 1 落位:Task 1 三轴搬迁 + 组合根 / Task 3/4 frame 入架构轴 / Task 2/5/6 entry·callseq·syscall 入 OS 轴——**全轴已就位**)+ 组合根 `x86_linux_target_files`(target triple 命名:project-mode 入口链,不入 concat——见段注释)。 @@ -58,10 +58,7 @@ ] # ① 架构轴 x86_64:寄存器分配 CAG + 指令编码 + 字节尺寸单源(sizes.cr) -# + int 多字 M1 tag/2L 编码族(tag2l.cr——波 1 Task 4 自 instr.cr 整搬)。 -# 段内顺序 = 「被依赖者先」惯例(函数可见性与顺序无关):tag2l 消费 instr 的 -# 编码原语 → instr 之后;frame.cr → tag2l(pf_epilogue 函数尾块调 e2_mw_*; -# tag2l 反向消费 frame 的 g2_tag_off——同轴双向引用,波 1 Task 4 裁定可接受)。 +# + tagged BigInt 立即数/装箱辅助(int_rep.cr)与运行时 blob。 arch_x86_64_files = [ 'src/arch/x86_64/backend_schema.cr', 'src/arch/x86_64/backend_globals.cr', @@ -71,7 +68,6 @@ 'src/arch/x86_64/int_rep.cr', 'src/runtime/bigint_blob.cr', 'src/arch/x86_64/instr.cr', - 'src/arch/x86_64/tag2l.cr', 'src/arch/x86_64/frame.cr', ] diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index a83271cd..27ef5983 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -406,5 +406,6 @@ cmp /tmp/corec2 /tmp/corec3 - 用户程序的 `.ccr` header reserved 字段为 0,`corearch` 默认按 tagged BigInt 发射。 - 编译器/自举链的 `.ccr` header 写 `CCR_FLAG_RAW_INT`;`corearch` 自动按 raw 发射,也支持显式 `--raw-int`。 - `.ccr` 版本为 v9.1 `(9 << 16) | 1`;`.cir` 缓存版本为 23。 -- 2-limb `tag2l` 仍作为 raw 编译器/回退路径保留,尚未删除;默认用户程序不经过它。 +- 2-limb `tag2l`、`g_x86_mw_*` 状态和帧 tag 区已删除;raw 编译器/回退路径只用 64 位裸值,默认用户程序走 tagged BigInt。 +- 旧 `test_mw_*` 已删除,语义面由 `tests/selfhost/test_bigint_*.py` 承接。 - canary 已按 v9.1、默认 tagged ELF 和全局整数初始化重新锁定。 diff --git a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md index b0b56500..278c84bb 100644 --- a/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md +++ b/docs/superpowers/specs/2026-10-23-arbitrary-precision-int-design.md @@ -166,7 +166,7 @@ bi_to_uword_checked 4. 不保证大整数运算与立即数运算有相同性能。 5. 不改变 arena 的生命周期模型。 6. 自举链的 stage0 由 Python 后端生成。Python 前端使用 Python 整数;Python 后端和运行时必须使用与 corec 相同的 tagged value 和对象布局,否则 stage0 与 self-hosted 输出不一致。 -7. 现有 2-limb `tag2l` 机制只覆盖局部加减,新实现完成后移除这条专用路径。 +7. 旧 2-limb `tag2l` 机制只覆盖局部加减,已在新实现完成后移除;raw 编译器/回退路径只使用 64 位裸值。 ## 未决 diff --git a/src/arch/x86_64/backend_arrays.cr b/src/arch/x86_64/backend_arrays.cr index 0e0330e5..1c0d1e67 100644 --- a/src/arch/x86_64/backend_arrays.cr +++ b/src/arch/x86_64/backend_arrays.cr @@ -186,72 +186,6 @@ fn grow_func_cp(needed: int): g_x86_func_cp = nb g_x86_func_cp_cap = nc -fn grow_mw_tag_off(needed: int): - if needed < g_x86_mw_tag_off_cap: - return - nc : ., mut = g_x86_mw_tag_off_cap * 2 - if nc < 128: - nc = 128 - if nc < needed: - nc = needed + 128 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_tag_off, g_x86_mw_tag_off_cap * 8, nb) - g_x86_mw_tag_off = nb - g_x86_mw_tag_off_cap = nc - -fn grow_mw_jo_patch(needed: int): - if needed < g_x86_mw_jo_cap: - return - nc : ., mut = g_x86_mw_jo_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_pos, g_x86_mw_jo_cap * 8, nb) - g_x86_mw_jo_pos = nb - nb2 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_dest, g_x86_mw_jo_cap * 8, nb2) - g_x86_mw_jo_dest = nb2 - nb3 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_is_sub, g_x86_mw_jo_cap * 8, nb3) - g_x86_mw_jo_is_sub = nb3 - nb4 := alloc(nc * 8) - _dyncpy(g_x86_mw_jo_resume, g_x86_mw_jo_cap * 8, nb4) - g_x86_mw_jo_resume = nb4 - g_x86_mw_jo_cap = nc - -fn grow_mw_oc_patch(needed: int): - if needed < g_x86_mw_oc_cap: - return - nc : ., mut = g_x86_mw_oc_cap * 2 - if nc < 64: - nc = 64 - if nc < needed: - nc = needed + 64 - nb := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_pos1, g_x86_mw_oc_cap * 8, nb) - g_x86_mw_oc_pos1 = nb - nb2 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_pos2, g_x86_mw_oc_cap * 8, nb2) - g_x86_mw_oc_pos2 = nb2 - nb3 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_s1, g_x86_mw_oc_cap * 8, nb3) - g_x86_mw_oc_s1 = nb3 - nb4 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_s2, g_x86_mw_oc_cap * 8, nb4) - g_x86_mw_oc_s2 = nb4 - nb5 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_dest, g_x86_mw_oc_cap * 8, nb5) - g_x86_mw_oc_dest = nb5 - nb6 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_op, g_x86_mw_oc_cap * 8, nb6) - g_x86_mw_oc_op = nb6 - nb7 := alloc(nc * 8) - _dyncpy(g_x86_mw_oc_resume, g_x86_mw_oc_cap * 8, nb7) - g_x86_mw_oc_resume = nb7 - g_x86_mw_oc_cap = nc - fn grow_func_code_sz(needed: int): if needed < g_x86_func_code_sz_cap: return diff --git a/src/arch/x86_64/backend_globals.cr b/src/arch/x86_64/backend_globals.cr index a1325cf5..a4d3cceb 100644 --- a/src/arch/x86_64/backend_globals.cr +++ b/src/arch/x86_64/backend_globals.cr @@ -50,56 +50,6 @@ g_x86_emit_vars : string, mut g_x86_emit_vars_cap : int, mut g_x86_emit_var_count : int, mut g_x86_emit_stack_size : int, mut -// Tag area for potentially multiword int variables, appended to the stack frame. -// g_x86_mw_tag_off maps a function-local variable to its tag byte offset: an i64 -// array indexed by v - var_start, with negative rbp-relative offsets; -1 means not -// tagged. g_x86_mw_tag_count is the number of tagged variables in the current -// function, one tag byte each. See the multiword backend design documents. -g_x86_mw_tag_off : string, mut -g_x86_mw_tag_off_cap : int, mut -g_x86_mw_tag_count : int, mut -// Overflow-jump sites for tagged int add and sub fast paths. emit_instr records each -// jo site. At the end of the function, frame.cr emits the 2-limb slow block and -// patches the recorded sites. -// Four parallel i64 arrays share one capacity and grow together. Index is site -// order, which is instruction order; one instruction has at most one site: -// g_x86_mw_jo_pos: absolute jo position; rel32 is at pos+2 and jo is 6 bytes. -// g_x86_mw_jo_dest: destination var index for the fast-path store. The slot form -// is computed at block emission, so register-allocated forms need no extra -// record. -// g_x86_mw_jo_is_sub: 1 for sub. The high-limb correction differs: for add it is -// CF ? -1 : 0; for sub it is CF ? 0 : -1. See e2_mw_slow_block in tag2l.cr. -// g_x86_mw_jo_resume: absolute instruction after the fast-path store, where the -// slow block jumps back. elf.cr fills it once the instruction is emitted. -g_x86_mw_jo_pos : string, mut -g_x86_mw_jo_cap : int, mut -g_x86_mw_jo_count : int, mut -g_x86_mw_jo_dest : string, mut -g_x86_mw_jo_is_sub : string, mut -g_x86_mw_jo_resume : string, mut -// Tag-check sites for consumers of tagged operands. Add, sub, and comparison fast -// paths first emit a tag check (test byte + jne rel32; tag=1 means a 2-limb -// operand). The jne target is the function's 2-limb block, whose position is known -// only at the end, so it is patched like the jo sites. -// Eight parallel i64 arrays share one capacity; index is site order, which is -// instruction order: -// g_x86_mw_oc_pos1/pos2: absolute positions of the first and second check jne, -// or -1 when absent (operand untagged, or s1 == s2). -// g_x86_mw_oc_s1/s2/dest: operand rows and destination; slot forms are computed -// at block emission. -// g_x86_mw_oc_op: OP_ADD/OP_SUB select the arithmetic block; OP_EQ..OP_GE select -// the comparison block. -// g_x86_mw_oc_resume: instruction end, where the block jumps back. -// See e2_mw_opnd_block in tag2l.cr. -g_x86_mw_oc_pos1 : string, mut -g_x86_mw_oc_pos2 : string, mut -g_x86_mw_oc_cap : int, mut -g_x86_mw_oc_count : int, mut -g_x86_mw_oc_s1 : string, mut -g_x86_mw_oc_s2 : string, mut -g_x86_mw_oc_dest : string, mut -g_x86_mw_oc_op : string, mut -g_x86_mw_oc_resume : string, mut g_x86_ret_patch_pos : string, mut g_x86_ret_patch_cap : int, mut g_x86_ret_patch_count : int, mut diff --git a/src/arch/x86_64/frame.cr b/src/arch/x86_64/frame.cr index 64e3076d..8ea521f3 100644 --- a/src/arch/x86_64/frame.cr +++ b/src/arch/x86_64/frame.cr @@ -1,48 +1,27 @@ // === frame.cr === -// x86-64 function frame: size calculation, prologue, epilogue, and the blocks -// appended at the function tail. +// x86-64 function frame: size calculation, prologue, and epilogue. // // Frame size has one source: pf_frame_size(vc). Phase 2 dry-run and the Phase 3 // sub rsp immediate both call it. pf_frame_overhead(ss) is only for the dry-run // byte count and must agree with the actual emission from pf_prologue and // pf_epilogue. Encoding sizes come from sizes.cr. // -// Tag area: tag bytes live below the slot area at -(vc*8 + 1 + k). The formula -// and recognition rules are in tag2l.cr mw_setup_tags. This file reads them -// through g2_tag_off. With zero tags, the frame layout is byte-identical to the -// layout without a tag area. -// // pf_prologue saves callee-saved registers, sets rbp, reserves the frame, stores -// parameters, clears tags on parameter rows, and loads register-allocated -// parameters. pf_epilogue patches returns, fills the sub rsp immediate, emits the -// epilogue, and appends the jo slow-path blocks and 2L operand blocks. +// parameters, and loads register-allocated parameters. pf_epilogue patches +// returns, fills the sub rsp immediate, and emits the epilogue. // // buf is the byte buffer, pos is the absolute buffer position, and cp is the // number of bytes already emitted. Returns the new cp. // -// Depends on instr.cr for encoding primitives, tag2l.cr for the tail blocks, -// sizes.cr for byte sizes, and the shared x86 globals. - -// Tag table read. tag2l.cr mw_setup_tags fills the table per function. -fn g2_tag_off(v: int) -> int: - // var v(当前函数内)的 tag 字节偏移(相对 rbp,恒负);-1 = 非 tagged。 - // 仅当前函数内有效(表由 mw_setup_tags 按函数填充)。 - if v < 0: - return -1 - lv := v - g_current_func_var_start - if lv < 0: - return -1 - if str_len(g_x86_mw_tag_off) <= lv * 8: - return -1 - return r64(g_x86_mw_tag_off, lv * 8) +// Depends on instr.cr for encoding primitives, sizes.cr for byte sizes, and the +// shared x86 globals. // Total frame bytes, shared by Phase 2 dry-run and Phase 3. fn pf_frame_size(vc: int) -> int: - // Frame bytes = vc*8 for var slots + g_x86_mw_tag_count tag bytes, padded - // to 16-byte alignment. With opt >= 1 (6 pushes) the frame is 8 mod 16; - // with opt < 1 (1 push) it is 0 mod 16. With zero tags the result is - // byte-identical to the frame without a tag area. - sz : ., mut = vc * 8 + g_x86_mw_tag_count + // Frame bytes = vc*8 for var slots, padded to 16-byte alignment. With + // opt >= 1 (6 pushes) the frame is 8 mod 16; with opt < 1 (1 push) it is + // 0 mod 16. + sz : ., mut = vc * 8 r := sz % 16 if g_opt_level >= 1: if r != 8: @@ -201,27 +180,6 @@ fn pf_prologue(vs: int, pc: int, ss: int, buf: string, pos: int) -> int: w8(buf, cp+2, 77) w8(buf, cp+3, po2) cp = cp + 4 - // Tag hygiene for tagged parameter rows: clear the tag in the prologue. - // A parameter can be read before its first definition in the function, - // and the tag closure can include parameter rows (for example the B' - // copy p = p + k). Without this clear, a consumer can read the tag from - // a previous call or garbage. Parameters are saved to a forced stack slot - // po2, and the tag byte is in the same frame. - if pty == TI_INT: - ptg := g2_tag_off(vs + pi) - if ptg != -1: - if ptg >= -128 && ptg <= 127: - w8(buf, cp, 198) - w8(buf, cp+1, 69) - w8(buf, cp+2, ptg) - w8(buf, cp+3, 0) - cp = cp + 4 - else: - w8(buf, cp, 198) - w8(buf, cp+1, 133) - e2_w32(buf, cp+2, ptg) - w8(buf, cp+6, 0) - cp = cp + 7 pi = pi + 1 // Load register-allocated parameters from stack to callee-saved regs @@ -244,11 +202,10 @@ fn pf_prologue(vs: int, pc: int, ss: int, buf: string, pos: int) -> int: return cp -// Phase 3 epilogue and function-tail blocks. +// Phase 3 epilogue. // Patch returns to the epilogue start, fill the sub rsp immediate with the actual // frame size, emit the epilogue (add rsp, reverse callee-saved pops, pop rbp, -// ret), then append the jo slow-path blocks and 2L operand blocks. Each site gets -// its own block in the cold tail. Returns the new cp. +// ret). Returns the new cp. // ss is the frame size used by the prologue. fn pf_epilogue(ss: int, buf: string, pos: int) -> int: cp : ., mut = pos @@ -312,51 +269,4 @@ fn pf_epilogue(ss: int, buf: string, pos: int) -> int: cp = cp + 1 // pop rbp w8(buf, cp, 195) cp = cp + 1 // ret - - // Function-tail slow-path blocks for tagged add/sub. - // Each jo site gets its own block. The block emits the 128-bit correction - // (e2_mw_slow_block), allocates 16 bytes, writes the 2-limb object, stores - // the pointer in the value slot, sets tag = 1, and jumps back after the - // fast-path store. jo records are emitted in site order. Functions without - // jo sites emit no bytes. - if g_x86_mw_jo_count > 0: - mw_ji : ., mut = 0 - loop: - if mw_ji >= g_x86_mw_jo_count: - break - jo_pos := r64(g_x86_mw_jo_pos, mw_ji * 8) - jo_d := r64(g_x86_mw_jo_dest, mw_ji * 8) - jo_sub := r64(g_x86_mw_jo_is_sub, mw_ji * 8) - jo_res := r64(g_x86_mw_jo_resume, mw_ji * 8) - mw_blk := cp - w32(buf, jo_pos + 2, mw_blk - (jo_pos + 6)) - cp = cp + e2_mw_slow_block(buf, cp, jo_d, jo_sub, jo_res) - mw_ji = mw_ji + 1 - g_x86_mw_jo_count = 0 - // Function-tail 2L operand blocks, after the jo blocks. - // Each consumer site gets its own block (e2_mw_opnd_block). The fast-path tag - // check reaches it with jne rel32. The block does 128-bit arithmetic or - // comparison and a common result store. Its shape is specialized statically - // by whether operand rows are tagged. Blocks that allocate use the same alloc - // patch table as the function body. No consumer sites means no bytes. - if g_x86_mw_oc_count > 0: - mw_oi : ., mut = 0 - loop: - if mw_oi >= g_x86_mw_oc_count: - break - oc_p1 := r64(g_x86_mw_oc_pos1, mw_oi * 8) - oc_p2 := r64(g_x86_mw_oc_pos2, mw_oi * 8) - oc_s1 := r64(g_x86_mw_oc_s1, mw_oi * 8) - oc_s2 := r64(g_x86_mw_oc_s2, mw_oi * 8) - oc_d := r64(g_x86_mw_oc_dest, mw_oi * 8) - oc_op := r64(g_x86_mw_oc_op, mw_oi * 8) - oc_res := r64(g_x86_mw_oc_resume, mw_oi * 8) - oc_blk := cp - if oc_p1 >= 0: - w32(buf, oc_p1 + 2, oc_blk - (oc_p1 + 6)) - if oc_p2 >= 0: - w32(buf, oc_p2 + 2, oc_blk - (oc_p2 + 6)) - cp = cp + e2_mw_opnd_block(buf, cp, oc_s1, oc_s2, oc_d, oc_op, oc_res) - mw_oi = mw_oi + 1 - g_x86_mw_oc_count = 0 return cp diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 39951261..5b3c28a4 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -15,12 +15,6 @@ fn g2_init(): g_x86_emit_var_count = 0 g_x86_emit_stack_size = 0 g_x86_ret_patch_count = 0 - // g_x86_mw_jo_count: reset per function. jo rel32s are patched to the - // function-tail slow-path block right after each function's epilogue. - g_x86_mw_jo_count = 0 - // Reset per function. 2L operand checks (jne 0F 85) are patched to the - // function-tail 2L block. - g_x86_mw_oc_count = 0 // g_x86_alloc_patch_count NOT reset: alloc calls are patched after all funcs. // g_x86_ext_rel_count NOT reset: extern relocations span all functions. // g_x86_rip_patch_count NOT reset @@ -101,7 +95,7 @@ fn g2_rodata_sz() -> int: i = i + 1 return o -// Tagged-slot helpers and the 2L encoder live in tag2l.cr. Call sequences live +// Tagged mathematical-int helpers live in int_rep.cr. Call sequences live // in src/os/linux/callseq.cr; syscall stubs live in src/os/linux/syscall.cr. // This file keeps the e2_* primitives, emit_instr dispatch, and builtin bodies. // ── Byte encoding helpers ── @@ -773,37 +767,10 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: return cp cp = cp + e2_load_var(buf, pos+cp, 10, s1) cp = cp + e2_load_var(buf, pos+cp, 11, s2) - // Int multiword tagged-slot check. At 2L consumer sites (add/sub with a - // tagged dest, or a comparison with any tagged operand), check tags before - // the fast path. If any operand is 2L (tag = 1), jump to the function-tail - // 2L block for 128-bit arithmetic or comparison (e2_mw_opnd_block). - // Untagged rows and sites emit no bytes. The check runs after operand - // loads and only writes flags, so it does not disturb r10 or r11. - mw_oc : ., mut = -1 - if (s3 == OP_ADD || s3 == OP_SUB) && g2_tag_off(d) != -1 || (s3 >= OP_EQ && s3 <= OP_GE): - if g2_tag_off(s1) != -1: - if mw_oc == -1: - mw_oc = mw_oc_new(s1, s2, d, s3) - cp = cp + e2_mw_oc_check(buf, pos+cp, mw_oc, 0, s1) - if s1 != s2 && g2_tag_off(s2) != -1: - if mw_oc == -1: - mw_oc = mw_oc_new(s1, s2, d, s3) - cp = cp + e2_mw_oc_check(buf, pos+cp, mw_oc, 1, s2) if s3 == OP_ADD: cp = cp + e2_alu(buf, pos+cp, 1) - // A tagged dest add/sub is followed by an overflow jump. e2_alu's OF - // is consumed immediately because the next instruction would overwrite - // flags. The slow-path block at the function tail stores the dest value, - // sets tag = 1, and jumps back after the store (e2_mw_slow_block in - // tag2l.cr; frame.cr pf_epilogue emits the block per site). The site - // records dest and whether the op is subtract, because the high-limb - // correction differs. - if g2_tag_off(d) != -1: - cp = cp + e2_mw_jo(buf, pos+cp, d, 0) else if s3 == OP_SUB: cp = cp + e2_alu(buf, pos+cp, 41) - if g2_tag_off(d) != -1: - cp = cp + e2_mw_jo(buf, pos+cp, d, 1) else if s3 == OP_MUL: // imul r10, r11 — 2-byte opcode 0x0F 0xAF cp = cp + emit_rex(buf, pos+cp, 1, 10/8, 0, 11/8) @@ -912,13 +879,6 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: else if s3 == OP_OR: cp = cp + e2_alu(buf, pos+cp, 9) cp = cp + e2_st(buf, pos+cp, 10, do2) - // Tag hygiene 1: clear the tag at every fast-path 64-bit definition. - // A fast value stored into a tagged slot must have tag = 0. Otherwise a - // later backedge execution would see stale tag = 1 and read the fast value - // as a 2-limb pointer. The jo and 2L blocks jump to resume after this - // instruction; the slow path writes tag = 1 itself, so this clear does not - // affect it. An untagged dest emits no bytes. - cp = cp + e2_mw_tag_clr(buf, pos+cp, d) return cp if op == IR_UNARY && d >= 0: @@ -1558,27 +1518,27 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 10%8) cp = cp + e2_st(buf, pos+cp, 10, do2) - // 全局源(恒 64 位值)-> d tagged 时防御性清 tag - cp = cp + e2_mw_tag_clr(buf, pos+cp, d) else: cp = cp + e2_load_var(buf, pos+cp, 10, s1) cp = cp + e2_st(buf, pos+cp, 10, do2) - // 拷贝定值 tag 传播(源 tagged -> 运行时 tag 拷贝;源 untagged - // -> 清 0),d 为 as 转换或拷贝序列中的记录(规则 B 闭包成员) - cp = cp + e2_mw_tag_cpy(buf, pos+cp, d, s1) else: cp = cp + e2_load_var(buf, pos+cp, 10, s1) cp = cp + e2_st(buf, pos+cp, 10, do2) - // s1<0 尾分支(域外行,现 IR 形态防御后备):mw_setup_tags 规则 B 只扫 - // 函数域 [vs, ve)(s1 ∈ 域 且 tagged 才标 d)-> d 恒 untagged,快值原样 - // 直拷,无 e2_mw_tag_cpy/清 0 字节(tag 拷贝只对有 tag 字节的域内行发)。 return cp if op == IR_STORE: o1 := g2_slot(s1) + // @raw_int 的跨表示边界:dex 的 TI_DEX_S 槽是 raw 缩放整数, + // 写入 tagged int 槽前必须装箱;否则后续 int 运算会把它当成 tagged + // 立即数(例如 7_000_000 被读成 3_500_000)。 + needs_big_box := g_x86_big_enabled != 0 && irv_type(s1) == TI_INT && irv_type(s2) == TI_DEX_S if s1 >= 0: if r64(g_x86_is_global, s1 * 8) != 0: cp = cp + e2_load_var(buf, pos+cp, 10, s2) + if needs_big_box != 0: + cp = cp + e2_mov(buf, pos+cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_mov(buf, pos+cp, 10, 0) grow_rip_patch(g_x86_rip_patch_count + 1) w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) @@ -1591,16 +1551,18 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + emit_modrm(buf, pos+cp, 0, 10%8, 11%8) else: cp = cp + e2_load_var(buf, pos+cp, 10, s2) + if needs_big_box != 0: + cp = cp + e2_mov(buf, pos+cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_mov(buf, pos+cp, 10, 0) cp = cp + e2_st(buf, pos+cp, 10, o1) - // 拷贝定值 tag 传播(规则 B' 闭包成员:s2 tagged -> 运行时 tag - // 拷贝;untagged/全局源 -> 清 0),变量行真定值 - cp = cp + e2_mw_tag_cpy(buf, pos+cp, s1, s2) else: cp = cp + e2_load_var(buf, pos+cp, 10, s2) + if needs_big_box != 0: + cp = cp + e2_mov(buf, pos+cp, 7, 10) + cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) + cp = cp + e2_mov(buf, pos+cp, 10, 0) cp = cp + e2_st(buf, pos+cp, 10, o1) - // s1<0 尾分支(域外目标行,现 IR 形态防御后备):规则 B' 只标函数域内 - // [vs, ve) 的 s1(且 s2 tagged)-> 域外 s1 恒 untagged,快值原样直拷, - // 无 tag 拷贝/清 0(g2_tag_off 域外恒 -1,拷贝源 s2 的 tag 无需实现)。 return cp if op == IR_LOAD_FIELD && d >= 0: diff --git a/src/arch/x86_64/sizes.cr b/src/arch/x86_64/sizes.cr index 17358206..c86ad946 100644 --- a/src/arch/x86_64/sizes.cr +++ b/src/arch/x86_64/sizes.cr @@ -5,12 +5,9 @@ // them for prologue and epilogue accounting. emit_instr must return the same // sizes through the e2_* helpers. // -// Do not hardcode byte counts in elf.cr, instr.cr, tag2l.cr, resolve.cr, -// frame.cr, or src/os/linux/entry.cr. sz_start_body and sz_start_argv_save must -// stay in sync with the _start body in entry.cr. -// -// The tag2l.cr mw helpers currently return matching literal sizes but do not -// call these helpers; changing sz_jo does not propagate to e2_mw_jo. +// Do not hardcode byte counts in elf.cr, instr.cr, resolve.cr, frame.cr, or +// src/os/linux/entry.cr. sz_start_body and sz_start_argv_save must stay in sync +// with the _start body in entry.cr. // ── Basic encoding sizes (one per e2_* helper in instr.cr) ── fn sz_mov() -> int: diff --git a/src/arch/x86_64/tag2l.cr b/src/arch/x86_64/tag2l.cr deleted file mode 100644 index 43d582f5..00000000 --- a/src/arch/x86_64/tag2l.cr +++ /dev/null @@ -1,650 +0,0 @@ -// === tag2l.cr === -// Tagged int slots and the 2L (two-limb) encoder for x86-64. -// -// A fast int var stores a 64-bit value or a pointer to a 2-limb heap object. -// Potential multiword vars get one tag byte in the frame. The slot offset from -// g2_slot is unchanged; the tag area is below the slot area, and tag byte k is -// at -(vc*8 + 1 + k) relative to rbp. Functions without tagged vars have no tag -// area, so their frame layout is unchanged. -// -// mw_setup_tags scans the function and marks: -// A. dest of IR_BINARY(OP_ADD/OP_SUB), the only >64-bit producers; -// B. IR_LOAD d <- s1 when s1 is tagged (as-conversion copies); -// B'. IR_STORE rho(s1) := rho(s2) when s2 is tagged (assignment copies). -// The mark set is a fixed point because tags can travel around loop backedges. -// Marking is monotone, so it converges in at most vc + 1 passes. -// -// Tag reads and writes go through g2_tag_off, defined in frame.cr. The tag table -// is owned by this file. frame.cr calls e2_mw_slow_block and e2_mw_opnd_block -// from the function epilogue; this file calls g2_tag_off and pf_frame_size. -// -// Boundaries: 2-limb values do not cross function boundaries, are not stored to -// globals, heap, or arrays, and do not enter MUL, DIV, SHL, unary neg, or other -// unimplemented sites. Tests avoid those shapes. -// -// Register allocation is not excluded for tagged vars. A slot or register holds -// either a fast 64-bit value or a 2-limb heap pointer; the 128-bit payload is on -// the heap and the tag is a frame byte. All value reads and writes use g2_slot, -// and all tag reads and writes use g2_tag_off. - -// Scan function fi and build tag byte offsets for its var domain [vs, vs+vc). -// This is pure IR work: the input is the final IR after .ccr loading, with no -// format or cross-process state. The tag model and the recognition rules must -// cover the same rows. The four tag writers are: -// 1. add/sub fast path clears the tag after storing the value; -// 2. IR_STORE and IR_LOAD copy or clear the tag; -// 3. prologue parameter saves clear it; -// 4. the 2L slow path writes tag = 1. -// Rules A, B, and B' mark exactly the rows these four sites can write. No frame -// entry clear is needed. -fn mw_setup_tags(fi: int, vs: int, vc: int): - grow_mw_tag_off(vc) - z : ., mut = 0 - loop: - if z >= vc: - break - w64(g_x86_mw_tag_off, z * 8, -1) - z = z + 1 - g_x86_mw_tag_count = 0 - if vc <= 0: - return - ic := r64(g_ir_func_instr_count, fi * 8) - ist := r64(g_ir_func_instr_start, fi * 8) - ve := vs + vc - // First pass: fixed-point scan of rules A, B, and B'. The destination and - // targets must be TI_INT vars in the function domain: - // A. IR_BINARY(ADD/SUB) marks dest; - // B. IR_LOAD d <- s1 marks d when s1 is marked; - // B'. IR_STORE rho(s1) := rho(s2) marks s1 when s2 is marked. - chg : ., mut = 1 - ii : ., mut = 0 - loop: - if chg == 0: - break - chg = 0 - ii = 0 - loop: - if ii >= ic: - break - ino := ist + ii - op := iri_op(ino) - if op == IR_BINARY: - d := iri_dest(ino) - if d >= vs && d < ve && irv_type(d) == TI_INT: - s3 := iri_s3(ino) - if (s3 == OP_ADD || s3 == OP_SUB) && r64(g_x86_mw_tag_off, (d - vs) * 8) == -1: - w64(g_x86_mw_tag_off, (d - vs) * 8, 0) // 0 = 已标记(占位) - chg = 1 - else if op == IR_LOAD: - d := iri_dest(ino) - if d >= vs && d < ve && irv_type(d) == TI_INT: - s1 := iri_s1(ino) - if s1 >= vs && s1 < ve && irv_type(s1) == TI_INT && r64(g_x86_mw_tag_off, (s1 - vs) * 8) != -1 && r64(g_x86_mw_tag_off, (d - vs) * 8) == -1: - w64(g_x86_mw_tag_off, (d - vs) * 8, 0) - chg = 1 - else if op == IR_STORE: - s1 := iri_s1(ino) - if s1 >= vs && s1 < ve && irv_type(s1) == TI_INT: - s2 := iri_s2(ino) - if s2 >= vs && s2 < ve && irv_type(s2) == TI_INT && r64(g_x86_mw_tag_off, (s2 - vs) * 8) != -1 && r64(g_x86_mw_tag_off, (s1 - vs) * 8) == -1: - w64(g_x86_mw_tag_off, (s1 - vs) * 8, 0) - chg = 1 - ii = ii + 1 - // Second pass: allocate tag bytes in ascending var order. The k-th tagged - // var is at rbp - (vc*8 + k). - cnt : ., mut = 0 - k : ., mut = 0 - loop: - if k >= vc: - break - if r64(g_x86_mw_tag_off, k * 8) != -1: - cnt = cnt + 1 - w64(g_x86_mw_tag_off, k * 8, -(vc * 8 + cnt)) - k = k + 1 - g_x86_mw_tag_count = cnt - -// Overflow check emission for the fast path. -// Emit jo for IR_BINARY add/sub when the dest is a tagged var in the current -// function. g2_tag_off returns -1 for globals, other functions, dest=-1, and -// copy rows, so untagged int arithmetic and dex (SSE branch) emit no bytes. -// This function is also the table-path exclusion gate: comparisons OP_EQ..OP_GE -// with any tagged operand need the 2L check, which the HIT table path does not -// model, so the whole instruction falls back to emit_instr. Add/sub tagged -// operands are covered by the tagged dest (rule A guarantees that a tagged -// operand implies a tagged dest). -fn mw_int_arith_jo_needed(instr_idx: int) -> int: - if iri_op(instr_idx) != IR_BINARY: - return 0 - s3 := iri_s3(instr_idx) - if s3 >= OP_EQ && s3 <= OP_GE: - s1 := iri_s1(instr_idx) - if g2_tag_off(s1) != -1: - return 1 - s2 := iri_s2(instr_idx) - if s1 != s2 && g2_tag_off(s2) != -1: - return 1 - return 0 - if s3 != OP_ADD && s3 != OP_SUB: - return 0 - if g2_tag_off(iri_dest(instr_idx)) == -1: - return 0 - return 1 - -// Emit jo rel32 (0F 80, 6 bytes) to the slow-path block. The block is placed at -// the function tail after epilogue, so its position is not known yet. Record -// pos, dest, and is_sub in g_x86_mw_jo_*; pf_epilogue emits the block and patches -// the rel32. elf.cr writes the resume offset after this instruction. -// rel32 is required because the block is at the function tail and the body can -// exceed the rel8 range. -fn e2_mw_jo(b: string, p: int, d: int, is_sub: int) -> int: - grow_mw_jo_patch(g_x86_mw_jo_count + 1) - w64(g_x86_mw_jo_pos, g_x86_mw_jo_count * 8, p) - w64(g_x86_mw_jo_dest, g_x86_mw_jo_count * 8, d) - w64(g_x86_mw_jo_is_sub, g_x86_mw_jo_count * 8, is_sub) - g_x86_mw_jo_count = g_x86_mw_jo_count + 1 - w8(b, p, 15) - w8(b, p + 1, 128) // 0F 80 = jo rel32 - e2_w32(b, p + 2, 0) // 占位,frame.cr pf_epilogue 块位置已知后回填 - return 6 - -// Slow path block: one per site, appended after the function epilogue. -// Site state on entry: -// - r10 = wrapped 64-bit add/sub result (low limb); -// - OF = 1; CF still holds the e2_alu result; -// - rsp is 16-byte aligned (the site is inside IR_BINARY emission, with no call -// stack arguments in flight). -// Only caller-saved registers are touched (rax, rcx, rdx, rdi, r10, r11), and -// push/pop keep the block balanced. Callee-saved registers used by O1/O2 -// allocation (rbx, r12-r15) pass through unchanged. -// -// 128-bit correction: -// add: high limb = CF ? -1 : 0; -// sub: high limb = CF ? 0 : -1. -// The wrapped low limb stays in r10. The implementation forms -CF with -// sbb r11,r11 and adds not r11 for sub. -// Boundary check: a = b = -2^63 for add gives L = 0, CF = 1, so [lo=0, hi=-1]. -fn e2_mw_slow_block(b: string, p: int, d: int, is_sub: int, resume: int) -> int: - cp : ., mut = 0 - // 1. 高 limb -> r11(add:sbb r11,r11 = -CF;sub:+not -> CF?0:-1)。 - // 须为块内首操作,CF 其后随时可能被写。 - w8(b, p + cp, 77) - w8(b, p + cp + 1, 25) - w8(b, p + cp + 2, 219) - cp = cp + 3 // sbb r11, r11 (4D 19 DB) - if is_sub != 0: - w8(b, p + cp, 73) - w8(b, p + cp + 1, 247) - w8(b, p + cp + 2, 211) - cp = cp + 3 // not r11 (49 F7 D3, REX.WB: rm=r11 需 B 位) - // 2. 保存 lo/hi(alloc 调用会摧毁 caller-saved r10/r11):两 push 后 - // rsp == 0 (mod 16),满足 SysV call 对齐(进入块时 rsp == 0)。 - w8(b, p + cp, 65) - w8(b, p + cp + 1, 82) - cp = cp + 2 // push r10 (41 52) - w8(b, p + cp, 65) - w8(b, p + cp + 1, 83) - cp = cp + 2 // push r11 (41 53) - // 3. alloc(16),16B 2-limb 对象;与 IR_ALLOC_* 同款补丁表注册 - // (g_x86_alloc_patch_count 逐函数不重置,函数尾块与函数体一起回填)。 - // mov edi, 16(BF imm32,16 为合法 imm32) - w8(b, p + cp, 191) - e2_w32(b, p + cp + 1, 16) - cp = cp + 5 - grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, p + cp) - g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 - w8(b, p + cp, 232) - e2_w32(b, p + cp + 1, 0) - cp = cp + 5 // call alloc(占位回填) - // 4. 恢复 lo/hi - w8(b, p + cp, 65) - w8(b, p + cp + 1, 91) - cp = cp + 2 // pop r11 (41 5B) - w8(b, p + cp, 65) - w8(b, p + cp + 1, 90) - cp = cp + 2 // pop r10 (41 5A) - // 5. 写 2-limb 对象(rax = alloc 返回的数据指针):[+0] = lo(环绕值)、 - // [+8] = hi(符号扩展修正)。注意 REX:rm=rax 无需 B 位,仅 R(源 - // r10/r11)-> 4C(对照 elf.cr alloc 内 mov [rax], r9 = 4C 89 08)。 - w8(b, p + cp, 76) - w8(b, p + cp + 1, 137) - w8(b, p + cp + 2, 16) - cp = cp + 3 // mov [rax], r10 (4C 89 10) - w8(b, p + cp, 76) - w8(b, p + cp + 1, 137) - w8(b, p + cp + 2, 88) - w8(b, p + cp + 3, 8) - cp = cp + 4 // mov [rax+8], r11 (4C 89 58 08) - // 6. 值槽存指针(跳过快路径 e2_st,本块自回存 dest;g2_slot 含 - // O1/O2 reg 形态:E2_REG_SLOT_BASE 编码 -> e2_mov reg, rax) - cp = cp + e2_st(b, p + cp, 0, g2_slot(d)) - // 7. tag 字节置位(g2_tag_off(d) != -1 由 jo 发射条件保证):槽内现为 - // 2-limb 指针 当且仅当 tag = 1(tag 不变量慢路径侧)。 - tago := g2_tag_off(d) - if tago >= -128 && tago <= 127: - w8(b, p + cp, 198) - w8(b, p + cp + 1, 69) - w8(b, p + cp + 2, tago) - w8(b, p + cp + 3, 1) - cp = cp + 4 - else: - w8(b, p + cp, 198) - w8(b, p + cp + 1, 133) - e2_w32(b, p + cp + 2, tago) - w8(b, p + cp + 6, 1) - cp = cp + 7 - // 8. 跳回快路径 store 之后(resume = 该站点指令尾,elf.cr 填) - w8(b, p + cp, 233) // E9 jmp rel32 - e2_w32(b, p + cp + 1, resume - (p + cp + 5)) - return cp + 5 - -// Tag hygiene: write tags at definition points, with no frame-entry clear. -// Every definition of a tagged row belongs to one of four sets: -// 1. add/sub fast path stores a value and clears tag to 0; -// 2. IR_STORE and IR_LOAD copy the source tag, or clear it when the source is -// untagged; -// 3. prologue parameter saves clear the tag; -// 4. 2-limb definitions write a pointer and set tag = 1. -// User vars are defined only by IR_STORE. Constants and arithmetic go through a -// temporary row first. IR_CONST, IR_UNARY, IR_CALL, and similar dests are -// single-use temporaries outside the tag closure, so the four sets are complete. -// -// Consumers: -// A. IR_RETURN dereferences a 2L pointer and returns the low limb in rax. -// B. Integer comparisons OP_EQ..OP_GE with a tagged operand use a 128-bit -// comparison: signed high limb, then unsigned low limb. -// C. Add/sub chains with a tagged operand use 128-bit arithmetic. If the result -// fits in 64 bits, it is stored as a fast value with tag 0; otherwise a new -// 2-limb object is allocated and tag = 1. The result bound is below 2^66, so -// the 128-bit domain is not exceeded. -// D. Not implemented: 2L operands in MUL, DIV, SHL, unary neg, or shifts; -// writes to globals, heap, or arrays; function boundaries. Tests avoid these -// shapes. -// ══════════════════════════════════════════════════════════════ - -// 字节级小助手(rbp 相对偏移,disp8/disp32 自动): -// test byte [rbp+off], 0xFF,F6 /0(ZF 当且仅当字节 == 0;只写标志) -fn e2_mw_t8(b: string, p: int, off: int) -> int: - if off >= -128 && off <= 127: - w8(b, p, 246) - w8(b, p+1, 69) - w8(b, p+2, off) - w8(b, p+3, 255) - return 4 - w8(b, p, 246) - w8(b, p+1, 133) - e2_w32(b, p+2, off) - w8(b, p+6, 255) - return 7 -// mov byte [rbp+off], imm,C6 /0 -fn e2_mw_b8(b: string, p: int, off: int, imm: int) -> int: - if off >= -128 && off <= 127: - w8(b, p, 198) - w8(b, p+1, 69) - w8(b, p+2, off) - w8(b, p+3, imm) - return 4 - w8(b, p, 198) - w8(b, p+1, 133) - e2_w32(b, p+2, off) - w8(b, p+6, imm) - return 7 -// mov al, [rbp+off],8A /0(rax 低字节 scratch,发射点 rax 无 live) -fn e2_mw_ld8(b: string, p: int, off: int) -> int: - if off >= -128 && off <= 127: - w8(b, p, 138) - w8(b, p+1, 69) - w8(b, p+2, off) - return 3 - w8(b, p, 138) - w8(b, p+1, 133) - e2_w32(b, p+2, off) - return 6 -// mov [rbp+off], al,88 /0 -fn e2_mw_st8(b: string, p: int, off: int) -> int: - if off >= -128 && off <= 127: - w8(b, p, 136) - w8(b, p+1, 69) - w8(b, p+2, off) - return 3 - w8(b, p, 136) - w8(b, p+1, 133) - e2_w32(b, p+2, off) - return 6 - -// 定值点 tag 清 0:变量 v 的槽刚被 64 位快值定值(v tagged 时发 mov byte 0)。 -fn e2_mw_tag_clr(b: string, p: int, v: int) -> int: - tg := g2_tag_off(v) - if tg == -1: - return 0 - return e2_mw_b8(b, p, tg, 0) - -// 拷贝定值 tag 传播:dst 的槽 ← src 的值(拷贝后 dst 的 tag = src 的 tag; -// src 无 tag 字节(恒快)-> dst 清 0)。dst untagged -> 零字节。 -fn e2_mw_tag_cpy(b: string, p: int, dst: int, src: int) -> int: - dtg := g2_tag_off(dst) - if dtg == -1: - return 0 - stg := g2_tag_off(src) - if stg == -1: - return e2_mw_b8(b, p, dtg, 0) - c : ., mut = e2_mw_ld8(b, p, stg) - c = c + e2_mw_st8(b, p + c, dtg) - return c - -// ── 消费者站点记录(oc = operand-check):位置后知(函数尾块),jo 同款 ── -fn mw_oc_new(s1: int, s2: int, d: int, op: int) -> int: - grow_mw_oc_patch(g_x86_mw_oc_count + 1) - w64(g_x86_mw_oc_pos1, g_x86_mw_oc_count * 8, -1) - w64(g_x86_mw_oc_pos2, g_x86_mw_oc_count * 8, -1) - w64(g_x86_mw_oc_s1, g_x86_mw_oc_count * 8, s1) - w64(g_x86_mw_oc_s2, g_x86_mw_oc_count * 8, s2) - w64(g_x86_mw_oc_dest, g_x86_mw_oc_count * 8, d) - w64(g_x86_mw_oc_op, g_x86_mw_oc_count * 8, op) - g_x86_mw_oc_count = g_x86_mw_oc_count + 1 - return g_x86_mw_oc_count - 1 - -// 站点检查:test tag(v), 0xFF;jne rel32(0F 85)-> 该站点函数尾 2L 块。 -// rel32 恒选:块附函数尾,函数体 >127B 时 rel8 不可达(jo 同理由)。chk = -// 0/1 -> pos1/pos2(站点至多 2 个检查,s1、s2 各行一个,s1==s2 只发 1 个)。 -// 记录位置 = jne 起始(p+c,test 前缀 4/7B 后;rel32 字段在 jne 起 +2, -// 若误记 test 起,回填会覆写 test 的 disp/imm 字节)。 -fn e2_mw_oc_check(b: string, p: int, oc: int, chk: int, v: int) -> int: - c : ., mut = e2_mw_t8(b, p, g2_tag_off(v)) - if chk == 0: - w64(g_x86_mw_oc_pos1, oc * 8, p + c) - else: - w64(g_x86_mw_oc_pos2, oc * 8, p + c) - w8(b, p + c, 15) - w8(b, p + c + 1, 133) - e2_w32(b, p + c + 2, 0) - return c + 6 - -// ── 2L 操作数装载(解 2-limb 对象到寄存器对)── -// regs: A = (r8=lo, r9=hi),B = (r10=lo, r11=hi);rax = 指针 scratch; -// 快值 -> hi = sar 63 符号扩展(两补 128 语义)。快值载入 r8/r10;2L = 槽值 -// (指针)入 rax 后解 [rax+0]/[rax+8](对象布局 [+0]=lo [+8]=hi)。 -// mov r8, [rax] 4C 8B 00 mov r9, [rax+8] 4C 8B 48 08 -// mov r10, [rax] 4C 8B 10 mov r11, [rax+8] 4C 8B 58 08 -fn e2_mw_ld2(b: string, p: int, lo_r: int, hi_r: int) -> int: - w8(b, p, 76) - w8(b, p+1, 139) - w8(b, p+2, (lo_r % 8) * 8) // mov r{lo}, [rax] - w8(b, p+3, 76) - w8(b, p+4, 139) - w8(b, p+5, 64 + (hi_r % 8) * 8) - w8(b, p+6, 8) // mov r{hi}, [rax+8] - return 7 -// mov r{hi}, r{lo}; sar r{hi}, 63,快值 64->128 符号扩展(hi = sext) -fn e2_mw_sext(b: string, p: int, lo_r: int, hi_r: int) -> int: - c : ., mut = e2_mov(b, p, hi_r, lo_r) - w8(b, p+c, 73) - w8(b, p+c+1, 193) // REX.WB + C1 /7 - w8(b, p+c+2, 248 + (hi_r % 8)) // modrm 11 111 rm(/7 = SAR) - w8(b, p+c+3, 63) - return c + 4 - -// 2L operand block: one per consumer site, after the jo blocks at the function -// tail. -// Entry: at least one operand is 2L, reached by the fast-path tag check. All -// operands are reloaded; r10/r11 values from the fast path are discarded. rsp is -// 16-byte aligned, and the only call (alloc) is balanced by push/pop. Only -// caller-saved registers are touched. -// Static specialization: untagged operands are known fast, so their tag test and -// 2L load are omitted. For two tagged operands, test s1: fast A means B is 2L; -// 2L A then tests s2. If s1 == s2, both tests are the same byte and the entry -// condition makes the branch self-consistent. -// Internal jumps are rel8. Cross-block jumps (tag check jne to the block, block -// tail jmp to resume) are rel32. -fn e2_mw_opnd_block(b: string, p: int, s1: int, s2: int, d: int, op: int, resume: int) -> int: - cp : ., mut = 0 - t1 := g2_tag_off(s1) - t2 : ., mut = t1 - if s1 != s2: - t2 = g2_tag_off(s2) - is_cmp : ., mut = 0 - if op >= OP_EQ && op <= OP_GE: - is_cmp = 1 - is_sub : ., mut = 0 - if is_cmp == 0 && op == OP_SUB: - is_sub = 1 - - if t1 != -1 && t2 != -1: - // ── 双 tagged 分派:测 t1,0 -> A 快 + B 必 2L;1 -> A 2L 再测 t2 ── - cp = cp + e2_mw_t8(b, p+cp, t1) - j_a2l : ., mut = cp - w8(b, p+cp, 117) - w8(b, p+cp+1, 0) - cp = cp + 2 // jne L_a2l - // A 快 - cp = cp + e2_load_var(b, p+cp, 8, s1) - cp = cp + e2_mw_sext(b, p+cp, 8, 9) - // B 必 2L - cp = cp + e2_load_var(b, p+cp, 0, s2) - cp = cp + e2_mw_ld2(b, p+cp, 10, 11) - j_op1 : ., mut = cp - w8(b, p+cp, 235) - w8(b, p+cp+1, 0) - cp = cp + 2 // jmp L_op - // L_a2l:A 2L - w8(b, p + j_a2l + 1, cp - (j_a2l + 2)) - cp = cp + e2_load_var(b, p+cp, 0, s1) - cp = cp + e2_mw_ld2(b, p+cp, 8, 9) - cp = cp + e2_mw_t8(b, p+cp, t2) - j_both : ., mut = cp - w8(b, p+cp, 117) - w8(b, p+cp+1, 0) - cp = cp + 2 // jne L_both - // B 快 - cp = cp + e2_load_var(b, p+cp, 10, s2) - cp = cp + e2_mw_sext(b, p+cp, 10, 11) - j_op2 : ., mut = cp - w8(b, p+cp, 235) - w8(b, p+cp+1, 0) - cp = cp + 2 // jmp L_op - // L_both:B 2L - w8(b, p + j_both + 1, cp - (j_both + 2)) - cp = cp + e2_load_var(b, p+cp, 0, s2) - cp = cp + e2_mw_ld2(b, p+cp, 10, 11) - // L_op = 当前 cp,回填两个 jmp - w8(b, p + j_op1 + 1, cp - (j_op1 + 2)) - w8(b, p + j_op2 + 1, cp - (j_op2 + 2)) - else if t1 != -1: - // 仅 s1 tagged:入口即 s1 2L,A 2L,B 恒快 - cp = cp + e2_load_var(b, p+cp, 0, s1) - cp = cp + e2_mw_ld2(b, p+cp, 8, 9) - cp = cp + e2_load_var(b, p+cp, 10, s2) - cp = cp + e2_mw_sext(b, p+cp, 10, 11) - else: - // 仅 s2 tagged:入口即 s2 2L,A 恒快,B 2L - cp = cp + e2_load_var(b, p+cp, 8, s1) - cp = cp + e2_mw_sext(b, p+cp, 8, 9) - cp = cp + e2_load_var(b, p+cp, 0, s2) - cp = cp + e2_mw_ld2(b, p+cp, 10, 11) - - // ── L_op:128 位运算(A=(r8,r9) B=(r10,r11))── - if is_cmp == 0: - // 算术:结果 (r10 = lo, r11 = hi) - if is_sub == 0: - w8(b, p+cp, 77) - w8(b, p+cp+1, 1) - w8(b, p+cp+2, 194) - cp = cp + 3 // add r10, r8(lo + CF) - w8(b, p+cp, 77) - w8(b, p+cp+1, 17) - w8(b, p+cp+2, 203) - cp = cp + 3 // adc r11, r9(高位进位处理) - else: - w8(b, p+cp, 77) - w8(b, p+cp+1, 41) - w8(b, p+cp+2, 208) - cp = cp + 3 // sub r8, r10(lo,CF=借位) - w8(b, p+cp, 76) - w8(b, p+cp+1, 137) - w8(b, p+cp+2, 216) - cp = cp + 3 // mov rax, r11(暂存 bh) - w8(b, p+cp, 77) - w8(b, p+cp+1, 137) - w8(b, p+cp+2, 203) - cp = cp + 3 // mov r11, r9(ah) - w8(b, p+cp, 73) - w8(b, p+cp+1, 25) - w8(b, p+cp+2, 195) - cp = cp + 3 // sbb r11, rax(ah-bh-CF) - w8(b, p+cp, 77) - w8(b, p+cp+1, 137) - w8(b, p+cp+2, 194) - cp = cp + 3 // mov r10, r8(lo) - // 公共落值:fits-64 当且仅当 hi == signmask(lo) -> 降级快值;否则 2L 对象 - // 结果界:|加数| <= 2^64-1 + 2^65,所以 |和| < 2^66,远小于 2^127。 - // 128 位域内恒可表示,不需要错误路径。 - cp = cp + e2_mov(b, p+cp, 2, 10) // mov rdx, r10 - w8(b, p+cp, 72) - w8(b, p+cp+1, 193) - w8(b, p+cp+2, 250) - w8(b, p+cp+3, 63) - cp = cp + 4 // sar rdx, 63(signmask) - w8(b, p+cp, 73) - w8(b, p+cp+1, 57) - w8(b, p+cp+2, 211) - cp = cp + 3 // cmp r11, rdx (0x49 39 D3: REX.W+B, reg=rdx). 0x4D would - // encode r10 and compare r11 with r10, causing a false fit. - j_fit : ., mut = cp - w8(b, p+cp, 116) - w8(b, p+cp+1, 0) - cp = cp + 2 // je L_fit - // 2L 存储:alloc(16) + 2-limb 写 + 槽存指针 + tag=1(同慢路径块) - w8(b, p+cp, 65) - w8(b, p+cp+1, 82) - cp = cp + 2 // push r10 - w8(b, p+cp, 65) - w8(b, p+cp+1, 83) - cp = cp + 2 // push r11 - w8(b, p+cp, 191) - e2_w32(b, p+cp+1, 16) - cp = cp + 5 // mov edi, 16 - grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, p + cp) - g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 - w8(b, p+cp, 232) - e2_w32(b, p+cp+1, 0) - cp = cp + 5 // call alloc - w8(b, p+cp, 65) - w8(b, p+cp+1, 91) - cp = cp + 2 // pop r11 - w8(b, p+cp, 65) - w8(b, p+cp+1, 90) - cp = cp + 2 // pop r10 - w8(b, p+cp, 76) - w8(b, p+cp+1, 137) - w8(b, p+cp+2, 16) - cp = cp + 3 // mov [rax], r10 - w8(b, p+cp, 76) - w8(b, p+cp+1, 137) - w8(b, p+cp+2, 88) - w8(b, p+cp+3, 8) - cp = cp + 4 // mov [rax+8], r11 - cp = cp + e2_st(b, p+cp, 0, g2_slot(d)) // 值槽存指针 - cp = cp + e2_mw_b8(b, p+cp, g2_tag_off(d), 1) // tag = 1 - w8(b, p+cp, 233) - e2_w32(b, p+cp+1, resume - (p+cp+5)) - cp = cp + 5 // jmp rel32 -> resume - l_fit := cp - w8(b, p + j_fit + 1, l_fit - (j_fit + 2)) // 回填 je -> L_fit - cp = cp + e2_st(b, p+cp, 10, g2_slot(d)) // 降级:快值落槽 - cp = cp + e2_mw_tag_clr(b, p+cp, d) // tag = 0 - w8(b, p+cp, 233) - e2_w32(b, p+cp+1, resume - (p+cp+5)) - cp = cp + 5 // jmp rel32 -> resume - return cp - - // ── 比较:结果 0/1(128 位语义,高 limb 带符号、同则低 limb 无符号)── - if op == OP_EQ || op == OP_NE: - w8(b, p+cp, 77) - w8(b, p+cp+1, 49) - w8(b, p+cp+2, 208) - cp = cp + 3 // xor r8, r10(al^bl) - w8(b, p+cp, 77) - w8(b, p+cp+1, 49) - w8(b, p+cp+2, 217) - cp = cp + 3 // xor r9, r11(ah^bh) - w8(b, p+cp, 77) - w8(b, p+cp+1, 9) - w8(b, p+cp+2, 193) - cp = cp + 3 // or r9, r8(ZF 当且仅当 全等) - if op == OP_EQ: - w8(b, p+cp, 15) - w8(b, p+cp+1, 148) - w8(b, p+cp+2, 192) - cp = cp + 3 - // sete al - else: - w8(b, p+cp, 15) - w8(b, p+cp+1, 149) - w8(b, p+cp+2, 192) - cp = cp + 3 - // setne al - else: - // LT/GT/LE/GE:eax 缺省 0(假);决定性 jcc -> L_t(真)/L_d(假, - // 前向);hi 同 -> lo 无符号再判。布局: - // xor eax; cmphi; j_ht L_t; j_hf L_d; cmplo; j_lf L_d; - // L_t: mov eax,1; L_d: 落值(j_ht 落 L_t、其余落 L_d,全前向) - w8(b, p+cp, 49) - w8(b, p+cp+1, 192) - cp = cp + 2 // xor eax, eax - w8(b, p+cp, 77) - w8(b, p+cp+1, 57) - w8(b, p+cp+2, 217) - cp = cp + 3 // cmp r9, r11(hi 符号) - hi_t : ., mut = 124 - hi_f : ., mut = 127 // 缺省 LT/LE:jl -> 真、jg -> 假 - if op == OP_GT || op == OP_GE: - hi_t = 127 - hi_f = 124 - // GT/GE:jg -> 真、jl -> 假 - j_ht : ., mut = cp - w8(b, p+cp, hi_t) - w8(b, p+cp+1, 0) - cp = cp + 2 - j_hf : ., mut = cp - w8(b, p+cp, hi_f) - w8(b, p+cp+1, 0) - cp = cp + 2 - w8(b, p+cp, 77) - w8(b, p+cp+1, 57) - w8(b, p+cp+2, 208) - cp = cp + 3 // cmp r8, r10(lo 无符号) - lo_f : ., mut = 115 // 缺省 LT:jae(lo >=u -> 假) - if op == OP_LE: - lo_f = 119 - else if op == OP_GT: - lo_f = 118 - else if op == OP_GE: - lo_f = 114 - // LE: ja / GT: jbe / GE: jb(lo 不满足 -> 假,L_d 前向) - j_lf : ., mut = cp - w8(b, p+cp, lo_f) - w8(b, p+cp+1, 0) - cp = cp + 2 - // L_t: mov eax, 1(落入 L_d 共享落值,全前向布局) - l_t := cp - w8(b, p+cp, 184) - e2_w32(b, p+cp+1, 1) - cp = cp + 5 - // L_d = 当前 cp,回填(j_ht -> L_t;j_hf/j_lf -> L_d) - w8(b, p + j_ht + 1, l_t - (j_ht + 2)) - w8(b, p + j_hf + 1, cp - (j_hf + 2)) - w8(b, p + j_lf + 1, cp - (j_lf + 2)) - // L_d:movzx + 落值(0/1 恒快;dest 若静态 tagged 防御性清 0,结果行 - // 实际恒 untagged,见 mw_setup_tags 规则域) - w8(b, p+cp, 68) - w8(b, p+cp+1, 15) - w8(b, p+cp+2, 182) - w8(b, p+cp+3, 208) - cp = cp + 4 // movzx r10d, al - cp = cp + e2_st(b, p+cp, 10, g2_slot(d)) - cp = cp + e2_mw_tag_clr(b, p+cp, d) - w8(b, p+cp, 233) - e2_w32(b, p+cp+1, resume - (p+cp+5)) - cp = cp + 5 // jmp rel32 -> resume - return cp diff --git a/src/ci/run.sh b/src/ci/run.sh index d85e1519..4e1de414 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -188,12 +188,10 @@ case "$CI_JOB_NAME" in # ─── 判据网加固批(criteria-harden,2026-09-16):**弱判据改强 + 挂点扩容** ─── # 审计依据 = docs/superpowers/specs/2026-09-16-criteria-strength-audit.md §4(挂载成本序 # 低 → 中 → 高)与 §2 表 B(B1-B6 弱判据 → TODO #2026-09-16-22-#2026-09-16-26/#2026-09-16-28)。本批在 #2026-09-16-27 成本序下挂 - # **低/中成本档**(下列四档;各档时长实测见行内注);`test_mw_task1-6` 属**口径换代** - # (零 diff 腿的基线须在**改动前**编译器上产 ⇒ CI 参照物结构性不可得)⇒ **本批不挂**, - # 结论 + 替代口径登记于 tests/harness/ci_hook_allowlist.txt。 + # **低/中成本档**(下列四档;各档时长实测见行内注);旧 `test_mw_task1-6` + # 随 v9.1 默认 BigInt 切换删除(2-limb 路径已移除),不再有未挂档。 # 注:本批同时把四档的**弱判据改强**:#2026-09-16-22 事件 1-4 全字段模板(test_hit_table)、 - # #2026-09-16-23/#2026-09-16-28 慢路径块体逐指令模板 + 零 diff 腿指令边界锁步(test_mw_task2,#2026-09-16-28 因基线 - # 不可得仍不挂,见 allowlist)、#2026-09-16-24 dump 值域白名单(test_hit_table)、#2026-09-16-25 STR 段冷/暖 + # #2026-09-16-24 dump 值域白名单(test_hit_table)、#2026-09-16-25 STR 段冷/暖 # 前缀契约(test_cir_warm_path,已挂上行)、#2026-09-16-26 剔除面白名单 + 计数(test_ccr_types,已挂上行)。 python3 tests/selfhost/test_slice_bounds.py # 低(TODO #2026-09-11-13/#2026-09-16-27 点名档):F11 切片越界守卫回归钉(越界读/写/变量下标/空切片 trap + 合法访问);本批实测 1.4s(7/7) python3 tests/selfhost/test_hit_table.py # 中(allowlist:25 + 审计「漏检面最大」同族):HIT 表模式合成层——v2 walker/夹具拒绝面/事件注入逐字节对照 + **事件 1-4 全字段模板(本批 #2026-09-16-22 改强)** + dump 值域白名单(本批 #2026-09-16-24);本批实测 2.3s(24/24) diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index 553bdac5..dc7a20ce 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -439,13 +439,15 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: // would leave loop-carried assignments unchanged. if op2 == 6 && d2 >= 0: w64(g_ir_vals, d2 * 8, 0) - // IR_ALLOC - if op2 == 9 && t1 >= 0 && t2 >= 0: - w64(g_ir_vals, t1 * 8, r64(g_ir_vals, t2 * 8)) // IR_STORE + if op2 == 9 && t1 >= 0 && t2 >= 0: + v2 := r64(g_ir_vals, t2 * 8) + if g_interp_big != 0 && irv_type(t1) == TI_INT && irv_type(t2) == TI_DEX_S: + v2 = ir_interp_tag(v2) + w64(g_ir_vals, t1 * 8, v2) + // IR_LOAD if op2 == 10 && d2 >= 0 && t1 >= 0: w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) - // IR_LOAD // Aggregate operations: ALLOC_ARRAY, ALLOC_STRUCT, LOAD_INDEX, // STORE_INDEX, LOAD_FIELD, STORE_FIELD, and ADDR_INDEX. The callee path // must implement them too, or an aggregate global read inside a callee @@ -871,8 +873,11 @@ fn ir_interpret() -> int: w64(g_ir_vals, d * 8, @ptr_of(bp)) if op == 9: if s1 >= 0 && s2 >= 0: - w64(g_ir_vals, s1 * 8, r64(g_ir_vals, s2 * 8)) - // IR_STORE + v := r64(g_ir_vals, s2 * 8) + if g_interp_big != 0 && irv_type(s1) == TI_INT && irv_type(s2) == TI_DEX_S: + v = ir_interp_tag(v) + w64(g_ir_vals, s1 * 8, v) + // IR_LOAD if op == 10: if d >= 0: w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index d01d4441..d128f767 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -242,7 +242,7 @@ fn grow_ir_var_rep(needed: int): _dyncpy(g_ir_var_rep, g_ir_var_rep_cap * 8, nb) // 新槽填 -1(哨兵 = 无表示位),alloc 零初始化,不填则未写槽被读成 // 表示位 var 0(每个值都被判成有表示位,产生错误值且不报错;for example, the `None` initial value - // 的表示位被读成 var0 的当前值 0 = 裸值)。Use the same initialization pattern as the tag area in tag2l.cr. + // 的表示位被读成 var0 的当前值 0 = 裸值)。 z : ., mut = g_ir_var_rep_cap loop: if z >= nc: diff --git a/src/format/elf/elf.cr b/src/format/elf/elf.cr index 0658d52d..00b03be5 100644 --- a/src/format/elf/elf.cr +++ b/src/format/elf/elf.cr @@ -1690,14 +1690,6 @@ fn elf_gen(buf: string) -> int: vs2 := r64(g_ir_func_var_start, fi * 8) pc2 := r64(g_ir_func_param_count, fi * 8) - // Identify potential multiword vars and include the tag area in the - // frame size. With zero tags, pf_frame_size matches the layout without a - // tag area. - // - // Identify potential multiword vars and include the tag area in the frame - // size. With zero tags, pf_frame_size matches the layout without a tag - // area. - mw_setup_tags(fi, vs2, vc2) fsz := r64(g_x86_func_code_sz, fi * 8) // Frame size comes from frame.cr. SysV requires 16-byte alignment: after // a call rsp is 8 mod 16. With opt >= 1 there are 6 pushes (rbx, r12-r15, @@ -1841,12 +1833,8 @@ fn elf_gen(buf: string) -> int: break g2_slot(vs + vi) vi = vi + 1 - // Identify potential multiword vars and build the tag byte offset table - // (g2_tag_off). The frame size is pf_frame_size(vc), including the tag - // area, with the same 16-byte alignment as the Phase 2 dry-run. With zero - // tags, the layout is unchanged. - // SysV 16-byte alignment, the same rule as the dry-run. - mw_setup_tags(fi, vs, vc) + // Frame size uses the shared pf_frame_size; SysV 16-byte alignment is + // the same rule as the Phase 2 dry-run. g_x86_emit_stack_size = pf_frame_size(vc) // Init label state for single-pass backpatching (-1 = not yet seen) @@ -1875,36 +1863,17 @@ fn elf_gen(buf: string) -> int: if ii >= ic: break inst_idx := ist + ii - // HIT table mode: mapped ops go through emit_instr_tabled. A return - // of -1 (unmapped, unsupported shape, or no table) falls back to - // emit_instr. Tagged int add/sub needs an overflow jump (jo), which - // the event lowering does not model, so the whole instruction takes - // the fallback path. - jo_before := g_x86_mw_jo_count - oc_before := g_x86_mw_oc_count sz : ., mut = -1 - if hit_table_active() != 0 && mw_int_arith_jo_needed(inst_idx) == 0: + if hit_table_active() != 0: sz = emit_instr_tabled(inst_idx, buf, cp) if sz < 0: sz = emit_instr(inst_idx, buf, cp) - // If this instruction emitted a jo (at most one site per instruction, - // add/sub), its resume point is the instruction tail after the fast - // path e2_st (absolute position cp + sz). The slow-path block jumps - // back here; it skips the fast store and stores dest itself. - if g_x86_mw_jo_count > jo_before: - w64(g_x86_mw_jo_resume, (g_x86_mw_jo_count - 1) * 8, cp + sz) - // If this instruction created a 2L operand site (at most one per - // instruction), its block resume point is also the instruction tail, - // after the tag clear. The 2L block writes its own tag. - if g_x86_mw_oc_count > oc_before: - w64(g_x86_mw_oc_resume, (g_x86_mw_oc_count - 1) * 8, cp + sz) cp = cp + sz ii = ii + 1 - // Epilogue and function-tail blocks (frame.cr pf_epilogue). Patch - // returns, fill the sub rsp immediate with save_ss, emit the epilogue - // (add rsp, reverse callee-saved pops with opt >= 1, pop rbp, ret), then - // append the jo slow-path blocks and 2L operand blocks in the cold tail. + // Epilogue (frame.cr pf_epilogue). Patch returns, fill the sub rsp + // immediate with save_ss, emit the epilogue (add rsp, reverse + // callee-saved pops with opt >= 1, pop rbp, ret). cp = pf_epilogue(save_ss, buf, cp) fi = fi + 1 diff --git a/src/os/linux/callseq.cr b/src/os/linux/callseq.cr index ff4061f4..0249c1c0 100644 --- a/src/os/linux/callseq.cr +++ b/src/os/linux/callseq.cr @@ -27,8 +27,8 @@ // Each function returns the new cp. Calls look like // cp = cs_xxx(buf, pos, cp, ...). // -// Depends on instr.cr for encoding primitives and g2_slot, frame.cr for -// g2_tag_off, tag2l.cr for e2_mw_t8, and the shared x86 patch and global arrays. +// Depends on instr.cr for encoding primitives and g2_slot, and the shared x86 +// patch and global arrays. // Register argument dispatch, shared by IR_CALL, IR_CALL_EXTERN, and IR_SPAWN. // SysV AMD64 assigns int args to rdi, rsi, rdx, rcx, r8, r9 and binary64 args to @@ -181,21 +181,6 @@ fn cs_ret_value(buf: string, pos: int, cp: int, s1: int) -> int: cp = cp + e2_sd_load(buf, pos+cp, g2_slot(s1)) else: cp = cp + e2_ld(buf, pos+cp, 0, g2_slot(s1)) - // Tag read path A for a tagged local return. tag=1 means the slot - // holds a 2-limb pointer; dereference it and put the low limb in rax - // (the exit code uses the low byte of the mathematical value). tag=0 - // leaves the fast value unchanged. The je rel8 skips the 3-byte - // dereference. - tg := g2_tag_off(s1) - if tg != -1: - cp = cp + e2_mw_t8(buf, pos+cp, tg) - w8(buf, pos+cp, 116) - w8(buf, pos+cp+1, 3) - cp = cp + 2 // je +3 - w8(buf, pos+cp, 72) - w8(buf, pos+cp+1, 139) - w8(buf, pos+cp+2, 0) - cp = cp + 3 // mov rax, [rax] // Record the jump position for the caller to patch to the epilogue. grow_ret_patch(g_x86_ret_patch_count + 1) w64(g_x86_ret_patch_pos, g_x86_ret_patch_count * 8, pos + cp) diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux/_import.cr index cf07d5de..0eb3d5e4 100644 --- a/src/targets/x86_64-linux/_import.cr +++ b/src/targets/x86_64-linux/_import.cr @@ -32,11 +32,7 @@ import sizes import int_rep import bigint_blob import instr -// Architecture tag-table owner: mw_setup_tags and the mw encoders used by elf.cr. -// tag2l and frame.cr reference each other; pf_epilogue calls e2_mw_*, and tag2l -// reads the table through g2_tag_off. -import tag2l -// Function frame layout: pf_frame_size, pf_prologue, pf_epilogue, and g2_tag_off. +// Function frame layout: pf_frame_size, pf_prologue, pf_epilogue. import frame import resolve import elf diff --git a/tests/harness/ci_hook_allowlist.txt b/tests/harness/ci_hook_allowlist.txt index 6caf7eb3..eccccb74 100644 --- a/tests/harness/ci_hook_allowlist.txt +++ b/tests/harness/ci_hook_allowlist.txt @@ -25,9 +25,9 @@ # · `test_region_cfg.py`(0.9s)· `test_live_ranges.py`(1.4s) # · `test_backend_bootstrap.py`(**22.5s**;allowlist 原自标「最高危」= N06 唯一门, # 本批实测时长后判定可挂)→ `selfhost-tests`(时长 = 本批本机实测,逐条与 run.sh 行注一致) -# - 未挂项仍列下方;`test_mw_task1-6`(口径换代,本批只登记结论)见对应条目说明。 +# - 未挂项仍列下方(旧 `test_mw_task1-6` 已随 2-limb 路径删除)。 -# ─── tests/selfhost(18 条;未挂)─── +# ─── tests/selfhost(11 条;未挂)─── tests/selfhost/test_apx_tag.py 未挂(历史缺口):需 corec 端到端;挂点属构建面 ⇒ 与其它编译器型未挂档同批评估 tests/selfhost/test_dex_arith.py 未挂(历史缺口):dex 定点语义;需 corec tests/selfhost/test_dex_type.py 未挂(历史缺口):dex 类型核心;需 corec @@ -36,12 +36,6 @@ tests/selfhost/test_global_init.py 未挂(历史缺口):全局初始化语 tests/selfhost/test_interp_float.py 未挂(历史缺口):解释器 dex/enum/dyn/pointer;需 corec tests/selfhost/test_lexer_parity.py 未挂(历史缺口):词法对照(两编译器均拒 `_`/宽度后缀);需 corec tests/selfhost/test_lsp.py 未挂(**TODO #2026-09-11-13 点名**):corelsp 八组集成;进程级 ⇒ 挂点须实测时长 -tests/selfhost/test_mw_task1.py 未挂(历史缺口):int 多字 M1 边界套件族;需 corec -tests/selfhost/test_mw_task2.py 未挂(同族)。**现状(判据载体化批 2026-09-16 实核)**:其零 diff 断言(`:284-303`)原先「基线不在就 [SKIP]」而基线在 build/(不入库)+ run.sh 从不传 --snapshot ⇒ **判据可整体静默消失**(新克隆/CI 恒 SKIP 且 rc=0)。本批已改为**默认 fail-closed**(缺基线 ⇒ FAIL;仅显式 --allow-skip/--skip-zdiff 才降级)。**为何仍未挂 CI**:零 diff 腿的基线必须在**改动前**编译器上产(--snapshot),CI 每次都是改动后的树 ⇒ 该腿的**参照物结构性不可得**,挂上去只会恒 FAIL 或被迫 --allow-skip(= 挂了等于没挂)。若要机器化,需把基线**入仓**(二进制不入库纪律冲突,须先取裁)或改为「结构断言 + 语义零变化」口径(TODO #2026-09-11-9 口径)⇒ **成本 = 中高,且属口径换代不是挂点扩容**。 -tests/selfhost/test_mw_task3.py 未挂(同族) -tests/selfhost/test_mw_task4.py 未挂(同族) -tests/selfhost/test_mw_task5.py 未挂(同族) -tests/selfhost/test_mw_task6.py 未挂(同族) tests/selfhost/test_native_float.py 未挂(历史缺口):Native ELF dex 运算 tests/selfhost/test_native_memory.py 未挂(历史缺口):Native ELF 聚合寻址 tests/selfhost/test_native_strings.py 未挂(历史缺口):Native ELF 字符串头 diff --git a/tests/harness/test_criteria_mutations.py b/tests/harness/test_criteria_mutations.py index fe1d3543..2cef2ae8 100644 --- a/tests/harness/test_criteria_mutations.py +++ b/tests/harness/test_criteria_mutations.py @@ -15,16 +15,14 @@ · v21(2026-09-18 串域批)前口径 = 「暖 = 冷**前缀**」且被当预存允许;该预存被证为缺陷本体 ⇒ 前缀样本由正控改列为**坏实现**,正控改为「冷 = 暖逐条相同」 M4 `--dump-tk-terms` 剔除面白名单 → #2026-09-16-26 / B5(test_ccr_types.dump_strip_face_problems) - M5 零 diff 腿指令边界锁步 → #2026-09-16-28 / B1(test_mw_task2.region_equal_mask_calls) - M6 慢路径块体逐指令模板 → #2026-09-16-23 / B6(test_mw_task2.parse_slow_block) 口径:全部为**内存内**突变(不改仓库任何文件、不写盘、零副作用)——突变体 = -真实载体(真实 TOML 表文本 / 真实 ELF 区字节 / 真实 dump 文本 / 真实块字节) -的**最小坏实现**式改写;M5/M6 的字节取自本仓真实产物(来源逐条注明)。 +真实载体(真实 TOML 表文本 / 真实 ELF 区字节 / 真实 dump 文本) +的**最小坏实现**式改写。 判据自身失败即 rc=1(CI 挂 bootstrap-tests;见 src/ci/run.sh)。 -**批 8 追加(2026-09-18 复核 Important)**:M1–M6 为**纯内存**突变;批 8 的四条突变是「改编译器源码 ⇒ +**批 8 追加(2026-09-18 复核 Important)**:M1–M4 为**纯内存**突变;批 8 的四条突变是「改编译器源码 ⇒ 重建 ⇒ 探针读数反向」形态,无法内存复刻 ⇒ 此处接机检其可机械验证的两半(**锚点仍唯一命中** + **期望值 与判据常数一致**,见 `main()` 的 M7–M10 段);**真跑腿**(重建 + 探针)以 `COREC_MUT_SELF=1` 开启。 """ @@ -42,7 +40,6 @@ import test_hit_table as HT # noqa: E402 import test_ccr_types as CT # noqa: E402 import test_cir_warm_path as CW # noqa: E402 -import test_mw_task2 as MW # noqa: E402 # ══════════════════════════════════════════════════════════════════════ @@ -89,78 +86,10 @@ def old_strip_compare_ok(out_flag: str, out_plain: str) -> bool: return stripped == "\n".join(out_plain.splitlines()) -def old_region_equal_mask_calls(a: bytes, b: bytes): - """#2026-09-16-28 旧形态:遇 0xE8 字节即跳 5B(含**非指令首**的 0xE8 ⇒ 其后 4B 盲窗)。""" - if len(a) != len(b): - return False - i = 0 - while i < len(a): - if a[i] != b[i]: - return False - if a[i] == 0xE8: - i = i + 5 - continue - i = i + 1 - return True - - -def old_block_prefix_ok(text: bytes, targets) -> bool: - """#2026-09-16-23 旧形态:只校验 2B 前缀 `4D 19` + 目标序。""" - return all(text[t:t + 2] == MW.BLK_PREFIX for t in targets) - - # ══════════════════════════════════════════════════════════════════════ # 突变体(内存内) # ══════════════════════════════════════════════════════════════════════ -# z2_copy @O0 的 main 区真实字节(本批自 build/mw_task2_scratch/z2_copy_o0 实读; -# 与 tests/selfhost/test_mw_task2.py 的 find_main_region 同源)。其中 -# `48 C7 45 E8 40 E2 01 00` = `mov qword [rbp-0x18], 123456`——**disp8 = 0xE8 是 -# 数据字节、不是指令首**,其后 4B(imm32 `40 E2 01 00`)正是旧判据的盲窗。 -Z2_REGION_HEX = ( - "554889e54883ec3048c7c0ffffffff488945f848c745e840e20100" - "4c8b55e84c8955f04c8b55f04c8955e04c8b55e04c8955d848c745d064000000" - "488b45d0e913000000488b7df84885ff7c05e816640000e9000000004883c4305dc3") - -# t1_add @O0 慢路径块真实字节(build/mw_task2_scratch/t1_add_o0,块首 = jo 目标): -# `4D 19 DB | 41 52 41 53 | BF 10 00 00 00 | E8 .. | 41 5B 41 5A | 4C 89 10 | -# 4C 89 58 08 | 48 89 45 C8 | C6 45 C6 01 | E9 ..` -T1_BLOCK_HEX = ("4d19db41524153bf10000000e8a1620000415b415a4c89104c895808" - "488945c8c645c601e99effffff") - - -def mut_flip_z2_frame_imm(region: bytes) -> bytes: - """坏实现:改 z2_copy 帧常数 imm32 的一个字节(落在 0xE8 数据字节后的盲窗内)。""" - out = bytearray(region) - k = region.find(bytes.fromhex("48c745e840e20100")) - assert k > 0, "突变体定位失败(真实字节模式未命中)" - out[k + 5] = 0xFF # imm32 第 2 字节 0xE2 → 0xFF(值 123456 → 巨大值) - return bytes(out) - - -def mut_block_tag_imm_zero(block: bytes) -> bytes: - """坏实现:慢路径块 tag 置位立即数 1 → 0(tag 不变量破坏)。""" - k = block.find(bytes.fromhex("c645c601")) - assert k > 0, "突变体定位失败" - out = bytearray(block) - out[k + 3] = 0 - return bytes(out) - - -def mut_block_insert_nop(block: bytes) -> bytes: - """坏实现:块首 2B 前缀后插一个 0x90(前缀仍在、顺序不变——旧判据绿)。""" - return block[:2] + b"\x90" + block[2:] - - -def mut_block_dest_reg_swap(block: bytes) -> bytes: - """坏实现:`4C 89 10`(mov [rax], r10)→ `4C 89 18`(mov [rax], r11)。""" - k = block.find(bytes.fromhex("4c8910")) - assert k > 0, "突变体定位失败" - out = bytearray(block) - out[k + 2] = 0x18 - return bytes(out) - - def mut_self_item2(): """M7 **真跑腿**(`COREC_MUT_SELF=1`;默认关):在 /tmp 副本上把条目 2 的分派块**失效化** ⇒ 重建 ⇒ 六位位码探针须回 **24**(= 修复前读数;2026-09-18 复核重跑实测);同时**原树正控**同探针须为 **30**。 @@ -333,54 +262,6 @@ def mk_str(entries): old_strip_compare_ok(reorder, plain), CT.dump_strip_face_problems(reorder, plain)[0])) - # ── M5(#2026-09-16-28/B1)零 diff 腿指令边界锁步:1 个坏实现 + 1 个合法差异正控 ── - # 缓冲区 = 真实区字节 + 尾部补零到 40000B:被测函数签名的 `text_*` 是**整个 - # text 段**(call 目标 = 段内绝对偏移,真实调用目标在区内之外)⇒ 合成载体 - # 必须留出目标可达空间,否则命中的是「目标越界」那条独立断言而非本突变面。 - reg = bytes.fromhex(Z2_REGION_HEX) - span = (0, len(reg)) - t_reg = reg.ljust(40000, b"\x00") - bad = mut_flip_z2_frame_imm(t_reg) - ok_bad, why_bad, _n = MW.region_equal_mask_calls(t_reg, span, bad, span) - cases.append(("M5/#2026-09-16-28 零 diff:改 0xE8 数据字节后的帧常数 imm32(旧盲窗)", - old_region_equal_mask_calls(reg, bad[:len(reg)]), - [] if ok_bad else [why_bad])) - # 合法差异正控:真 call rel32 位移不同 ⇒ 必须**仍绿**(不得误伤豁免面)。 - # 真 call 站点 = 解码出的 `is_call` 指令首(**不是** `find(b"\xe8")`——那个 - # 恰好会命中 disp8 数据字节,正是本判据要区分的两件事)。 - call_off = next(off for off, _ln, is_call in MW.x86_walk(reg) if is_call) - legal = bytearray(t_reg) - legal[call_off + 1] = (legal[call_off + 1] + 7) % 256 # 只动真 call 的 rel32 首字节 - ok_pos, why_pos, ncalls = MW.region_equal_mask_calls(t_reg, span, - bytes(legal), span) - cases.append(("M5/#2026-09-16-28 正控:真 call rel32 位移变化(合法)仍绿", - ok_pos, [] if ok_pos else [why_pos], - {"expect_strong_clean": True})) - print(f" (M5 正控:真 call 站点 @{call_off},豁免面计数 = {ncalls}" - f"(>0 = 探针触发非空转))") - - # ── M6(#2026-09-16-23/B6)慢路径块体模板:3 个坏实现 + 1 个正控 ─────────────── - blk = bytes.fromhex(T1_BLOCK_HEX) - text = b"\x00" * 16 + blk + b"\x00" * 16 - try: - end, alloc_t, resume_t = MW.parse_slow_block(text, 16, is_sub=False) - print(f" (M6 正控:真实块解析 OK end={end} alloc={alloc_t} " - f"resume={resume_t})") - except AssertionError as e: - cases.append(("M6/#2026-09-16-23 正控:真实块体可解析", True, [f"正控自身失败: {e}"])) - for label, mut in [ - ("tag 立即数 1→0", mut_block_tag_imm_zero(blk)), - ("块首插 0x90", mut_block_insert_nop(blk)), - ("mov [rax],r10 → r11", mut_block_dest_reg_swap(blk))]: - mtext = b"\x00" * 16 + mut + b"\x00" * 16 - try: - MW.parse_slow_block(mtext, 16, is_sub=False) - probs = [] - except AssertionError as e: - probs = [str(e)] - cases.append((f"M6/#2026-09-16-23 块体模板:{label}", - old_block_prefix_ok(mtext, [16]), probs)) - # ── M7–M10(批 8,2026-09-18 复核 Important ②③):四条批级突变的**锚点与期望一致性** ── # 四条突变都是「改编译器源码 ⇒ 重建 ⇒ 探针读数反向」的形态 ⇒ **无法**用本档纯内存体例复刻; # 故接机检其**可机械验证的两半**:① 突变锚点在源码中**仍存在且唯一**(锚漂移 = 突变打空而无人知); diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 8bb33ae5..d930957f 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -72,7 +72,7 @@ import sys BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -# 段表/对象面解析器唯一真源 = test_ccr_v7.py(同目录;test_mw_task1.py 先例) +# 段表/对象面解析器唯一真源 = test_ccr_v7.py(同目录) sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from test_ccr_v7 import parse_object_dump # noqa: E402 COREC = os.path.join(BASE, 'build/corec') diff --git a/tests/selfhost/test_mw_task1.py b/tests/selfhost/test_mw_task1.py deleted file mode 100644 index 03547a73..00000000 --- a/tests/selfhost/test_mw_task1.py +++ /dev/null @@ -1,301 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 1:tagged 槽框架(潜在多字变量识别 + tag 区 + 栈布局)。 - -验证对象(架构轴 src/arch/x86_64/ + 格式轴 src/format/elf/): - - mw_setup_tags(tag2l.cr——波 1 Task 4 自 instr.cr 整搬;tag 表 owner): - per 函数潜在多字识别 → tag 字节偏移表。 - 识别 = 不动点闭包(评审修复:单遍前向对循环携带值不成立——tag 态可沿 - 回边携带): - A. IR_BINARY(OP_ADD/OP_SUB) 的 dest(add/sub 溢出 = M1 唯一 >64 生产者); - B. IR_LOAD 值拷贝 d←s1(as 转换拷贝):s1 已标 → 标 d; - B'. IR_STORE 定值拷贝 ρ(s1):=ρ(s2)(ir_gen 赋值形态,真实槽拷贝载体): - s2 已标 → 标 s1——变量行(return/比较消费的槽)只经 STORE 得到值, - 只标临时行不够; - 重复全扫至无新标(单调 ⇒ ≤ vc+1 遍收敛)。 - - tag 区栈布局:帧 = vc*8 + tag_count,按 SysV 16 对齐规则取整; - 无 tagged 变量的函数帧公式与旧版逐字节一致(快路径零变化); - - 布局可观测面:ELF 中函数 prologue 的 sub rsp 立即数 = 帧总字节。 - -方法:Python 独立复刻识别规则(对 .ccr 二进制格式的旁路 oracle——不同实现 -互相校验),从 .ccr 读 per 函数 var 域/指令流/变量类型,按上述规则做不动点, -算出期望 tag 数与期望帧字节;再从 ELF 文本段定位 main(_start 尾 call main -的模式)并解析其 prologue 的 sub rsp 立即数,断言相等。行为层再跑一遍产物体 -核对 exit code。s6(循环携带拷贝)另断言不动点 ⊋ 单遍(防止 oracle/实现被 -回退成单遍而重新掩盖评审发现)。 - -.ccr 读取依赖 v7 段表布局(test_ccr_v7.py 的 V7File 为格式唯一真源——格式 -演进时只需改那边;Task 3 v7 收官后 test_ccr_v6.py 已退役合并,本文件别名 -V6File 仅为调用点命名惯性——语义 = v7 walker,见 import 行注释)。 -""" -import os -import struct -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -SCRATCH = BUILD / "mw_task1_scratch" - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -from test_ccr_v7 import V7File as V6File # noqa: E402 (v7 段表解析器 = 格式唯一真源;别名保调用点不变) - -# IR opcodes (ast.cr) -IR_BINARY = 2 -IR_LOAD = 10 -IR_STORE = 9 -OP_ADD = 1 -OP_SUB = 2 -TI_INT = 0 - - -def load_ir(ccr_path): - """v6 .ccr → (funcs, var_types)。 - - funcs: list of {name, var_start, var_count, nodes}——nodes = 本函数节点切片 - (NOD 全表按 REG root region [enter, exit) 切); - var_start = globals 行 + 前缀函数块行序游标(SYM func 记录序累加)。 - var_types: {var_idx: tk}(SYM func 内嵌声明区:行序 = 函数块内索引)。 - """ - v = V6File(ccr_path.read_bytes()) - strs = v.str_table() - sym = v.sym_parse() - nods = v.nod() - regs = v.reg() - var_types = {} - funcs = [] - vs = len(sym["globals"]) - for f in sym["funcs"]: - n0, n1 = regs[f["root_region"]][2], regs[f["root_region"]][3] - if n0 < 0 or n1 > len(nods) or n0 >= n1: - raise AssertionError(f"func {f['name']}: bad node span [{n0},{n1}) " - f"of {len(nods)}") - for i, decl in enumerate(f["var_decls"]): - var_types[vs + i] = decl["type"] - funcs.append(dict(name=strs[f["name"]], var_start=vs, - var_count=f["var_count"], nodes=nods[n0:n1])) - vs += f["var_count"] - return funcs, var_types - - -def detect_tags(func, var_types, max_passes=None, store_edges=True): - """复刻 tag2l.cr mw_setup_tags:规则 A + B(IR_LOAD 拷贝) + B'(IR_STORE - 定值拷贝),重复全扫至无新标。 - - 退化形态(仅区分性断言用): - max_passes = 1 → 单遍前向(含 B'——评审缺陷形态:回边携带漏标); - store_edges = False → 无 B'(评审前实现规则:A+B,变量行永不入集)。 - """ - vs = func["var_start"] - ve = vs + func["var_count"] - tagged = set() - - def in_dom(x): - return vs <= x < ve and var_types.get(x, -1) == TI_INT - - npass = 0 - while True: - changed = False - for (op, d, s1, s2, s3, tk, fe, ec) in func["nodes"]: - if op == IR_BINARY: - if in_dom(d) and s3 in (OP_ADD, OP_SUB) and d not in tagged: - tagged.add(d) - changed = True - elif op == IR_LOAD: - if (in_dom(d) and s1 in tagged and in_dom(s1) - and d not in tagged): - tagged.add(d) - changed = True - elif op == IR_STORE and store_edges: - # 定值拷贝 ρ(s1) := ρ(s2)(dest 恒 -1) - if (in_dom(s1) and s2 in tagged and in_dom(s2) - and s1 not in tagged): - tagged.add(s1) - changed = True - npass += 1 - if not changed: - break - if max_passes is not None and npass >= max_passes: - break - return tagged - - -def frame_total(vc, n, opt): - """复刻 frame.cr pf_frame_size(含 tag 区 + SysV 16 对齐;x86 实例化波 1 - Task 3 自 instr.cr mw_frame_size 迁入并改名——公式 verbatim)。""" - sz = vc * 8 + n - if opt >= 1: - if sz % 16 != 8: - pad = (8 - sz % 16) % 16 - sz += pad - else: - if sz % 16 != 0: - sz += 16 - sz % 16 - return sz - - -# _start 尾模式:mov edi,eax(89 C7) + mov eax,60(B8 3C000000) + syscall(0F 05) -START_TAIL = b"\x89\xc7\xb8\x3c\x00\x00\x00\x0f\x05" - - -def find_main_frame(elf_path, opt): - """定位 main(_start 内 call main 的 E8 rel32 紧跟 START_TAIL)并解析帧字节。""" - b = elf_path.read_bytes() - text = b[176:] - idx = text.find(START_TAIL) - if idx < 5: - raise AssertionError("cannot locate _start tail in ELF text") - p = idx - 5 # E8 位置(text 相对) - rel = struct.unpack_from("= len(text): - raise AssertionError("main offset out of text") - m = main_off - # prologue - if opt >= 1: - # push rbx(53) push r12-r15(41 54..57) push rbp(55) mov rbp,rsp(48 89 e5) - assert text[m:m + 13] == b"\x53\x41\x54\x41\x55\x41\x56\x41\x57\x55\x48\x89\xe5", \ - text[m:m + 13].hex() - m += 13 - else: - assert text[m:m + 4] == b"\x55\x48\x89\xe5", text[m:m + 4].hex() - m += 4 - if text[m] == 0x48 and text[m + 1] == 0x83 and text[m + 2] == 0xEC: - return text[m + 3] - if text[m] == 0x48 and text[m + 1] == 0x81 and text[m + 2] == 0xEC: - return struct.unpack_from(" int:\n return 7\n", - "s2_intadd": "fn main() -> int:\n a := 2000000000\n b := 2000000000\n c := a + b\n return c\n", - "s3_sub": "fn main() -> int:\n a := 10\n b := 3\n c := a - b\n return c\n", - "s4_addcopy": "fn main() -> int:\n a := 1\n b := 2\n c := a + b\n d := c\n e := d + c\n f := e\n return f\n", - "s5_loop": "fn main() -> int:\n sum := 0\n i := 0\n while i < 10:\n sum = sum + i\n i = i + 1\n return sum\n", - # 评审场景:snap 的拷贝点线性先于 tag 源(x = x + k)的定值——运行时 - # x 的 tag 态沿回边携带,迭代 ≥2 的 snap = x as int 拷贝的是 2-limb x。 - "s6_loopcopy": "fn main() -> int:\n x : ., mut = 0\n i : ., mut = 0\n snap := 0\n while i < 3:\n snap = x as int\n x = x + 1\n i = i + 1\n return snap\n", - # 8 个 add 定值点(旧规则 tag 数 = 8)后跟一个拷贝变量行 s(新规则 +2): - # vc*8 = 160 时 O1/O2 帧 168(旧)vs 184(新)——二进制级可观测差。 - "s7_framediff": "fn main() -> int:\n r : ., mut = 0\n r = r + 1\n r = r + 1\n r = r + 1\n r = r + 1\n r = r + 1\n r = r + 1\n r = r + 1\n r = r + 1\n s := r\n return s\n", -} - -EXPECTED_EXIT = {"s1_noarith": 7, "s2_intadd": 0, "s3_sub": 7, - "s4_addcopy": 6, "s5_loop": 45, "s6_loopcopy": 2, - "s7_framediff": 8} - - -def run_checked(args, label): - cmd = ["nice", "-n", "19", *map(str, args)] - r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=300) - if r.returncode != 0: - print(f"[FAIL] {label}: exit {r.returncode}") - print(r.stdout[-3000:]) - print(r.stderr[-3000:]) - return False - return True - - -def main() -> int: - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - ok = True - for name, desc in CASES: - src = SCRATCH / f"{name}.cr" - src.write_text(SRCS[name], encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - ccr = Path(str(out) + ".ccr") - if not run_checked([COREC, "build", src, "-o", out, "--static", "-O", str(opt)], - f"{name} @O{opt} build"): - ok = False - continue - if not ccr.exists(): - print(f"[FAIL] {name} @O{opt}: no .ccr at {ccr}") - ok = False - continue - funcs, var_types = load_ir(ccr) - mains = [f for f in funcs if f["name"] == "main"] - if len(mains) != 1: - print(f"[FAIL] {name} @O{opt}: expected 1 main, got {[f['name'] for f in funcs]}") - ok = False - continue - mainf = mains[0] - tags = detect_tags(mainf, var_types) - n = len(tags) - expect_f = frame_total(mainf["var_count"], n, opt) - try: - got_f = find_main_frame(out, opt) - except AssertionError as e: - print(f"[FAIL] {name} @O{opt}: prologue parse: {e}") - ok = False - continue - if got_f != expect_f: - print(f"[FAIL] {name} @O{opt}: frame {got_f} != expected {expect_f} " - f"(vc={mainf['var_count']}, tags={n}, desc={desc})") - ok = False - else: - print(f"[PASS] {name} @O{opt}: frame {got_f} == vc*8({mainf['var_count'] * 8}) " - f"+ {n} tags aligned (desc: {desc})") - # 行为 - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, timeout=30) - want = EXPECTED_EXIT[name] - if r.returncode != want: - print(f"[FAIL] {name} @O{opt}: run exit {r.returncode}, want {want}") - ok = False - else: - print(f"[PASS] {name} @O{opt}: run exit {r.returncode}") - # 区分性断言(oracle 级,防实现/测试共同退化掩盖评审缺陷) - if name == "s6_loopcopy" and opt == 0: - # 不动点必须 ⊋ 单遍前向——s6 的拷贝链只经回边跨遍收敛 - sp = detect_tags(mainf, var_types, max_passes=1) - if not sp < tags: - print(f"[FAIL] {name} @O{opt}: fixpoint {sorted(tags)} " - f"not a strict superset of single-pass {sorted(sp)} — " - f"case no longer discriminates the review defect") - ok = False - else: - print(f"[PASS] {name} @O{opt}: fixpoint({len(tags)}) > " - f"single-pass({len(sp)}) — 循环携带拷贝链被收敛捕获") - # s7:帧级可观测性——至少一个 opt 下旧规则(A+B 单遍,评审前实现) - # 的帧与新规则帧不同(否则帧断言无法区分新旧实现) - if name == "s7_framediff": - old_tags = detect_tags(mainf, var_types, max_passes=1, store_edges=False) - obs = [o for o in (0, 1, 2) - if frame_total(mainf["var_count"], len(old_tags), o) - != frame_total(mainf["var_count"], len(tags), o)] - if not obs: - print(f"[FAIL] {name}: 旧规则帧 == 新规则帧 at all opt " - f"(old={len(old_tags)}, new={len(tags)}) — 样例失去帧级区分力") - ok = False - else: - print(f"[PASS] {name}: old-rules({len(old_tags)} tags) vs " - f"fixpoint({len(tags)} tags) frame differs at O{obs}") - print() - print("=== mw task1: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_mw_task2.py b/tests/selfhost/test_mw_task2.py deleted file mode 100644 index ba33c3cf..00000000 --- a/tests/selfhost/test_mw_task2.py +++ /dev/null @@ -1,725 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 2/3:快路径溢出检测发射(tagged int add/sub + jo → 慢路径块)。 - -验证对象(架构轴 src/arch/x86_64/ + 格式轴 src/format/elf/): - - emit_instr IR_BINARY int(TI_INT)ADD/SUB:dest ∈ tagged 集(g2_tag_off(d) - != -1——Task 1 规则 A 对域内 add/sub dest 无条件标记)→ e2_alu 后紧跟 - jo(0F 80 rel32——rel8 对函数尾附加的慢路径块在 >127B 函数体上不可达, - t3 是此必要性的行为级断言)。 - - Task 3 起:函数尾每 jo 站点独立真实慢路径块(2-limb 修正 + alloc(16) + - 写 2-limb + dest 回存 + tag 置位 + 跳回 e2_st 之后——e2_mw_slow_block); - 无 jo 的函数不发块、untagged 快路径零变化(z 用例逐字节验证)。 - - 块前缀 = push r10(41 52,确定性)——站点块定位锚。 - -行为层:无溢出 add(t1)jo 不触发、exit 正常;溢出 add(t2)jo 触发 → -慢路径块 → 2-limb 结果经测试钩子(syscall3 原始 16B 写 stdout——验全 128 -位通道)assert 数学正确(本文件验 2^63 一例,全套验算在 test_mw_task3.py); -t3 大函数 250 次 +1 无溢出 exit 250。 - -零 diff:z 用例(无 int 算术)需与改动前编译器输出逐字节一致。基线快照: - python3 tests/selfhost/test_mw_task2.py --snapshot # 改动前执行一次 -产物存 build/mw_task2_zdiff/(build 目录不入库)。**缺基线时默认 FAIL**(A5 修复, -判据载体化批 2026-09-16)——原实现「不在就 [SKIP]」使整套零 diff 断言可**静默消失** -(新克隆/CI 恒 SKIP 而 rc=0 = 假绿);现仅显式 `--allow-skip` / `--skip-zdiff` 才降级。 -CI/新克隆需在改动前编译器上自产基线(`--snapshot`)才能跑零 diff 腿。 - -需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py -""" -import argparse -import os -import struct -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -SCRATCH = BUILD / "mw_task2_scratch" -ZDIFF = BUILD / "mw_task2_zdiff" - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -from test_mw_task1 import START_TAIL, detect_tags, find_main_frame, load_ir # noqa: E402 - -# IR opcodes / ops(ast.cr,oracle 用——与 tag2l.cr mw_setup_tags 同源常量) -IR_BINARY = 2 -OP_ADD = 1 -OP_SUB = 2 - -# jo near rel32 = 0F 80 cd(6B);慢路径块确定性前缀 = sbb r11, r11 -# (4D 19——高 limb 修正首操作,add/sub 共用;sub 后随 48 F7 not) -JO_PAT = b"\x0f\x80" -BLK_PREFIX = b"\x4d\x19" - - -# ══════════════════════════════════════════════════════════════════════ -# 判据侧 x86-64 指令长度解码(B1/#2026-09-16-28 + B6/#2026-09-16-23 修复,2026-09-16 判据网加固批) -# -# 缘起(判据强度审计 §0/表 B,TODO #2026-09-16-28/#2026-09-16-23): -# - 零 diff 腿原实现「遇 0xE8 字节即跳 5 字节」——0xE8 出现在 disp8/imm32 -# 等**非指令首**位置(值 −24 = 0xE8 极常见)时,其后 4 字节**永不比较** -# = 盲窗(改帧常数低 4B 即可骗过,见 #2026-09-16-28)。 -# - 慢路径块原判据只有 2B 前缀锚(4D 19)+ 目标序(#2026-09-16-23)。 -# 两处都需要「字节流的指令边界」这一事实 ⇒ 本组函数(判据侧独立实现, -# **不复用发射器代码**——发射器改了才可能发现)。 -# 口径:**fail-closed**——未登记 opcode/形态 ⇒ InsnDecodeError(判据转红), -# 绝不静默放宽(判据不得悄悄变弱)。解码正确性以 objdump 对拍验证(本批报告: -# 17 个真实 main 区/慢路径块逐指令边界 == objdump --insn-width=16,含 t3 的 -# 3272 条指令、含 call/jcc/movabs/SIB 形态)。 -# ══════════════════════════════════════════════════════════════════════ - - -class InsnDecodeError(Exception): - """未登记的指令形态(fail-closed:判据红,不静默跳过)。""" - - -_LEGACY_PREFIX = {0x66, 0x67, 0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64, 0x65} -# 无 modrm、无立即数 -_OP_NONE = set(range(0x50, 0x60)) | {0x90, 0x98, 0x99, 0xC3, 0xC9, 0xCC, 0xCE, 0xF4} -# 有 modrm、无立即数(ALU 双操作数族 0x01/03/09/0B/11/13/19/1B/21/23/29/2B/31/33/ -# 39/3B——**0x19 = sbb r/m64, r64 是慢路径块首指令 4D 19 DB 的 opcode**,未登记 -# 时解码器 fail-closed 报错:本批首跑即被它拦下,正证明 fail-closed 有效) -_OP_MODRM = {0x01, 0x03, 0x09, 0x0B, 0x11, 0x13, 0x19, 0x1B, 0x21, 0x23, 0x29, 0x2B, - 0x31, 0x33, 0x39, 0x3B, 0x63, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8A, 0x8B, - 0x8D, 0x8F, 0xD0, 0xD1, 0xD2, 0xD3, 0xFE, 0xFF} -# 0F 双字节:无操作数 / 有 modrm、无立即数 -_OP0F_NONE = {0x05, 0x0B, 0x0E, 0x31, 0x34, 0x35, 0x77} -_OP0F_MODRM = (set(range(0x40, 0x50)) | set(range(0x90, 0xA0)) | - {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x1F, 0x28, 0x29, 0x2A, 0x2B, - 0x2C, 0x2D, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, - 0x5D, 0x5E, 0x5F, 0x6E, 0x7E, 0xA3, 0xAB, 0xAF, 0xB1, 0xB3, 0xB6, - 0xB7, 0xBE, 0xBF, 0xC0, 0xC1, 0xD6, 0xEF}) - - -def _modrm_end(buf, i): - """buf[i] = ModRM → (ModRM 含 SIB/disp 之后的下标, reg 字段)。""" - m = buf[i] - i += 1 - reg, mod, rm = (m >> 3) & 7, m >> 6, m & 7 - if rm == 4 and mod != 3: # SIB - sib = buf[i] - i += 1 - if mod == 0 and (sib & 7) == 5: - i += 4 - elif mod == 1: - i += 1 - elif mod == 2: - i += 4 - elif mod == 0 and rm == 5: # RIP-relative - i += 4 - elif mod == 1: - i += 1 - elif mod == 2: - i += 4 - return i, reg - - -def x86_insn_len(buf, i): - """解码 buf[i] 起一条指令 → (长度, 是否 call rel32)。未登记形态抛 InsnDecodeError。""" - n = len(buf) - start = i - while i < n and buf[i] in _LEGACY_PREFIX: - i += 1 - if i >= n: - raise InsnDecodeError(f"@{start}: 前缀后越界") - rex = 0 - if 0x40 <= buf[i] <= 0x4F: - rex = buf[i] - i += 1 - if i >= n: - raise InsnDecodeError(f"@{start}: REX 后越界") - op = buf[i] - i += 1 - - def need(k, what): - if i + k > n: - raise InsnDecodeError(f"@{start}: {what} 越界(opcode {op:#04x})") - - if op == 0x0F: - need(1, "第二字节") - op2 = buf[i] - i += 1 - if op2 in _OP0F_NONE: - pass - elif 0x80 <= op2 <= 0x8F: - need(4, "jcc rel32") - i += 4 - elif op2 in _OP0F_MODRM: - need(1, "modrm") - i, _ = _modrm_end(buf, i) - else: - raise InsnDecodeError(f"@{start}: 未登记的 0F {op2:02x}") - return i - start, False - if op == 0xE8: - need(4, "call rel32") - return i - start + 4, True - if op == 0xE9: - need(4, "jmp rel32") - return i - start + 4, False - if op == 0xEB or 0x70 <= op <= 0x7F or 0xE0 <= op <= 0xE3: - need(1, "rel8") - return i - start + 1, False - if op in _OP_NONE: - return i - start, False - if 0xB8 <= op <= 0xBF: # mov r32/r64, imm - k = 8 if (rex & 8) else 4 - need(k, "mov imm") - return i - start + k, False - if op == 0x68: - need(4, "push imm32") - return i - start + 4, False - if op == 0x6A: - need(1, "push imm8") - return i - start + 1, False - if op in (0x05, 0x69, 0x81): - need(1, "modrm") - i, _ = _modrm_end(buf, i) - need(4, "imm32") - return i - start + 4, False - if op in (0x6B, 0x80, 0x82, 0x83, 0xC0, 0xC1, 0xC6): - need(1, "modrm") - i, _ = _modrm_end(buf, i) - need(1, "imm8") - return i - start + 1, False - if op in (0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, 0x34, 0x3C, 0xA8): - need(1, "imm8") - return i - start + 1, False - if op == 0xA9: - need(4, "imm32") - return i - start + 4, False - if op == 0xC7: # mov r/m64, imm32 - need(1, "modrm") - i, _ = _modrm_end(buf, i) - need(4, "imm32") - return i - start + 4, False - if op in (0xF6, 0xF7): # group3:/0 /1 带立即数 - need(1, "modrm") - i, reg = _modrm_end(buf, i) - if reg in (0, 1): - k = 1 if op == 0xF6 else 4 - need(k, "group3 imm") - i += k - return i - start, False - if op in _OP_MODRM: - need(1, "modrm") - i, _ = _modrm_end(buf, i) - return i - start, False - raise InsnDecodeError(f"@{start}: 未登记的 opcode {op:#04x}") - - -def x86_walk(buf, base=0): - """线性扫过 buf → [(绝对偏移, 长度, 是否 call)];未登记形态抛 InsnDecodeError。""" - out = [] - i = 0 - while i < len(buf): - ln, is_call = x86_insn_len(buf, i) - if ln <= 0: - raise InsnDecodeError(f"@{i}: 长度 {ln}") - out.append((base + i, ln, is_call)) - i += ln - return out - -CASES = [ - # (name, exit, stdout_want(16B)/None, intent) - ("t1_add", 0, None, - "add 快路径无溢出(2e9+2e9=4e9 < 2^63):jo 在但恒不触发——jo 发射断言 + 行为不变"), - ("t2_add_overflow", 16, struct.pack(" i64max)触发 jo → 慢路径块 → 2-limb 数学正确:" - "测试钩子(syscall3 原始 16B 写 stdout)验 lo=2^63、hi=0(全 128 位通道)"), - ("t3_bigfunc", 250, None, - "250 次 +1(>127B 函数体):jo rel32 必需性——每个 jo 仍可达其函数尾块," - "首 jo 距块 >127B(rel8 编码在回退时不可达)"), - # B6/#2026-09-16-23 修复附例(判据网加固批 2026-09-16):**sub 站点**慢路径块 = 块体 - # 模板的 sub 分支(sbb r11,r11 后另 3B `49 F7 D3` not r11)——原三例全为 - # add ⇒ 该分支在块体判据里从未被走到(§0bis 探针触发面缺口),本例补上。 - ("t4_sub_overflow", 16, struct.pack(" int:\n a := 2000000000\n b := 2000000000\n c := a + b\n return c\n", - "t2_add_overflow": "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n r := syscall3(1, 1, x, 16)\n return r\n", - "t3_bigfunc": None, # Python 生成(250 次 x = x + 1) - "t4_sub_overflow": "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x - -4611686018427387904\n r := syscall3(1, 1, x, 16)\n return r\n", - "z1_noarith": "fn main() -> int:\n return 7\n", - "z2_copy": "fn main() -> int:\n a := 123456\n b := a\n c := b\n return 100\n", - "z3_branch": "fn main() -> int:\n x : ., mut = 0\n while x > 0:\n x = 0\n return 9\n", -} -SRCS["t3_bigfunc"] = ("fn main() -> int:\n x : ., mut = 0\n" - + "\n".join(" x = x + 1" for _ in range(250)) - + "\n return x\n") - - -def _selftest_failclosed_paths(): - """fail-closed 自检:**未登记 opcode 的两条 raise 路径必须抛 InsnDecodeError, - 且不得抛 ValueError。** - - 缘起(2026-09-23 实测;**非本档引入的缺陷,但本档承受它**):TODO 标识迁移 - (#94,`#NN` → `#YYYY-MM-DD-N`)把本档两处 `{op:#04x}` 误改写成 - `{op:#2026-09-09-1x}` ⇒ 构造异常消息时抛 `ValueError: Invalid format specifier` - ⇒ `InsnDecodeError` 根本没被构造出来、调用方 `except InsnDecodeError` 接不住 - ⇒ **判据从「带诊断的红」退化成「未捕获崩溃」**。 - - 为什么必须主动走:格式串**只在未登记形态下才被求值** ⇒ 该损坏是**休眠**的, - 正常跑不触发、套件照常绿。所以判据不能是「跑一遍看红不红」,必须**主动扫过 - 全部 opcode** 把那两条 raise 路径走到。两个命中计数是**空转守卫**:=0 ⇒ - 探针没走到路径 ⇒ 判红(判据空转比判据缺失更危险)。 - - 返回 (ok, detail)。 - """ - hits = {} - for label, bufs in (("单字节", [bytes([op]) for op in range(256)]), - ("0F 双字节", [bytes([0x0F, op2]) for op2 in range(256)])): - cnt = 0 - for b in bufs: - try: - x86_insn_len(b, 0) - except InsnDecodeError as e: - if "未登记" in str(e): - cnt += 1 - except ValueError as e: - return False, (f"{label} {b.hex()}: 抛了 ValueError({e})——" - f"raise 行的消息格式化坏了(`#04x` 被改写过?)") - hits[label] = cnt - if any(v == 0 for v in hits.values()): - return False, f"探针空转:未走到 raise 路径 {hits}(判据空转 ⇒ 判红)" - return True, f"两条 fail-closed raise 路径均抛 InsnDecodeError(命中 {hits})" - - -def jo_sites_oracle(funcs, var_types): - """期望 jo 数 = 该函数 IR 中 dest ∈ tagged 的 int ADD/SUB(发射侧 - g2_tag_off(d) != -1 条件的 oracle 同构——规则 A 使域内 add/sub dest - 恒入集,dest ∈ tags 等价于发射侧判定)。""" - mains = [f for f in funcs if f["name"] == "main"] - if len(mains) != 1: - raise AssertionError(f"expected 1 main, got {[f['name'] for f in funcs]}") - mainf = mains[0] - tags = detect_tags(mainf, var_types) - n = 0 - for (op, d, s1, s2, s3, tk, fe, ec) in mainf["nodes"]: - if op == IR_BINARY and s3 in (OP_ADD, OP_SUB) and d in tags: - n = n + 1 - return mainf, n - - -def mw_site_ops(funcs, var_types): - """jo 站点顺序的 op 列表(OP_ADD/OP_SUB;与 jo_sites_oracle **同源同序**: - 指令序 = 发射序 ⇒ 第 i 个 jo 站点 ↔ 本列表第 i 项)——慢路径块体判据用 - (op == OP_SUB ⇒ 块内必有 `not r11` 3 字节;add 版必无)。""" - mains = [f for f in funcs if f["name"] == "main"] - if len(mains) != 1: - raise AssertionError(f"expected 1 main, got {[f['name'] for f in funcs]}") - tags = detect_tags(mains[0], var_types) - ops = [] - for (op, d, s1, s2, s3, tk, fe, ec) in mains[0]["nodes"]: - if op == IR_BINARY and s3 in (OP_ADD, OP_SUB) and d in tags: - ops.append(s3) - return ops - - -def find_main_region(elf_path, opt, frame): - """main 代码区间 [start, ret_end)(text 相对偏移): - start = _start 尾 call main;main 区终点 = 其尾声 ret 之后(慢路径块即附 - 于此、下一函数之前)——尾声字节模板由 opt 与 oracle 帧字节构造 - (add rsp,frame + pop rbp [+ pop r15..rbx] + ret——用户函数 ret 仅尾声 - 一处,模板在函数体内出现的概率可忽略;main 之后是 stdlib 函数,不能用 - _init_globals 定位)。""" - b = elf_path.read_bytes() - text = b[176:] - idx = text.find(START_TAIL) - if idx < 5: - raise AssertionError("cannot locate _start tail in ELF text") - p = idx - 5 - rel = struct.unpack_from("= len(text): - raise AssertionError("main offset out of text") - if frame == 0: - # 无帧函数:尾声 = pop rbp; ret(无 add rsp) - tail = b"\x5d\xc3" if opt < 1 else b"\x5d\x41\x5f\x41\x5e\x41\x5d\x41\x5c\x5b\xc3" - elif frame > 127: - tail = b"\x48\x81\xc4" + struct.pack(", rax - C6 45 .. 01 | C6 85 ..×4 01 mov byte , 1 (tag 置位 = 1,硬判) - E9 ..×4 jmp resume (回快路径 store 之后) - 返回 (blk_end, alloc_target, resume_target)。形态不符 ⇒ AssertionError。 - - **反例自检(audit §0):什么坏实现能骗过本断言?**——旧判据(2B 前缀 - `4D 19` + 目标序)能被骗过:把块体余下字节任意改写(`4C 89 10` 换寄存器、 - tag 立即数 1→0、`E9` 回跳目标改到别处、甚至整块截短)都仍绿——它只证明 - 「块首两字节在」,不证明块体是那段修正代码。本模板逐指令比字节 + 独立 - 断言「tag 立即数 == 1」(慢路径侧 tag 不变量)与「回跳目标 = 区内指令 - 边界」(由调用方以 region_offs 判)⇒ 上述任一改写必红。 - - **探针触发自检(audit §0bis)**:本函数只在 `len(jos) > 0`(oracle 判定的 - 真 jo 站点)时被调用;t1/t2/t3 均 ≥1 站点 ⇒ 模板断言真被走到(t 用例 - 在 z 用例之外保证非空转;调用方另断言块间连续 = 长度公式成立)。 - """ - cursor = t - - def insn(what, want=None): - nonlocal cursor - try: - ln, _is_call = x86_insn_len(text, cursor) - except InsnDecodeError as e: - raise AssertionError(f"{what}@+{cursor - t}: 解码失败(fail-closed){e}") - b = text[cursor:cursor + ln] - cursor += ln - if want is not None and b != want: - raise AssertionError(f"{what}@+{cursor - t - ln}: 字节 {b.hex()} " - f"!= 期望 {want.hex()}") - return b, cursor - ln - - insn("sbb r11,r11", b"\x4d\x19\xdb") - if is_sub: - insn("not r11", b"\x49\xf7\xd3") - insn("push r10", b"\x41\x52") - insn("push r11", b"\x41\x53") - insn("mov edi,16", b"\xbf\x10\x00\x00\x00") - cb, cp0 = insn("call alloc") - if cb[0] != 0xE8 or len(cb) != 5: - raise AssertionError(f"call alloc@+{cp0 - t}: 非 E8 rel32({cb.hex()})") - alloc_t = cp0 + 5 + struct.unpack_from(", rax") - ok_st = ((len(sb) == 3 and sb[0] in (0x48, 0x49) and sb[1] == 0x89 - and (sb[2] >> 6) == 3 and ((sb[2] >> 3) & 7) == 0) - or (len(sb) == 4 and sb[:3] == b"\x48\x89\x45") - or (len(sb) == 7 and sb[:3] == b"\x48\x89\x85")) - if not ok_st: - raise AssertionError(f"dest 槽存指针@+{sp0 - t}: 形态不符({sb.hex()})") - # tag 字节置位:C6 /0,立即数**必须 == 1**(慢路径侧 tag 不变量) - tb, tp0 = insn("mov byte , 1") - if len(tb) == 4: - ok_tag = tb[:2] == b"\xc6\x45" and tb[3] == 0x01 - elif len(tb) == 7: - ok_tag = tb[:2] == b"\xc6\x85" and tb[6] == 0x01 - else: - ok_tag = False - if not ok_tag: - raise AssertionError(f"tag 置位@+{tp0 - t}: 形态/立即数不符({tb.hex()})" - f"——tag 不变量要求立即数 == 01") - jb, jp0 = insn("jmp resume") - if jb[0] != 0xE9 or len(jb) != 5: - raise AssertionError(f"回跳@+{jp0 - t}: 非 E9 rel32({jb.hex()})") - resume_t = jp0 + 5 + struct.unpack_from(" int: - ap = argparse.ArgumentParser() - ap.add_argument("--snapshot", action="store_true", - help="生成零 diff 基线(须在改动前编译器上运行)") - ap.add_argument("--skip-zdiff", action="store_true", - help="跳过零 diff 字节比较(即使基线存在)") - ap.add_argument("--allow-skip", action="store_true", - help="基线缺失时降级为 [SKIP](默认 **FAIL**——见文件头 A5 修复注)") - args = ap.parse_args() - # fail-closed 自检先行(不依赖 build/ ——纯解码器面) - _sok, _sdetail = _selftest_failclosed_paths() - print(f"[{'PASS' if _sok else 'FAIL'}] fail-closed raise 路径: {_sdetail}") - if not _sok: - return 1 - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - if args.snapshot: - ZDIFF.mkdir(exist_ok=True) - ok = True - for name, want_exit, want_out, desc in CASES: - src = SCRATCH / f"{name}.cr" - src.write_text(SRCS[name], encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - ccr = Path(str(out) + ".ccr") - if not run_checked([COREC, "build", src, "-o", out, "--static", - "-O", str(opt)], f"{name} @O{opt} build"): - ok = False - continue - if not ccr.exists(): - print(f"[FAIL] {name} @O{opt}: no .ccr at {ccr}") - ok = False - continue - funcs, var_types = load_ir(ccr) - mainf, exp = jo_sites_oracle(funcs, var_types) - try: - frame = find_main_frame(out, opt) - except AssertionError as e: - print(f"[FAIL] {name} @O{opt}: prologue parse: {e}") - ok = False - continue - try: - text, m0, m1 = find_main_region(out, opt, frame) - except AssertionError as e: - print(f"[FAIL] {name} @O{opt}: region parse: {e}") - ok = False - continue - jos = scan_jos(text, m0, m1) - # ── jo 个数断言(untagged 用例:exp=0 → 不得有任何 0F 80)── - if len(jos) != exp: - print(f"[FAIL] {name} @O{opt}: jo sites {len(jos)} != oracle {exp} " - f"(tags={len(detect_tags(mainf, var_types))}) — {desc}") - ok = False - continue - if exp == 0: - print(f"[PASS] {name} @O{opt}: no jo (oracle {exp}); region " - f"[{m0},{m1}) len={m1 - m0} (desc: {desc})") - else: - # Task 3:每站点独立慢路径块——jo rel32 目标 = 各自块 - # (发射序 = 指令序 → 扫描序;首站点块 = 尾声 ret 后首字节 m1)。 - # B6/#2026-09-16-23 修复:块体判据从「2B 前缀锚 + 目标序」升级为**逐指令 - # 模板断言**(parse_slow_block:除调用位移/槽偏移/回跳位移三处 - # 站点相关字段外全字节钉死 + tag 立即数 == 1 硬判),且块间 - # **连续**(块长公式成立)、`not r11` 与站点 op 一致。 - targets = [t for (_, t) in jos] - if jos[0][1] != m1: - print(f"[FAIL] {name} @O{opt}: first jo target {jos[0][1]} " - f"!= block region start {m1}") - ok = False - continue - bad_ord = [t for t in targets if t < m1] - if bad_ord: - print(f"[FAIL] {name} @O{opt}: block targets pre-epilogue " - f"{bad_ord}") - ok = False - continue - if any(targets[i] >= targets[i + 1] for i in range(len(targets) - 1)): - print(f"[FAIL] {name} @O{opt}: block targets not strictly " - f"increasing: {targets}") - ok = False - continue - site_ops = mw_site_ops(funcs, var_types) - if len(site_ops) != len(jos): - print(f"[FAIL] {name} @O{opt}: oracle ops {len(site_ops)} != " - f"jo sites {len(jos)}(站点 ↔ op 对齐失败)") - ok = False - continue - region_offs = {off for off, _, _ in x86_walk(text[m0:m1])} - blk_err = None - alloc_targets = set() - for si, (jo, tgt) in enumerate(jos): - try: - end, alloc_t, resume_t = parse_slow_block( - text, tgt, is_sub=(site_ops[si] == OP_SUB)) - except AssertionError as e: - blk_err = (f"site {si}(jo@{jo},op={'sub' if site_ops[si] == OP_SUB else 'add'})" - f" 块体模板不符: {e}") - break - if si + 1 < len(jos) and end != targets[si + 1]: - blk_err = (f"site {si} 块尾 {end} != 下一站点块首 " - f"{targets[si + 1]}(块长公式/块间连续失败)") - break - # 回跳目标 = 快路径 store 之后:必须在主区内、且是**真指令边界** - if not (m0 < resume_t < m1) or (resume_t - m0) not in region_offs: - blk_err = (f"site {si} 回跳目标 {resume_t} 不在主区指令边界内 " - f"([{m0},{m1}),边界集 {len(region_offs)} 项)") - break - if not (0 <= alloc_t < len(text)): - blk_err = f"site {si} alloc 调用目标 {alloc_t} 越出 text" - break - alloc_targets.add(alloc_t) - if blk_err: - print(f"[FAIL] {name} @O{opt}: {blk_err}") - ok = False - continue - if len(alloc_targets) != 1: - print(f"[FAIL] {name} @O{opt}: 各站点 alloc 目标不一致 " - f"{sorted(alloc_targets)}(应全为同一 alloc 助手)") - ok = False - continue - dmax = targets[0] - jos[0][0] # 首 jo 距其块(= m1 − jo0) - print(f"[PASS] {name} @O{opt}: {len(jos)} jo -> per-site blocks " - f"@{targets[0]}..+{targets[-1] - targets[0]} " - f"(first-jo dist {dmax}B; 块体模板逐指令 + 块间连续 + " - f"tag=1 + 回跳落指令边界 + alloc 目标唯一 " - f"{alloc_targets.pop()}; desc: {desc})") - if name == "t3_bigfunc" and opt == 0: - # rel32 必需性:最远 jo 距块 >127B——rel8 编码不可达,回退必红 - if dmax <= 127: - print(f"[FAIL] {name} @O{opt}: farthest jo-block dist " - f"{dmax} <= 127 — t3 失去 rel32 判别力") - ok = False - # ── 行为(t2 溢出 → 2-limb 修正 + 测试钩子验 16B;其余 exit 码)── - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, capture_output=True, timeout=60) - if r.returncode != want_exit: - print(f"[FAIL] {name} @O{opt}: run exit {r.returncode}, " - f"want {want_exit}") - ok = False - else: - print(f"[PASS] {name} @O{opt}: run exit {r.returncode}") - if want_out is not None: - if r.stdout != want_out: - print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " - f"expected {want_out.hex()} (lo/hi 2-limb 全 128 位)") - ok = False - else: - print(f"[PASS] {name} @O{opt}: stdout 16B == {want_out.hex()} " - f"(2-limb lo/hi 正确)") - # ── 零 diff:untagged 用例 main 指令流 == 改动前快照(E8 call - # 位移例外——stdlib 函数因 tagged 代码合法增长而整体后移)── - if name.startswith("z") and not args.skip_zdiff: - bl = ZDIFF / f"{name}_o{opt}.bin" - if bl.exists(): - bt, b0, b1 = find_main_region(bl, opt, frame) - same, why, ncalls = region_equal_mask_calls( - text, (m0, m1), bt, (b0, b1)) - if not same: - print(f"[FAIL] {name} @O{opt}: main stream differs vs " - f"pre-change baseline: {why}") - ok = False - elif ncalls == 0: - # §0bis 探针触发自检:call 豁免面未被走到 ⇒ 断言空转 - print(f"[FAIL] {name} @O{opt}: zero-diff 腿未走到 call " - f"豁免面({why})——判据在空转,判红") - ok = False - else: - print(f"[PASS] {name} @O{opt}: main {why} vs " - f"pre-change baseline") - elif args.snapshot: - import shutil - shutil.copyfile(out, bl) - print(f"[SNAP] {name} @O{opt}: baseline saved {bl.name}") - elif args.allow_skip: - # 显式豁免(**唯一**保留 SKIP 的口径):调用者已知基线不可得并承担缺口。 - print(f"[SKIP] {name} @O{opt}: no zero-diff baseline " - f"({bl}) — **显式 --allow-skip**:判据缺口由调用者承担" - f"(run --snapshot on pre-change compiler 产基线)") - else: - # A5 修复(判据载体化批 2026-09-16):基线在 build/(不入库)而 run.sh - # 从不传 --snapshot ⇒ 原实现「不在就 [SKIP]」使**整套零 diff 断言可静默 - # 消失**(新克隆/CI 恒 SKIP 而 rc=0 = 假绿)。现默认 fail-closed: - # 基线缺失 ⇒ FAIL;仅显式 --allow-skip / --skip-zdiff 才降级。 - print(f"[FAIL] {name} @O{opt}: zero-diff baseline missing " - f"({bl}) — 默认 fail-closed(判据不得静默消失)。" - f"补救:在**改动前**编译器上跑 --snapshot 产基线;" - f"确需跳过请显式 --allow-skip(或 --skip-zdiff)") - ok = False - print() - print("=== mw task2: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_mw_task3.py b/tests/selfhost/test_mw_task3.py deleted file mode 100644 index 3aa74a2d..00000000 --- a/tests/selfhost/test_mw_task3.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 3:慢路径 + 2-limb 表示运行时(jo 后数学修正 + 堆对象 + tag 写)。 - -验证对象(src/arch/x86_64/ tag2l.cr e2_mw_slow_block(波 1 Task 4 自 instr.cr -整搬)+ src/arch/x86_64/ frame.cr pf_epilogue 函数尾块发射——Task 3 自 elf.cr 抽出): - 溢出(jo/OF=1)后 128 位真值修正:低 limb = 快路径环绕结果 r10;高 limb = - add:CF ? -1 : 0(sbb r11,r11——正溢出 CF=0/负溢出 CF=1 推演); - sub:CF ? 0 : -1(sbb+not——正溢出借位 CF=1/负溢出混合符号无借位 CF=0)。 - 存储:alloc(16)(与 IR_ALLOC_* 同 bump/arena 语义)→ [+0] lo / [+8] hi → - dest 值槽存指针(含 O1/O2 reg 形态)+ tag 字节置位(g2_tag_off)→ jmp 回 - 快路径 store 之后(resume)。 - -验证通道(全 128 位可观测):溢出结果 2-limb 对象 16 原始字节经 - syscall3(1,1,x,16)(SYS_write,x 槽 = 对象指针——`x 值槽存指针`的直接可 - 观测面)写 stdout → Python 断言 lo/hi。判别矩阵(CF/op 四象限,任一规则 - 回退成另一 op 的规则即红): - m1 add 正溢出(CF=0)→ hi=0;m2 add 负溢出(CF=1)→ hi=-1; - m3 sub 正溢出(CF=1)→ hi=0(若用 add 规则 → hi=-1 红); - m4 sub 负溢出(CF=0)→ hi=-1(若用 add 规则 → hi=0 红); - m5 同函数双 add 站点(两块独立发射);m6 同函数 add+sub 混站(两块形状)。 - -需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py -""" -import os -import struct -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -SCRATCH = BUILD / "mw_task3_scratch" - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -from test_mw_task1 import detect_tags, load_ir # noqa: E402 -from test_mw_task2 import jo_sites_oracle # noqa: E402 - - -def limb_bytes(S): - """128 位数学值 S(Python int,∈ [-2^64, 2^64))→ 2-limb 原始 16B - (lo 小端 8B + hi 小端 8B——与 e2_mw_slow_block 的 [+0]/[+8] 布局一致)。""" - return struct.pack("> 64) - - -# (name, src, 期望 128 位值, 意图) -S64 = 1 << 63 -CASES = [ - ("m1_add_pos", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n r := syscall3(1, 1, x, 16)\n return r\n", - S64, "add 正溢出:CF=0 → 高 limb = 0(lo=2^63 环绕 0x8000..0)"), - ("m2_add_neg", - "fn main() -> int:\n x : ., mut = -9223372036854775807\n x = x + -2\n r := syscall3(1, 1, x, 16)\n return r\n", - -S64 - 1, "add 负溢出:CF=1 → 高 limb = -1(lo=2^63-1、hi=0xFF..F)"), - ("m3_sub_pos", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x - -4611686018427387904\n r := syscall3(1, 1, x, 16)\n return r\n", # sub 规则 hi=0(add 规则会给 -1 → 红) - S64, "sub 正溢出:CF=1 → 高 limb = 0(判别:add 规则在此给 -1)"), - ("m4_sub_neg", - "fn main() -> int:\n x : ., mut = -4611686018427387905\n x = x - 4611686018427387904\n r := syscall3(1, 1, x, 16)\n return r\n", # 无借位 CF=0)→ sub 规则 hi=-1 - -S64 - 1, "sub 负溢出:CF=0 → 高 limb = -1(判别:add 规则在此给 0)"), - ("m5_two_add_sites", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n r1 := syscall3(1, 1, x, 16)\n y : ., mut = -9223372036854775807\n y = y + -2\n r2 := syscall3(1, 1, y, 16)\n return r1 + r2\n", - None, "同函数双 add 站点:两独立块都执行、都正确(rc=32 + 32B 流)"), - ("m6_mix_sites", - "fn main() -> int:\n a : ., mut = 4611686018427387904\n a = a - -4611686018427387904\n r1 := syscall3(1, 1, a, 16)\n b : ., mut = -4611686018427387905\n b = b - 4611686018427387904\n r2 := syscall3(1, 1, b, 16)\n c : ., mut = -9223372036854775807\n c = c + -2\n r3 := syscall3(1, 1, c, 16)\n return r1 + r2 + r3\n", - None, "add+sub 混站:sub 块(含 not 修正)与 add 块同函数共存"), -] - -EXPECT_STREAM = { - "m1_add_pos": limb_bytes(S64), - "m2_add_neg": limb_bytes(-S64 - 1), - "m3_sub_pos": limb_bytes(S64), - "m4_sub_neg": limb_bytes(-S64 - 1), - "m5_two_add_sites": limb_bytes(S64) + limb_bytes(-S64 - 1), - "m6_mix_sites": limb_bytes(S64) + limb_bytes(-S64 - 1) + limb_bytes(-S64 - 1), -} - - -def run_checked(args, label): - cmd = ["nice", "-n", "19", *map(str, args)] - r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) - if r.returncode != 0: - print(f"[FAIL] {label}: exit {r.returncode}") - print(r.stdout[-3000:]) - print(r.stderr[-3000:]) - return False - return True - - -def main() -> int: - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - ok = True - for name, src, _val, desc in CASES: - srcf = SCRATCH / f"{name}.cr" - srcf.write_text(src, encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - ccr = Path(str(out) + ".ccr") - if not run_checked([COREC, "build", srcf, "-o", out, "--static", - "-O", str(opt)], f"{name} @O{opt} build"): - ok = False - continue - # 站点数 oracle(同 test_mw_task2——发射侧 jo 数 = dest∈tagged 的 - # int ADD/SUB 指令数):多站点用例要求站点数 == 溢出运算点数 - funcs, var_types = load_ir(ccr) - _mainf, nsites = jo_sites_oracle(funcs, var_types) - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, capture_output=True, - timeout=60) - want = EXPECT_STREAM[name] - ns = len(want) // 16 # 期望写的对象数(单站 1 / 双站 2 / 三站 3) - if r.stdout != want: - print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " - f"{want.hex()} — 2-limb 128 位值数学不正确 " - f"(sites={nsites}, 期望对象 {ns})\n {desc}") - ok = False - continue - if r.returncode != 16 * ns: - print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " - f"{16 * ns}(16B × {ns} 对象)") - ok = False - continue - print(f"[PASS] {name} @O{opt}: {ns} 对象 2-limb 流 == " - f"{want.hex()}(exit {r.returncode},sites={nsites}," - f"{desc})") - print() - print("=== mw task3: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_mw_task4.py b/tests/selfhost/test_mw_task4.py deleted file mode 100644 index 315d2c82..00000000 --- a/tests/selfhost/test_mw_task4.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 4:消费者适配(tag 读路径 + tag 卫生)。 - -验证对象(src/arch/x86_64/ instr.cr(消费点/站点发射)+ tag2l.cr(tag 卫生 + -2L 块编码——波 1 Task 4 自 instr.cr 整搬)+ frame.cr(波 1 Task 3 起:帧序 + -函数尾 2L 操作数块发射)+ src/format/elf/ elf.cr): - A. tag 卫生(定义点写,无帧入口清零——每 tagged 行的每个定值点都写 tag): - ① 快路径定值清 tag:tagged 变量被快路径 add/sub 重定义(同站点回边再 - 执行——临时行 stale tag=1 会给拷贝传播错误值); - ② IR_STORE/IR_LOAD 定值拷贝 = 运行时 tag 拷贝(s2/s1 tagged → 拷贝 - tag 字节;untagged 源 → 清 0); - ③ 参数行 prologue 保存清 tag(tagged 参数行在函数内首次定值前的读取 - ——stale tag 会把 64 位参数误读为 2-limb 指针)。 - B. tag 读路径(消费者): - ① IR_RETURN:tag=1 → 解 2-limb 指针取低 limb → exit 低 8 位 = - 数学值低 8 位(无解引用 = 指针低字节 = 错值/随机); - ② 比较(int EQ/NE/LT/GT/LE/GE):2-limb 参与比较 = 128 位比较 - (先高 limb 带符号、同则低 limb 无符号——低 limb 无符号是判别点); - ③ 链式运算:2-limb 值再参与 add/sub = 128 位全精度混合算术 - (2L+快 / 快+2L / 2L+2L——低+低进位链 + 高+高),结果可装回 64 位 - 时降级为快值(down-normalize——保持「2L ⟹ |v|≥2^63 ⟹ 非零」不变量, - IR_BRANCH 指针真值性因此保持正确)、否则新 2-limb 对象(hi 可出 - {0,−1} 域,如 −2^64−8 → hi=−2——128 位码域内无误差); - ④ fits-64 判定判别(d1/d2——Critical 修复护栏):链达 −2^64−2 - (hi==lo==−2)/ 2^64+1(hi==lo==1)须保持 2L 并全 128 断言—— - fits 判据 hi==signmask(lo) 的 cmp 若错编成 hi vs lo(cmp r11,r10) - 即假拟合降级截断(静默错值,其余用例均不可见——唯一判别形态)。 - -验证通道(沿用 Task 3):2-limb 对象原始 16B 经 syscall3(1,1,x,16) 写 -stdout(x 槽 = 指针——call 参数走 M1 出逃边界,此处恰为直读面)。 - -需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py -""" -import struct -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -SCRATCH = BUILD / "mw_task4_scratch" - -B62 = 1 << 62 # 4611686018427387904 -NB63 = -(1 << 63) + 1 # -(2^63-1):可写字面量下限 - - -def limb_bytes(S): - """128 位数学值 S(Python int)→ 2-limb 原始 16B(lo 小端 + hi 小端)。""" - lo = S & ((1 << 64) - 1) - hi = (S >> 64) & ((1 << 64) - 1) - return struct.pack(" int:\n x : ., mut = 4611686018427387910\n x = x + 4611686018427387904\n return x\n", - None, 6, "return 2L:exit 低 8 位 = 数学值 2^63+6 的低字节 6(无 deref = 指针字节)"), - ("r2_ret_big_neg", - "fn main() -> int:\n x : ., mut = -9223372036854775807\n x = x + -2\n return x\n", - None, 255, "return 2L 负值:低字节 0xFF = 255"), - # ── B② 比较(判别:指针比较给反 / 低 limb 有符号给反)── - ("c1_cmp_neg_lt_zero", - "fn main() -> int:\n x : ., mut = -9223372036854775807\n x = x + -2\n if x < 0:\n return 1\n return 2\n", - None, 1, "2L 负数 < 0 = 真(指针为正 → 指针比较给 2——判别)"), - ("c2_cmp_2l_vs_fast_neg", - "fn main() -> int:\n x : ., mut = -9223372036854775807\n x = x + -2\n if x > -5:\n return 1\n return 2\n", - None, 2, "2L(-2^63-1) > -5 = 假(指针为正 → 给 1——判别)"), - ("c3_cmp_lo_unsigned", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 1\n if 5 < x:\n return 1\n return 2\n", - None, 1, "5 < 2^63+1 = 真——低 limb 须无符号比较(有符号 lo:5 > -2^63+1 → 给 2)"), - ("c4_cmp_eq_diff_obj", - "fn main() -> int:\n a : ., mut = 4611686018427387904\n a = a + 4611686018427387904\n b : ., mut = 4611686018427387904\n b = b + 4611686018427387904\n if a == b:\n return 1\n return 2\n", - None, 1, "2L == 2L(同值异对象):指针比较给 2——判别 128 位 EQ"), - ("c5_cmp_ge_2l", - "fn main() -> int:\n a : ., mut = 4611686018427387904\n a = a + 4611686018427387904\n b : ., mut = 4611686018427387904\n b = b + 4611686018427387904\n if a >= b && a <= b:\n return 1\n return 2\n", - None, 1, "2L GE/LE 同值链(布尔合并 = 比较结果再参与运算)"), - # ── B③ 链式运算(2L 再参与 add/sub——128 位混合算术)── - ("a1_chain_mixed_add", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n x = x + 5\n r := syscall3(1, 1, x, 16)\n return r\n", - limb_bytes((1 << 63) + 5), 16, "2L + 快正值:2^63+5(钩子验全 128)"), - ("a2_chain_fast_first", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n y : ., mut = 6\n y = y + x\n r := syscall3(1, 1, y, 16)\n return r\n", - limb_bytes((1 << 63) + 6), 16, "快 + 2L(操作数序反转):2^63+6"), - ("a3_chain_sub_2l_fast", - "fn main() -> int:\n x : ., mut = 4611686018427387911\n x = x + 4611686018427387904\n x = x - 1\n r := syscall3(1, 1, x, 16)\n return r\n", - limb_bytes((1 << 63) + 6), 16, "2L − 快:2^63+6(保持 2L)"), - ("a4_chain_sub_downnorm", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n x = x - 5\n return x\n", - None, 251, "2L − 快降级回 64 位:2^63−5 低字节 0xFB = 251"), - ("a5_chain_2l_2l_neg_correc", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n y : ., mut = -9223372036854775807\n y = y + -2\n z : ., mut = x + y\n w : ., mut = z + 7\n return w\n", - None, 6, "2L+2L 降级(2^63 + (-2^63-1) = -1 → 快值)+ 快链继续 → 6"), - ("a6_chain_2l_2l_borrow", - "fn main() -> int:\n m : ., mut = -9223372036854775807\n m = m + -4\n a : ., mut = 4611686018427387904\n a = a + 4611686018427387904\n b : ., mut = a + 5\n d : ., mut = m - b\n r := syscall3(1, 1, d, 16)\n return r\n", # hi=-2(出 {0,-1} 域)lo=2^64-8 - limb_bytes(-(1 << 64) - 8), 16, "2L−2L 双 limb 借位链:−2^64−8 → hi=−2(128 码域内精确)"), - ("a7_chain_loop_hi1", - "fn main() -> int:\n x : ., mut = 0\n i : ., mut = 0\n while i < 6:\n x = x + 4611686018427387904\n i = i + 1\n r := syscall3(1, 1, x, 16)\n return r\n", - limb_bytes((1 << 64) + (1 << 63)), 16, - "循环携带链:同站点 快→溢出→混合→hi=1(6·2^62 = 2^64+2^63)"), - # ── 判别回归(fits-64 假拟合——hi==lo 恰在 {0,−1} 之外,其余用例不可见)── - # d1/d2 链终值 hi==lo(−2^64−2 → hi=lo=−2;2^64+1 → hi=lo=1):真 fits - # 判据 hi == sar63(lo)(lo=−2 → −1 ≠ hi;lo=1 → 0 ≠ hi)→ 必须保持 2L。 - # fits 检查若错编成 cmp hi, lo(reg 字段误指 r10 而非 rdx)→ 假拟合 → - # 降级截断(−2 / 1 快值落槽 + tag 0)→ syscall3 把快值当指针 → EFAULT - # (红证据:stdout 空 + exit ≠ 16)。 - ("d1_chain_neg_fitdisc", - "fn main() -> int:\n m : ., mut = -9223372036854775807\n m = m + -2\n b : ., mut = 4611686018427387904\n b = b + 4611686018427387904\n b = b + 1\n d : ., mut = m - b\n r := syscall3(1, 1, d, 16)\n return r\n", - # (lo=2^64-2 hi=2^64-2——同值非同 fit:lo 负 → signmask = -1 ≠ hi) - limb_bytes(-(1 << 64) - 2), 16, "假拟合判别:−2^64−2 → hi==lo==−2 须保持 2L(错编 cmp hi,lo 截断 −2)"), - ("d2_chain_pos_fitdisc", - "fn main() -> int:\n a : ., mut = 4611686018427387904\n a = a + 4611686018427387904\n b : ., mut = 4611686018427387904\n b = b + 4611686018427387904\n b = b + 1\n c : ., mut = a + b\n r := syscall3(1, 1, c, 16)\n return r\n", - # (lo=1 hi=1——lo 正 → signmask = 0 ≠ hi) - limb_bytes((1 << 64) + 1), 16, "假拟合判别:2^64+1 → hi==lo==1 须保持 2L(错编 cmp hi,lo 截断 1)"), - # ── A 卫生:溢出后重定义/拷贝/参数,快值不得被误读为 2L ── - ("h1_hygiene_store_redef", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n x = 7\n return x\n", - None, 7, "卫生:2L 后常量重定义 → return 快值 7(stale tag 会解引用 7 → 崩)"), - ("h2_hygiene_site_reexec", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n i : ., mut = 0\n while i < 3:\n x = x + 4611686018427387904\n x = x - 4611686018427387904\n i = i + 1\n x = x - 4611686018427387904\n return x\n", - None, 0, "卫生:同站点快/慢交替(stale tag=1 会把快值 2^62 解引用 → 崩)"), - ("h3_hygiene_copy_prop", - "fn main() -> int:\n x : ., mut = 4611686018427387904\n x = x + 4611686018427387904\n y := x\n r := syscall3(1, 1, y, 16)\n z := x as int\n r2 := syscall3(1, 1, z, 16)\n return r + r2\n", - limb_bytes(1 << 63) * 2, 32, "拷贝链 tag 传播:y/z 都读到 2^63(无传播 = 指针低字节)"), - ("h4_hygiene_param", - # 参数行 prologue 清 tag 的回归护栏:call 1 令 p 以 2L 态(tag=1)离开 - # (函数内最后一次 p 定值 = 溢出的 p=p+2^62),call 2 若不清参数 tag, - # p=p+2^62 站点会把 64 位参数值当 2L 指针解引用(值 ~4.6e18 → 崩)。 - "fn fb(p: int) -> int:\n p = p + 4611686018427387904\n q : ., mut = p - p\n return q\nfn main() -> int:\n r1 := fb(4611686018427387910)\n r2 := fb(4611686018427387910)\n return r1 + r2\n", - None, 0, "卫生:参数 prologue 清 tag(2 次调用都须把 64 位参数当快值)"), -] - - -def run_checked(args, label): - cmd = ["nice", "-n", "19", *map(str, args)] - r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) - if r.returncode != 0: - print(f"[FAIL] {label}: exit {r.returncode}") - print(r.stdout[-3000:]) - print(r.stderr[-3000:]) - return False - return True - - -def main() -> int: - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - ok = True - for name, src, want_stream, want_exit, desc in CASES: - srcf = SCRATCH / f"{name}.cr" - srcf.write_text(src, encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - if not run_checked([COREC, "build", srcf, "-o", out, "--static", - "-O", str(opt)], f"{name} @O{opt} build"): - ok = False - continue - os = __import__("os") - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, capture_output=True, - timeout=60) - if want_stream is not None and r.stdout != want_stream: - print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " - f"{want_stream.hex()}\n {desc}") - ok = False - continue - if r.returncode != want_exit: - print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " - f"{want_exit}\n {desc}") - ok = False - continue - print(f"[PASS] {name} @O{opt}: exit {r.returncode}" - + (f" stream={want_stream.hex()}" if want_stream else "") - + f"\n {desc}") - print() - print("=== mw task4: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_mw_task5.py b/tests/selfhost/test_mw_task5.py deleted file mode 100644 index 0eb7a913..00000000 --- a/tests/selfhost/test_mw_task5.py +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 5:D6 分配器适配——现状确认 + 测试加固(裁决:不改)。 - -plan D6 原案:「tagged(潜在多字)变量排除寄存器分配(只栈)」。执行前核 -实现状(Task 4 concern #2026-07-31-1 消费点确认)后裁决:**reg 形态自洽成立,D6 排除 -不实施**。依据: - - 1. 表示演进使 D6 原始动机失效:plan 写作时的「多字值不可驻单寄存器」前提 - 在最终 D1a 表示(值槽 = 64 位快值或 2-limb 堆对象**指针** + 帧旁路 tag - 字节)下不成立——寄存器驻留的是指针,128 位载荷在堆对象; - 2. 单 seam 结构保证:全部变量值读写经 g2_slot(含 Task 3 jo 慢路径块回存 - e2_mov(dst_reg,rax)、Task 4 2L 操作数块装载、return 解指针、tag 卫生 - 落值)——tag 读写经 g2_tag_off(帧字节,与值形态无关);强制栈例外仅 - prologue 参数保存/寄存器参数装载(消费任何 tag 之前);表(HIT)路径 - preflight 已拒 reg 形态变量(hit_ev_slot_addr_ok → 落旧路径); - 3. O2 实证:tagged 变量**确实**被 CAG 分配寄存器(含 2L 态驻留——本测试 - 元断言),且 task3/4 电池 60 组 O2 全绿(16B 通道全 128 断言)—— - reg 形态下 2L 语义可运行且精确; - 4. 排除零收益有代价:发射侧无独立 reg/栈路径可删(块对 g2_slot 参数化, - 单代码路径),排除只损 O2 性能(tagged = 最常见的 add 循环变量行)。 - -本测试 = 该裁决的可执行形态(若将来翻案改只栈,下列 O2 元断言会红——届时 -须同步更新本文件并给出新证据): - - A. 行为(O0/O1/O2):三类 2L 态用例全绿(exit + syscall3 16B 全 128 - 通道)——与 task3/4 语义面重叠,但此处与 B 的元断言同用例链接; - B. 元断言(2026-09-07 regalloc 移后端后载体 = corearch --dump-regassign: - .ccr 不再携带 REG_ASSIGN(R2/D-1=Y)——corearch load 后自算;dump 通道 - 按 --opt-level 门逐对输出 var→reg。oracle 识别规则 = test_mw_task1): - - O1:corearch -O1 dump = 空(分配门 = opt_level ≥ 2——O1 tagged 变量 - 恒全栈,只栈平凡成立); - - O2:corearch -O2 dump:指定函数(运行时确实发生 2L 溢出的函数) - tagged∩REG_ASSIGN ≠ ∅ ——「2L 态值驻寄存器 + 行为全绿」链接成立 = - reg 形态自洽的活体实证。 - -.ccr 结构读取 = test_mw_task1.load_ir/detect_tags(oracle 复刻识别规则); -meta 元数据改经 corearch --dump-regassign(分配真相源 = corearch 内存态)。 -""" -import os -import re -import struct -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -COREARCH = BUILD / "corearch" -SCRATCH = BUILD / "mw_task5_scratch" - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -from test_mw_task1 import load_ir, detect_tags # noqa: E402 - -REGASSIGN_LINE = re.compile(r"^regassign: (\d+) (\d+)$", re.MULTILINE) - - -def limb_bytes(S): - """128 位数学值 S → 2-limb 原始 16B(lo 小端 + hi 小端)。""" - lo = S & ((1 << 64) - 1) - hi = (S >> 64) & ((1 << 64) - 1) - return struct.pack(" dict: - """corearch --dump-regassign 通道(regalloc 移后端):corearch load .ccr - 后按 --opt-level 门自算(O2 = 分配)并逐对输出 var→reg。O0/O1 → 空。 - - 返回 {var_idx: reg}(.ccr 不再携带 REG_ASSIGN——分配结果同进程消费, - 元断言改从此通道取分配真相源)。""" - r = subprocess.run(["nice", "-n", "19", str(COREARCH), str(ccr_path), - "--dump-regassign", "--opt-level", str(opt_level)], - cwd=BASE, capture_output=True, text=True, timeout=120) - if r.returncode != 0: - raise RuntimeError(f"corearch --dump-regassign rc={r.returncode}: " - f"{r.stdout[-500:]} {r.stderr[-500:]}") - pairs = {} - for ln in r.stdout.splitlines(): - m = REGASSIGN_LINE.match(ln.strip()) - if m: - pairs[int(m.group(1))] = int(m.group(2)) - return pairs - - -# (name, src, 2L 溢出所在函数名, 期望 stdout(None = 只看 exit), 期望 exit, 意图) -CASES = [ - # h4 同形:参数行 2L 态 + 跨调用帧复用(O2 下 fb 的 p/q 驻 callee-saved) - ("t5_param_2l_reg", - "fn fb(p: int) -> int:\n p = p + 4611686018427387904\n q : ., mut = p - p\n return q\nfn main() -> int:\n r1 := fb(4611686018427387910)\n r2 := fb(4611686018427387910)\n return r1 + r2\n", - ["fb"], None, 0, "O2 元断言目标:fb 的 p/q tagged 且驻寄存器;2 次调用绿"), - # a7 同形:循环携带 2L 态跨迭代驻 reg(每轮 jo 慢路径 + alloc 新对象) - ("t5_loop_carry_2l_reg", - "fn main() -> int:\n x : ., mut = 0\n i : ., mut = 0\n while i < 6:\n x = x + 4611686018427387904\n i = i + 1\n r := syscall3(1, 1, x, 16)\n return r\n", - ["main"], limb_bytes((1 << 64) + (1 << 63)), 16, - "O2 元断言目标:循环携带 x 驻 reg 且运行时达 2L 态(hi=1)——16B 通道验证"), - # c5 同形:双 2L 对象比较(a/b 各自溢出生 2L 堆对象,比较读 tag 解指针) - ("t5_two2l_cmp_reg", - "fn main() -> int:\n a : ., mut = 4611686018427387904\n a = a + 4611686018427387904\n b : ., mut = 4611686018427387904\n b = b + 4611686018427387904\n if a >= b && a <= b:\n return 1\n return 2\n", - ["main"], None, 1, "O2 元断言目标:a/b 2L 态驻 reg;128 位比较绿"), -] - - -def run_checked(args, label): - cmd = ["nice", "-n", "19", *map(str, args)] - r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) - if r.returncode != 0: - print(f"[FAIL] {label}: exit {r.returncode}") - print(r.stdout[-3000:]) - print(r.stderr[-3000:]) - return False - return True - - -def main() -> int: - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - ok = True - for name, src, ovf_funcs, want_stream, want_exit, desc in CASES: - srcf = SCRATCH / f"{name}.cr" - srcf.write_text(src, encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - if not run_checked([COREC, "build", srcf, "-o", out, "--static", - "-O", str(opt)], f"{name} @O{opt} build"): - ok = False - continue - ccr = SCRATCH / f"{name}_o{opt}.ccr" - if not ccr.exists(): - print(f"[FAIL] {name} @O{opt}: .ccr missing at {ccr}") - ok = False - continue - funcs, var_types = load_ir(ccr) - if opt == 1: - regs = dump_regassign(ccr, 1) - if regs: - print(f"[FAIL] {name} @O1: corearch dump REG_ASSIGN = " - f"{len(regs)} != 0(O1 不应有寄存器分配——分配门 = O2)") - ok = False - else: - print(f"[PASS] {name} @O1: REG_ASSIGN = 0(全栈,tagged 只栈平凡)") - if opt == 2: - regs = dump_regassign(ccr, 2) - # 元断言:运行时 2L 溢出所在函数内 tagged∩REG_ASSIGN ≠ ∅ - hit = False - for f in funcs: - if f["name"] not in ovf_funcs: - continue - vs = f["var_start"] - ve = vs + f["var_count"] - tg = detect_tags(f, var_types) - ins = sorted(v for v in tg if vs <= v < ve and v in regs) - if ins: - hit = True - print(f"[PASS] {name} @O2 元断言: func {f['name']} " - f"tagged∩regs={[(v, regs[v]) for v in ins]}") - if not hit: - print(f"[FAIL] {name} @O2 元断言: {ovf_funcs} 内无 tagged 变量" - "被分配寄存器——reg 形态裁决失效(详见测试头注释)") - ok = False - # 行为断言(与 task3/4 同语义面;此处与上列元断言同用例链接) - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, capture_output=True, - timeout=60) - if want_stream is not None and r.stdout != want_stream: - print(f"[FAIL] {name} @O{opt}: stdout {r.stdout.hex()} != " - f"{want_stream.hex()}\n {desc}") - ok = False - continue - if r.returncode != want_exit: - print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " - f"{want_exit}\n {desc}") - ok = False - continue - print(f"[PASS] {name} @O{opt}: exit {r.returncode}" - + (f" stream={want_stream.hex()}" if want_stream else "") - + f"\n {desc}") - print() - print("=== mw task5: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_mw_task6.py b/tests/selfhost/test_mw_task6.py deleted file mode 100644 index 6023d869..00000000 --- a/tests/selfhost/test_mw_task6.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python3 -"""int 多字 M1 Task 6:溢出程序边界套件(M1 收官回归语料补全)。 - -Task 3 已覆盖 CF/op 判别矩阵(m1-m6),Task 4 已覆盖消费者判别 -(return/比较/链式/卫生 r-a/h/d 系列)——本套件补 plan Task 6 要求的 -**边界值组合**:i64max(2^63−1,最大可写字面量)±1 链、i64min(−2^63, -不可写为字面量——恰在编码边界的快值形态)邻域、负数与快慢混合、2L 在 -上下边界处参与比较/链式/循环携带。全部用例 × {O0, O1, O2}。 - -边界事实(语料构造依据): -- i64max = 9223372036854775807 是最大合法字面量(lexer i64max 幅守卫); - −2^63 字面量被拒 → i64min 快值只能经 −(2^63−1) + −1 构造(该步**不** - 溢出——OF 只发生在和 < −2^63,硬件判定与 128 码域在此分界)。 -- 2L 值观测 = syscall3(1,1,x,16) 原始 16B 通道(spec §5:exit 只给低 8 位; - 全 128 断言经测试钩子);快值终点 = return 低字节数学断言。 -- 期望值全部为 Python 独立计算(limb_bytes),非从编译器输出回读。 - -验证面(每用例): -1. 16B 流(期望 2L 的站点)逐字节 == 数学值 2-limb 表示——越界点确实走了 - 慢路径且 128 位真值正确; -2. exit 码 == 16×对象数(有流)或 return 数学值低 8 位(快值终点); -3. 边界比较真值表(e5-e7 判别:128 语义下唯一正确结果,任一「指针比较/ - 低 limb 有符号/分派漏读」实现给反)。 - -需先重建自举编译器:nice -n 19 python3 build_selfhost_native.py -""" -import os -import subprocess -import sys -from pathlib import Path - -BASE = Path(__file__).resolve().parents[2] -BUILD = BASE / "build" -COREC = BUILD / "corec" -SCRATCH = BUILD / "mw_task6_scratch" - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -from test_mw_task4 import limb_bytes # noqa: E402 - -S64 = 1 << 63 -IMAX = S64 - 1 # 9223372036854775807(i64max——最大可写快值字面量) -IMIN1 = -IMAX # -(2^63-1)(最小可写负字面量 = i64min+1) - -# (name, src, 期望 stdout 流(None = 只看 exit), 期望 exit, 意图) -CASES = [ - # ── 上边界:i64max +1 = 恰好一步越界(jo 正溢出 hi=0)── - ("e1_max_plus1", - "fn main() -> int:\n x : ., mut = 9223372036854775807\n x = x + 1\n r := syscall3(1, 1, x, 16)\n return r\n", - limb_bytes(S64), 16, "上边界 +1:i64max → 2^63 恰好一步越界"), - # ── 上边界往返:三步连续越界 + 降级回快值 + 再越界(fits 判据 lo 符号位)── - ("e2_max_chain_roundtrip", - "fn main() -> int:\n x : ., mut = 9223372036854775807\n x = x + 1\n x = x + 1\n x = x + 1\n r1 := syscall3(1, 1, x, 16)\n x = x - 3\n x = x + 1\n r2 := syscall3(1, 1, x, 16)\n return r1 + r2\n", - limb_bytes(S64 + 2) + limb_bytes(S64), 32, - "上边界往返:3 步越界链(2L+快)→ −3 降级回 i64max(fits:hi==signmask(2^63−1)==0)→ +1 再越界"), - # ── 下边界邻域:快值恰在 −2^63(不溢出)+ add/sub 双越界 + 回升降级 ── - ("e3_min_edge_addsub", - "fn main() -> int:\n x : ., mut = -9223372036854775807\n x = x + -1\n x = x + -1\n r1 := syscall3(1, 1, x, 16)\n x = x - 1\n r2 := syscall3(1, 1, x, 16)\n x = x + 3\n return x\n", # exit = (-2^63+1) 低字节 0x01 - limb_bytes(-S64 - 1) + limb_bytes(-S64 - 2), 1, - "下边界:−(2^63−1)+−1 = −2^63 快值不溢出(负 OF 只发生在 < −2^63)→ add/sub 连续越界 → +3 降级回快值 return 1"), - # ── sub 形式的上边界(i64max − −n)+ 降级后再越界 ── - ("e4_sub_neg_edge", - "fn main() -> int:\n x : ., mut = 9223372036854775807\n x = x - -1\n r1 := syscall3(1, 1, x, 16)\n x = x - 1\n x = x - -2\n r2 := syscall3(1, 1, x, 16)\n return r1 + r2\n", - limb_bytes(S64) + limb_bytes(S64 + 1), 32, - "sub 边界:i64max − −1 = 2^63(CF 判别)→ −1 降级 → − −2 快→溢再越界"), - # ── 上下边界同函数双站越界 + 2L vs 2L 高 limb 符号判别 ── - ("e5_two_edge_cross_cmp", - "fn main() -> int:\n a : ., mut = 9223372036854775807\n a = a + 1\n b : ., mut = -9223372036854775807\n b = b + -1\n b = b + -1\n r : ., mut = syscall3(1, 1, a, 16)\n r = r + syscall3(1, 1, b, 16)\n if a > b:\n r = r + 1\n return r\n", # 16+16+1 = 33 - limb_bytes(S64) + limb_bytes(-S64 - 1), 33, - "上下边界混合:双站(上/下各一步越界)+ 2L vs 2L 高 limb 判别(同低 limb 无符号形给反)"), - # ── i64max 边界 fast↔2L 比较真值表(LT/GT 双分派 + EQ/GE 假性)── - ("e6_max_cmp_dispatch", - "fn main() -> int:\n x : ., mut = 9223372036854775807\n x = x + 1\n ok : ., mut = 0\n if 9223372036854775807 < x:\n ok = ok + 1\n if x > 9223372036854775807:\n ok = ok + 1\n if x == 9223372036854775807:\n ok = ok - 10\n if 9223372036854775807 >= x:\n ok = ok - 10\n return ok\n", - None, 2, "i64max 边界比较真值表:max<2^63 两分派方向真、EQ/GE 假(−10 哨兵)"), - # ── i64min 边界:快值 −2^63 vs 2L −2^63−1——同 hi 低 limb 无符号判别 ── - ("e7_min_cmp_lo_unsigned", - "fn main() -> int:\n m : ., mut = -9223372036854775807\n m = m + -1\n n : ., mut = m\n n = n + -1\n o : ., mut = 0\n if n < m:\n o = o + 1\n if m > n:\n o = o + 1\n if n > m:\n o = o + 100\n return o\n", - None, 2, "i64min 边界:快值 −2^63 vs 2L −2^63−1——同 hi(−1) 低 limb 无符号判别(有符号 lo 给 101)"), - # ── 循环携带越过上边界(同站点回边逐轮 2L 化)+ 独立快计数器 ── - ("e8_loop_max_chain", - "fn main() -> int:\n x : ., mut = 9223372036854775807\n i : ., mut = 0\n while i < 3:\n x = x + 1\n i = i + 1\n r := syscall3(1, 1, x, 16)\n return r\n", - limb_bytes(S64 + 2), 16, - "循环携带越过上边界:i64max 起每轮 +1(快→2L 同站点回边链)+ 独立快计数"), -] - - -def run_checked(args, label): - cmd = ["nice", "-n", "19", *map(str, args)] - r = subprocess.run(cmd, cwd=BASE, capture_output=True, text=True, timeout=600) - if r.returncode != 0: - print(f"[FAIL] {label}: exit {r.returncode}") - print(r.stdout[-3000:]) - print(r.stderr[-3000:]) - return False - return True - - -def main() -> int: - if not COREC.exists(): - print("build/corec missing; run build_selfhost_native.py") - return 1 - SCRATCH.mkdir(exist_ok=True) - ok = True - for name, src, want_stream, want_exit, desc in CASES: - srcf = SCRATCH / f"{name}.cr" - srcf.write_text(src, encoding="utf-8") - for opt in (0, 1, 2): - out = SCRATCH / f"{name}_o{opt}" - if not run_checked([COREC, "build", srcf, "-o", out, "--static", - "-O", str(opt)], f"{name} @O{opt} build"): - ok = False - continue - os.chmod(out, 0o755) - r = subprocess.run([str(out)], cwd=BASE, capture_output=True, - timeout=60) - if want_stream is not None: - if r.stdout != want_stream: - print(f"[FAIL] {name} @O{opt}: stdout " - f"{r.stdout.hex()} != {want_stream.hex()} — " - f"2-limb 128 位流数学不正确\n {desc}") - ok = False - continue - ns = len(want_stream) // 16 - want_rc = want_exit if want_exit is not None else 16 * ns - if r.returncode != want_rc: - print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " - f"{want_rc}(16B×{ns} 对象{'+比较加分' if want_exit != 16 * ns else ''})") - ok = False - continue - else: - if r.returncode != want_exit: - print(f"[FAIL] {name} @O{opt}: exit {r.returncode} != " - f"{want_exit}(return 数学值低 8 位判别)\n {desc}") - ok = False - continue - print(f"[PASS] {name} @O{opt}: exit {r.returncode}" - + (f",流 {len(want_stream)}B == {want_stream.hex()}" - if want_stream is not None else "") - + f"\n {desc}") - print() - print("=== mw task6: " + ("ALL PASS" if ok else "FAILURES") + " ===") - return 0 if ok else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/selfhost/test_opt_dex.py b/tests/selfhost/test_opt_dex.py index 9bac5593..81e72191 100644 --- a/tests/selfhost/test_opt_dex.py +++ b/tests/selfhost/test_opt_dex.py @@ -379,8 +379,9 @@ def _four_state_src(setup): return 0 """, 8, "bits", "G2 判别判据·apx 源(写点归一 + 读点定型两段都得对才 = 8)"), # ── M1 tag 对改判的**行为钉**(维护者 2026-09-17 附加判据)── - # 机理:`dex?` 槽型由 `TI_INT`(改前)→ `TI_DEX_S`(改后),而 M1 tag 闭包(`tag2l.cr:117/126/128/135/137`) - # 与序言 tag 卫生(`frame.cr:160`)**同判定式**(`== TI_INT`)⇒ 该槽的 tag 编码随槽型移开。 + # 机理:`dex?` 槽型由 `TI_INT`(改前)→ `TI_DEX_S`(改后),而旧 M1 tag 闭包 + # 与序言 tag 卫生**同判定式**(`== TI_INT`);该旧 tag 路径已随 v9.1 默认 BigInt + # 切换删除。此处保留**行为往返一致**判据(不是断言编码细节): # **要求 = 行为往返一致**(不是断言「不再编码」——那是把实现细节当判据): # 同一函数内**共置**「会走 M1 tag 编码的 int 链」(ADD/SUB 种子 + 拷贝元)与 `dex?` 值 # (形参 + 局部),断言**两侧各自往返无损**。出现任何新行为差异 ⇒ 报 lead(不得用「分类变化」一句带过)。 diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index 5bf989b9..8cf6aeca 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -177,7 +177,7 @@ # **后来人注意**:**任何新增编译器侧全局**都会移动这张表(登记 ⇒ 启动期 intern ⇒ id 顺移)。 # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 -canary_elf pa dfae9d845362856fe3609ecd23fcb2deaaa7eae1d2ca39b7a242695513e9bb09 53430 +canary_elf pa 826d74cf7f099f9758e11a64d443695ff8360327a88e7eb5922ddfd617e0dce5 53430 pa_ccr pa.ccr e581daf5a8e30f91745d3c0b6fb6ae6421422248507ec401a67228ae7c6c2355 96985 pa_static_ccr pa_st.bin.ccr a7d2433b0c78428ed93e434ccd617f1308362d7bb268764712d2972e243485a1 97128 gt_ccr gt.ccr 21b1f39c96bd2242b963bb5a1726eb93b912739f77c31317da055d6b37d88103 143687 @@ -271,3 +271,13 @@ gt_static_ccr gt_st.bin.ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662 # pa_static_ccr 041b6e3e83d915690324c5e453b49652207e9f919080e53d2ddec1060d595f62 # gt_ccr edfd8e98d7b64fdb3a99e62bffc99954879432a48390bedbe98171348a49dd22 # gt_static_ccr bbfe47897c8fb97f9cc38ffbfe6043577604ef86595760db55cd7ac0781b0c97 + +# ── 【本批重锁 2026-10-23】删除旧 2-limb/tag 路径(任务 9 收尾)── +# canary_elf: `dfae9d84…` → `826d74cf…`,size 不变(53430B)。 +# 归因(**编译器全局位移,非发射面回归**):删除 `tag2l.cr` 及 18 个 +# `g_x86_mw_*` 编译期全局(tag_off/cap/count、jo_pos/cap/count/dest/is_sub/resume、 +# oc_pos1/pos2/cap/count/s1/s2/dest/op/resume)⇒ `corearch` 启动期登记集缩小 +# ⇒ 被编译程序内嵌 intern id 顺移;机码语义未变,`pa` 运行仍 rc=0。 +# 四条 `.ccr` **逐字节不变**(`.ccr` 由 `corec` 前端生成,不含后端全局)。 +# D1 确定性闸门 5/5 PASS(重锁前置已绿)。 +# 旧值留痕(自本批起作废):canary_elf `dfae9d845362856fe3609ecd23fcb2deaaa7eae1d2ca39b7a242695513e9bb09` 53430。 From cf2a4f3ce129fac7fccaeeee36f820f9105c2f36 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 13:20:15 +0800 Subject: [PATCH 29/58] docs: add pattern-matching IR design --- .../2026-10-23-pattern-matching-ir-design.md | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md diff --git a/docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md b/docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md new file mode 100644 index 00000000..c84aa06d --- /dev/null +++ b/docs/superpowers/specs/2026-10-23-pattern-matching-ir-design.md @@ -0,0 +1,270 @@ +# match 的决策树编译与统一分支节点 + +## 概述 + +`match` 目前在 `src/compiler/ir_gen.cr:2683` 起按 arm 顺序展开为 `IR_CONST`、`IR_BINARY`、`IR_BRANCH` 组成的比较链,corec 的 IR 中只有二元条件分支。字符串、字符、结构体、标识符模式在 `ir_gen` 中没有测试或绑定生成,会静默执行第一个 arm。 + +本文定义完整版:把 `IR_BRANCH` 从二元条件分支替换为多路决策节点,`if` 是 case 数为 2 的特例,`match` 在 corec 编译成 Maranget 决策树,corearch 只做机器映射。`.ccr` 升到 v9.1,新增 CASE 段。 + +设计状态:已定,决定见文末“决定”一节。 + +## 核心决定 + +1. 一种分支节点。`IR_BRANCH` 的新形式是 scrutinee、case 表、default 三部分。二元 `if` 是 case 数为 2 的特例,`IR_JUMP` 是无条件跳转的紧凑编码。 +2. 替换,不并存。旧的 `s1=cond, s2=true, s3=false` 编码退役,所有生成点和消费点改用新形式。不新增 `IR_SWITCH`、`IR_TEST` 或第二种控制流。 +3. 决策树在 corec。`match` 使用 Maranget 的模式矩阵算法生成决策树。测试归一化为整数比较、字段投影和 `str_eq` 调用。corearch 不感知模式种类。 +4. 机器映射在 corearch。case 数为 2 时发 `cmp/jcc`,密集 case 发跳转表,稀疏 case 发二分。阈值和布局只在 corearch。 +5. `.ccr` v9.1。主版本保持 9,header 的 reserved 字段改作 revision,写 1。CASE 段作为 tag 9 追加在 IFACE 之后,存 `int value -> label`。NOD 的 `IR_BRANCH` 存 default label、case 起点和 case 数量。v9.0 文件不再读取。 +6. 语义覆盖。string、char、struct、tuple、ident 模式必须有测试或绑定,不能静默命中。 +7. HDFG 参与。值域剪枝、纯度 body 共享、region 热度分阶段接入,不改变控制流形状。 + +## 现状 + +### IR 形状 + +| 位置 | 现状 | +|---|---| +| `src/compiler/ast.cr:611` | `IR_BRANCH : int = 19`,操作数为 `s1=cond, s2=true_label, s3=false_label` | +| `src/compiler/ir_gen.cr:2683` | `EXPR_MATCH` 的 IR 生成入口 | +| `src/compiler/ir_gen.cr:2769` | 每个 int arm 生成 `IR_CONST`、`IR_BINARY(OP_EQ)`、`IR_BRANCH` | +| `src/arch/x86_64/instr.cr:1529` | `IR_BRANCH` 发射 `test`、`je`、`jmp`,二元分支各一条跳转 | +| `src/compiler/ccr_io.cr:112` | `CCR_VERSION = 9`,NOD 记录 40 字节;header 的 reserved 字段当前写 0 | +| `src/compiler/dataflow.cr:337` | HDFG 只把 `IR_BRANCH` 的条件变量记为数据依赖 | +| `src/arch/x86_64/regalloc.cr:526` | 活跃性分析按 `s2`、`s3` 两个 label 收集后继 | +| `src/compiler/cir_cache.cr` | 函数级缓存保存 `op/dest/s1/s2/s3/tk`,不保存 case 表 | + +实测 IR:4 路整数 `match` 生成 4 组 `const pval`、`binary cmp`、`branch`,每组两个跳转。64 路 `match` 是 64 组顺序比较。corearch 没有多路分支可以映射成跳转表。 + +### 模式覆盖 + +`src/compiler/parser.cr:1410` 的 `parse_pattern` 支持下列模式。`ir_gen` 的 `EXPR_MATCH` 只处理前四项。 + +| 模式 | parser | ir_gen | 当前结果 | +|---|---|---|---| +| wildcard `_` | 支持 | 支持 | 默认分支 | +| int 字面量 | 支持 | 支持 | 逐 arm 比较 | +| bool 字面量 | 支持 | 支持 | 逐 arm 比较 | +| enum 变体 | 支持 | 支持 | 读 tag 后比较 | +| string 字面量 | 支持 | 没有测试 | 静默执行第一个 arm | +| char 字面量 | 支持 | 没有测试 | 静默执行第一个 arm | +| struct 模式 | 支持 | 没有测试 | 静默执行第一个 arm;checker 不校验字段存在性,也不把字段子模式的 ident 加入作用域 | +| tuple 模式 | `grammar/core.ebnf` 有定义,`parse_pattern` 没有 `(` 分支 | 没有测试 | 语法错误 | +| ident 绑定 | 支持 | 没有绑定 | 变量不可用 | + +char 的运行期值当前是字符驻留字符串的索引。实测 `'A' as int` 得到 14,不是 65。`EXPR_CHAR.int_val` 存的也是驻留索引。char 模式直接比较 `ast_int_val` 与运行期值一致;把 char 全局改成码点是独立的语言变更,不在本文范围。 + +### HDFG + +`src/compiler/dataflow.cr` 构造 HDFG:每个 IR 节点是 `DFNode`,数据边是 def-use,state 边表示顺序和终止依赖,region 嵌套用 `SG_IF`、`SG_LOOP`、`SG_FOR`、`SG_FLOW`、`SG_UNSAFE` 表示。现有分析包括: + +- `compute_all_purity`:函数和指令级的纯度。 +- `ptr_analysis.cr`:过程间 Andersen 式 points-to,调用结果保守。 +- `region_check.cr`:DEREF 目标必须在活 region 内。 +- `provenance_verify.cr`:DEREF 偏移必须在 allocation size 内。 +- type engine:枚举和 optional 的穷尽性判定。 + +这些信息可以在 match 编译时使用,但当前 `ir_gen` 没有读取。 + +## 设计 + +### 统一分支节点 + +`IR_BRANCH` 改成多路决策节点。字段含义: + +| 字段 | 含义 | +|---|---| +| `dest` | default label;`-1` 表示没有 default,运行到未命中时 trap | +| `s1` | scrutinee 变量 | +| `s2` | CASE 段的起始索引 | +| `s3` | case 数量 | +| `type_kind` | 比较宽度,保留现有含义 | + +CASE 记录: + +| 字段 | 含义 | +|---|---| +| `value` | int 值、枚举 tag、bool 值或字符驻留索引 | +| `target` | 命中后的目标 label | + +约束: + +- CASE 记录只存整数值。字符串、结构体和 tuple 模式在 corec 归一化,不进入 CASE 段。 +- `if` 编码为一个 case `{1 -> then}` 加 `dest=else`。corearch 对 `s3 == 1` 且 `value == 1` 发射 `test`、`jne`,与现有 `IR_BRANCH` 的机器码相同。 +- `while` 的条件分支使用相同编码,回边由 `IR_JUMP` 表示。 +- `IR_JUMP` 保留,表示无条件跳转。语义上它是没有 case、只有一个 default 的决策节点,编码上继续使用 `s1=label`。 +- `IR_LABEL` 保留,表示基本块入口。 +- 旧的 `IR_BRANCH` 二元编码不再生成。 + +### corec 决策树 + +输入是 match arms 的 pattern 矩阵。第一列选择按 Maranget 的规则:选第一个包含 refutable pattern 的列。对单列标量模式,corec 生成一个 `s3=N` 的多路 `IR_BRANCH`,只对 case 值排序和去重,不拆成二叉。二分和跳转表都由 corearch 决定。对嵌套模式,corec 递归生成多个 `IR_BRANCH` 和字段投影。 + +测试归一化: + +| 模式 | 归一化 | 使用的现有指令 | +|---|---|---| +| int、bool、char | 整数值 | `IR_CONST`、`IR_BRANCH` | +| enum | `IR_LOAD_ENUM_TAG` 后按 tag 分派 | `IR_LOAD_ENUM_TAG`、`IR_BRANCH` | +| optional | 先按表示位(裸值或装箱)分派,再按 tag 或值分派 | 现有表示位指令、`IR_BRANCH` | +| struct、tuple | `IR_LOAD_FIELD` 后对子值测试 | `IR_LOAD_FIELD`、`IR_BRANCH` | +| string | `IR_CALL str_eq` 返回 0 或 1,再按结果分派 | `IR_CALL`、`IR_BRANCH` | +| ident | irrefutable,绑定 scrutinee 或子值 | `IR_STORE` 或直接绑定 | +| wildcard | default 分支 | `IR_BRANCH` 的 `dest` | + +optional 的处理有现成逻辑:`ir_gen.cr:2737` 起在枚举 tag 分派前判断裸值和装箱,裸 optional 不是指针,直接读 tag 会出错。完整版把表示位纳入 Maranget 的列模型,见“决定”第 2 条。第一版保留现有 optional 特例链,其他模式走决策树。 + +字符串模式的完整版策略: + +1. 先按字符串长度分组,生成长度 `IR_BRANCH`。 +2. 同一长度内按首字节分组,生成首字节 `IR_BRANCH`。 +3. 组内逐条调用 `str_eq`,按返回值生成二路 `IR_BRANCH`。 +4. 没有命中的路径走 default。 + +分组只减少 `str_eq` 调用次数,不改变语义。第一版可以只做逐条 `str_eq`,分组作为后续优化。 + +列选择启发式: + +- case 值排序后生成一个多路节点。corearch 的二分实现负责平衡。 +- Scott 和 Ramsey 的实验结论是,多数程序上不同启发式生成的树大小相同;机器生成的程序可能差 2 到 20 倍。corec 第一版采用排序加去重,不实现 necessity 启发式。 +- arm 数量小于 4 时保留顺序比较,避免多路节点的管理开销。 + +重复值: + +- 同一个 case 值出现多次时,保留第一个 arm,后续 arm 是冗余。checker 对枚举和 optional 已经报 `EC_TM_REDUNDANT`;字面量冗余检查在完整版补齐。 +- test 本身没有副作用,case 的测试顺序不影响语义。 + +body 共享: + +- `compute_all_purity` 在 `df_state_finalize` 里运行,晚于 match 的 IR 生成。生成期不能直接读取被调函数的纯度。 +- 完整版在纯度计算之后增加一次延迟重定向:两个 arm 的 body 是相同 AST,且纯度判定为纯时,把两个 case 的 target 指向同一个 label。 +- 第一版不做 body 共享。阶段 5 实现延迟重定向,并保证绑定环境一致。 + +值域剪枝: + +- scrutinee 的 producer 是常量、`load8`、bool 比较或 `IR_LOAD_ENUM_TAG` 时,记录可能值集合。 +- case 值与可能值集合不相交时,不生成该 case。 +- 值域分析只做函数内、沿 HDFG 数据边回溯。跨过程和 points-to 不在完整版第一轮。 + +### corearch 映射 + +corearch 只看 `IR_BRANCH` 的 case 数量和 value 分布,不看模式种类。 + +| 条件 | 映射 | +|---|---| +| `s3 == 1` 且 `value == 1` | `test/jne`,与现有二元分支同码 | +| `s3 == 2` 且 default 只有一个 | `cmp/jcc` | +| `s3 > 2` 且 value 密集 | 跳转表 | +| `s3 > 2` 且 value 稀疏 | 二分比较树 | +| `s3 == 0` 且 `dest >= 0` | 无条件跳转 | +| `dest == -1` 且没有 case 命中 | trap 或 `ud2` | + +密集的初始判据:`s3 >= 8` 且 `max(value) - min(value) + 1 <= 2 * s3`。阈值在 benchmark 后调整。 + +跳转表布局: + +- 表项是 32 位相对偏移,放在 rodata。 +- 目标地址等于表项地址加偏移。静态 ELF 下不需要绝对地址重定位。 +- 表前先做范围检查;value 在 `[min, max]` 外时跳 default。 +- 表的标签地址由现有 label patch 机制解析。 + +字符串模式不进入 corearch 的多路映射。corec 已经把它归一化成 `str_eq` 调用和二路 `IR_BRANCH`,corearch 只处理整数。 + +### .ccr v9.1 + +版本和段表: + +- `CCR_VERSION` 保持 9。header 的 reserved 字段改作 revision,写 1。 +- 段表从 8 项增加到 9 项。现有 tag 1 到 8 保持不变,CASE 作为 tag 9 追加在 IFACE 之后。 +- NOD 记录仍为 40 字节。`IR_BRANCH` 的 `dest` 存 default label,`s2` 存 CASE 起始索引,`s3` 存 case 数量。 +- CASE 记录为 16 字节:`value` 为 8 字节有符号整数,`target` 为 4 字节 label,保留 4 字节对齐。 + +加载校验: + +- 版本等于 9 且 revision 等于 1,段表为 9 项。 +- `first_case + case_count` 不超过 CASE 段项数。 +- 每个 `target` 和 `dest` 都在函数的 label 范围内。 +- case 数量为 0 时允许,表示只有 default。 +- v9.0 文件(revision 为 0、段表为 8 项)不再读取。缓存目录需要清理一次。 + +### 语义 + +- 首个匹配。重复 case 值保留第一个 arm。 +- case 测试按决策树顺序执行;测试没有副作用,body 只在命中时执行。 +- 绑定在目标基本块内建立。enum payload、struct 字段和 ident 绑定使用现有 `bind_local` 或 `IR_LOAD_FIELD`。 +- 字面量 match 要求 `_` 或 irrefutable arm;缺少时 checker 报 `EC_TM_EXHAUST`,见“决定”第 4 条。 +- `dest` 为 `-1` 且没有 case 命中时,运行到 trap。该形态只在 checker 放行的程序中出现。 +- 字符串比较使用 `str_eq`。空字符串和嵌入 NUL 按现有 string 语义处理。 +- 枚举和 optional 的穷尽性沿用 checker 现有判定。字面量、字符串、char、struct 的穷尽性在完整版补齐。 + +### HDFG 接入 + +| 信息 | 用途 | 阶段 | +|---|---|---| +| 常量、`load8`、bool、enum tag 的值域 | 删除不可达 case,缩小跳转表范围 | 决策树落地后 | +| `compute_all_purity` | 合并相同 body | 纯度计算后的延迟 pass | +| `SG_LOOP`、`SG_FOR` | 给 corearch 热路径提示,优先跳转表 | 跳转表落地后 | +| points-to 和跨过程摘要 | 推断函数返回值的可能集合 | 后续 | +| provenance | 不直接参与 match 编译 | 不在本次范围 | + +HDFG 的 region 信息已经写入 `.ccr` 的 REG 段,corearch 可以直接读取,不需要为热度新增字段。 + +## 约束 + +1. bootstrap desugar 前置。`bootstrap/corec/frontend/desugar.py` 的 `_desugar_match` 当前只支持 Wildcard 和 EnumPattern,其他模式抛 `NotImplementedError`。corec 源码一旦在 `match` 里使用 int、char、string、struct 模式,`build_selfhost_native.py` 的 stage 1 就会失败。实施前必须扩展 bootstrap desugar,覆盖 corec 源码实际用到的模式,或限制 corec 源码只使用 Wildcard 和 EnumPattern。 +2. 编译器源码禁用 `|`、`&` 和 `as`。决策树生成代码也遵守这条限制。 +3. 自举要求 stage 2 和 stage 3 逐字节相同。stage 1 由 Python bootstrap 构建,可以和新决策树代码不同。 +4. 所有长编译和测试使用 `nice -n 19` 或 `cpulimit -l 10`。 +5. `cir_cache.cr` 必须同步。函数级缓存的记录只有 `op/dest/s1/s2/s3/tk`,不包含 CASE 表。含多路 `IR_BRANCH` 的函数如果命中缓存,恢复后 case 索引会指向空表或别的函数的表。阶段 1 必须扩展缓存记录,保存每个函数的 CASE 区间,或对含多路分支的函数禁用缓存。 + +## 实施清单 + +阶段 1 和阶段 2 必须同批落地。阶段 1 单独切换 `.ccr` 和 corearch 时,`ir_gen` 还在发旧编码,`if` 和 `loop` 会被新后端误读,中间状态不能构建。 + +| 阶段 | 文件 | 改动 | 验收 | +|---|---|---|---| +| 0 | `tools/baseline/`、`tests/` | 记录 if、loop、match 的机器码和基准 | 基准可重复 | +| 1+2 | `src/compiler/ast.cr`、`globals.cr`、`ccr_io.cr` | 新 `IR_BRANCH` 字段、CASE 段、v9.1 读写 | 手工构造的 v9.1 文件可读写;v9.0 被拒绝 | +| 1+2 | `src/compiler/cir_cache.cr` | 保存 CASE 区间,或禁用含多路分支的函数缓存 | warm build 不 miscompile | +| 1+2 | `src/compiler/ir_gen.cr` | `match` 生成多路节点;if、while、`&&`、`||` 改用新编码 | 现有 suite 和 probes 通过 | +| 1+2 | `src/compiler/dataflow.cr`、`dump.cr`、`interp.cr` | 多 successor、case 表打印、解释执行 | 解释执行结果与现有 match 相同 | +| 1+2 | `src/arch/x86_64/regalloc.cr`、`sizes.cr`、`instr.cr` | 多 successor 活跃性、case 表读取、二路映射 | if/loop 机器码与阶段 0 相同 | +| 3 | `src/arch/x86_64/instr.cr`、`sizes.cr` | 密集 case 跳转表、稀疏 case 二分 | 64 路基准达到目标 | +| 4 | `src/compiler/ir_gen.cr`、`parser.cr`、`checker.cr` | string、char、struct、tuple、ident 模式;字面量穷尽性;struct 字段校验和 ident 绑定 | 覆盖用例不再静默命中;无 `_` 的字面量 match 报 `EC_TM_EXHAUST` | +| 4 | `bootstrap/corec/frontend/desugar.py` | 扩展模式支持,保持两前端语义 | bootstrap-tests 通过 | +| 5 | `src/compiler/checker.cr`、`ir_gen.cr` | HDFG 值域剪枝、纯度 body 共享、optional 表示位纳入决策树、热度提示 | 基准和诊断用例通过 | + +## 测试与验收 + +1. 行为对拍:`tests/suite`、`tests/probes`、`tests/selfhost` 全部通过。 +2. 自举:`build_selfhost_native.py` 完成三阶段构建,stage 2 和 stage 3 的 `cmp` 结果为 IDENTICAL。 +3. `./build/corec check src/compiler` 返回 0,`error[` 计数为 0。 +4. if、loop、`&&`、`||` 的机器码和阶段 0 逐字节相同,确认二路特化没有回归。 +5. 16 路和 64 路整数 `match` 的基准不低于现有 if 链。64 路由 corearch 的二分实现达到约 6 次比较,corec 只生成一个多路节点。 +6. 合成字符分派基准:0 到 255 的字符值与 40 个 case 的分派,不低于现有线性链;密集 case 在跳转表落地后应明显低于线性链。`src/compiler/lexer.cr` 的改写是后续独立任务,不在本文实施范围。 +7. 编译时间和峰值内存不显著增加。决策树生成的复杂度为 O(n log n),case 表大小与 arm 数线性相关。 +8. canary:ELF 和 `.ccr` 基准值会变化,更新 `tools/baseline/canary_values.tsv` 并在同一批记录原因。 +9. 语义覆盖:无 `_` 的 int/char/string match 报 `EC_TM_EXHAUST`;optional 裸值和装箱两种形态的结果与现有实现一致;char 的 match 值与当前 `'A' as int` 一致。 + +## 不包含 + +1. guard、range pattern、or-pattern。Core 当前没有这些语法。 +2. profile-guided 分支排序。 +3. 跨过程值域分析和函数摘要。 +4. 新的后端或外部链接器。 +5. match 语法变更。 +6. v9.0 兼容读取。 +7. 字符串 pattern 的插值语义。字符串 pattern 只支持无插值的字面量;带插值的 pattern 报诊断。 +8. `src/compiler/lexer.cr` 的 match 改写。它是后续独立任务。 +9. char 改成 Unicode 码点。它是独立语言变更,不在本文范围。 + +## 决定 + +1. char 运行期值。match 第一版直接比较当前 char 值,即字符驻留字符串的索引,不改变 char 语义。char 改成 Unicode 码点作为独立语言变更另行实施。依据是 Rust `char` 和 Haskell `Char` 都使用码点,驻留索引属于编译器内部表示。影响范围包括 lexer、parser、`as int`、ABI 和缓存,不在本文范围。 +2. optional 表示位。完整版把表示位纳入 Maranget 的列模型,作为“表示”伪列;第一版保留现有裸值和装箱特例链。依据是 Rust 的 niche optimization 在 MIR 里仍按 enum 处理,Maranget 的列模型可以扩展表示列。影响阶段 5 和 optional 测试。 +3. tuple 和 struct 模式。完整版补 parser 的 tuple 模式,补 checker 的 struct 字段存在性校验和 ident 绑定。依据是 Rust、Haskell、OCaml 和 SML 都把 tuple 与 struct 模式当作一等模式。影响阶段 4,文件包括 `parser.cr` 和 `checker.cr`。 +4. 字面量穷尽性。要求 `_` 或 irrefutable arm;缺少时 checker 报 `EC_TM_EXHAUST`。依据是 Rust 报 non-exhaustive patterns,Haskell 和 OCaml 也要求穷尽,Maranget 的 usefulness 算法把整数和字符串视为无限域。影响 checker,现有 match 需要扫一遍。 +5. 跳转表阈值。初始判据是 `s3 >= 8` 且 `max-min+1 <= 2*s3`,之后用 benchmark 调整。依据是 LLVM 的 switch lowering 按 case 密度在跳转表、位测试和二分之间选择。影响 corearch。 +6. 跳转表重定位。表项使用 32 位相对偏移,目标是静态 ELF;不支持 PIE 和动态链接。依据是 LLVM 的 PIC 跳转表普遍使用相对偏移。影响 corearch。 +7. bootstrap desugar 范围。覆盖 corec 源码实际使用的模式,不追求覆盖 parser 支持的全部模式。依据是 stage 0 只需要支持 bootstrap 源码。影响 bootstrap;lexer 重写前必须覆盖 int 和 char。 +8. `IR_JUMP`。保留,作为没有 case 的紧凑编码;语义上仍是统一决策节点的特例。依据是 LLVM 和 MIR 都保留无条件跳转 terminator。影响阶段 1 的改动面。 +9. `IR_DYN_DISPATCH`。不并入统一分支节点,保留独立。依据是 dyn 分派属于运行时类型的 vtable 语义,和静态 case 表不同;Rust 的 dyn Trait 也不走 switch。影响后续。 From a50e99a6e48ba5585da05e13cd2038c59564ce6b Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 13:25:08 +0800 Subject: [PATCH 30/58] cli: group hidden debug flags in help output Add a debug marker to CLI flag records and print hidden debug channels in their own help group. Update corec debug flag registrations. --- src/compiler/main.cr | 23 ++++---- src/stdlib/cli.cr | 125 ++++++++++++++++++++++++++++--------------- 2 files changed, 94 insertions(+), 54 deletions(-) diff --git a/src/compiler/main.cr b/src/compiler/main.cr index 20d2598e..41fd1e1b 100644 --- a/src/compiler/main.cr +++ b/src/compiler/main.cr @@ -230,19 +230,20 @@ fn corec_main() -> int: cli_cmd("selftest-purity", "Run effect-purity self tests (Task 1)") cli_flag("output", "o", "Output path") cli_flag_bool("static", "", "Static linking (embed runtime)") - cli_flag("opt-level", "O", "Optimization level (0,1,2,3; default=1) — O1 CSE(corec 进程内);O2 寄存器分配+判定在 corearch(corec build 透传 --opt-level,.ccr 不承载分配结果)") - cli_flag_bool("inject-var-shift", "", "Hidden debug: shift func0 var decl block left by 1, then save (GC-4 test hook)") - cli_flag_bool("inject-cir-skip-journal", "", "Hidden debug: skip .cir intern-journal replay at load (v21 mutation hook; NOT for real builds)") - cli_flag_bool("dump-types", "", "Hidden debug: dump TYPE segment content (row table + term DAG + cross-process judgment probe) after populate (R2 P4 Task 2 test channel)") - cli_flag_bool("dump-ifaces", "", "Hidden debug: dump IFACE segment content (entry table + shapes + user ifaces + impls + method table + cross-segment term probe) after populate (R2 P4 Task 3 test channel)") - cli_flag_bool("dump-tk-terms", "", "Hidden debug: dump per-DFNode tk + type-term slot (cir; R2 P4 Task 4 test channel — cold/warm snapshot symmetry)") + cli_flag("opt-level", "O", "Optimization level (0,1,2,3; default=1). O1 CSE runs in corec; O2 register allocation runs in corearch") + // Debug channels are printed in their own help group. + cli_flag_bool_debug("inject-var-shift", "", "Debug hook: shift func0 var decl block left by 1") + cli_flag_bool_debug("inject-cir-skip-journal", "", "Debug hook: skip .cir journal replay at load") + cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") + cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") + cli_flag_bool_debug("dump-tk-terms", "", "Debug: dump per-DFNode type terms") // The old type-shadow CLI channels were removed; the judgment path has no // switch and always goes through the engine. - cli_flag_bool("verify-named-dedup", "", "R2 P2a: assert side-table == res_type_node for all named types (debug)") - cli_flag_bool("verify-evp-nodes", "", "R2 P3 T4: assert enum variant payload type nodes recorded (debug)") - cli_flag_bool("diag-gate-report", "", "FC T2: report fail-closed gate verdicts per compile (debug; default off — 不改 rc/产物)") - cli_flag_bool("dump-vcs", "", "批 6 T4: dump spec VC list (#check/#ensure) — hidden debug channel; stdout only, no artifact, rc untouched") - cli_flag_bool("dump-params", "", "(乙) A1: dump per-param slot type at prologue (`PARAM fn p name slot= decl=`) — hidden debug channel; stdout only, no artifact, rc untouched") + cli_flag_bool_debug("verify-named-dedup", "", "Debug: verify type side-table consistency") + cli_flag_bool_debug("verify-evp-nodes", "", "Debug: verify enum payload type nodes") + cli_flag_bool_debug("diag-gate-report", "", "Debug: report fail-closed gate verdicts") + cli_flag_bool_debug("dump-vcs", "", "Debug: dump spec VC list") + cli_flag_bool_debug("dump-params", "", "Debug: dump parameter slot types") if cli_parse() != 0: return 1 diff --git a/src/stdlib/cli.cr b/src/stdlib/cli.cr index a0d52f21..71ac2d1c 100644 --- a/src/stdlib/cli.cr +++ b/src/stdlib/cli.cr @@ -34,6 +34,7 @@ struct CliFlag: value: string, has_value: int, is_bool: int, + debug: int, // --- Global state --- g_cli_prog : string, mut @@ -70,8 +71,8 @@ fn grow_cli_flags(needed: int): nc = 8 if nc < needed: nc = needed + 8 - nb := alloc(nc * 48) - _dyncpy(g_cli_flags, cur_cap * 48, nb) + nb := alloc(nc * 56) + _dyncpy(g_cli_flags, cur_cap * 56, nb) g_cli_flags = nb g_cli_flag_cap = nc fn grow_cli_args(needed: int): @@ -116,7 +117,7 @@ fn cli_cmd(name: string, desc: string): fn cli_flag(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) - off : ., mut = g_cli_flag_count * 48 + off : ., mut = g_cli_flag_count * 56 unsafe: w64(g_cli_flags, off, @ptr_of(long_name)) w64(g_cli_flags, off + 8, @ptr_of(short_name)) @@ -124,11 +125,12 @@ fn cli_flag(long_name: string, short_name: string, desc: string): w64(g_cli_flags, off + 24, @ptr_of("")) // value w64(g_cli_flags, off + 32, 0) // has_value w64(g_cli_flags, off + 40, 0) // is_bool + w64(g_cli_flags, off + 48, 0) // debug g_cli_flag_count = g_cli_flag_count + 1 fn cli_flag_bool(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) - off : ., mut = g_cli_flag_count * 48 + off : ., mut = g_cli_flag_count * 56 unsafe: w64(g_cli_flags, off, @ptr_of(long_name)) w64(g_cli_flags, off + 8, @ptr_of(short_name)) @@ -136,6 +138,33 @@ fn cli_flag_bool(long_name: string, short_name: string, desc: string): w64(g_cli_flags, off + 24, @ptr_of("")) // value w64(g_cli_flags, off + 32, 0) // has_value w64(g_cli_flags, off + 40, 1) // is_bool + w64(g_cli_flags, off + 48, 0) // debug + g_cli_flag_count = g_cli_flag_count + 1 + +fn cli_flag_bool_debug(long_name: string, short_name: string, desc: string): + grow_cli_flags(g_cli_flag_count + 1) + off : ., mut = g_cli_flag_count * 56 + unsafe: + w64(g_cli_flags, off, @ptr_of(long_name)) + w64(g_cli_flags, off + 8, @ptr_of(short_name)) + w64(g_cli_flags, off + 16, @ptr_of(desc)) + w64(g_cli_flags, off + 24, @ptr_of("")) // value + w64(g_cli_flags, off + 32, 0) // has_value + w64(g_cli_flags, off + 40, 1) // is_bool + w64(g_cli_flags, off + 48, 1) // debug + g_cli_flag_count = g_cli_flag_count + 1 + +fn cli_flag_debug(long_name: string, short_name: string, desc: string): + grow_cli_flags(g_cli_flag_count + 1) + off : ., mut = g_cli_flag_count * 56 + unsafe: + w64(g_cli_flags, off, @ptr_of(long_name)) + w64(g_cli_flags, off + 8, @ptr_of(short_name)) + w64(g_cli_flags, off + 16, @ptr_of(desc)) + w64(g_cli_flags, off + 24, @ptr_of("")) // value + w64(g_cli_flags, off + 32, 0) // has_value + w64(g_cli_flags, off + 40, 0) // is_bool + w64(g_cli_flags, off + 48, 1) // debug g_cli_flag_count = g_cli_flag_count + 1 // --- Internal helpers --- @@ -147,8 +176,8 @@ fn _cli_find_flag(name: string) -> int: break // Read a string from a 64-bit word through @str_of inside unsafe. unsafe: - ln := @str_of(r64(g_cli_flags, i * 48)) - sn := @str_of(r64(g_cli_flags, i * 48 + 8)) + ln := @str_of(r64(g_cli_flags, i * 56)) + sn := @str_of(r64(g_cli_flags, i * 56 + 8)) if str_len(ln) > 0 && str_eq(ln, name) != 0: return i if str_len(sn) > 0 && str_eq(sn, name) != 0: @@ -244,11 +273,11 @@ fn cli_parse() -> int: println(arg) cli_help() return -1 - if r64(g_cli_flags, fi * 48 + 40) != 0: + if r64(g_cli_flags, fi * 56 + 40) != 0: // is_bool - w64(g_cli_flags, fi * 48 + 32, 1) // has_value + w64(g_cli_flags, fi * 56 + 32, 1) // has_value unsafe: - w64(g_cli_flags, fi * 48 + 24, @ptr_of("1")) + w64(g_cli_flags, fi * 56 + 24, @ptr_of("1")) // value(串字入裸字槽) else: ai = ai + 1 @@ -259,9 +288,9 @@ fn cli_parse() -> int: return -1 val := get_arg(ai) unsafe: - w64(g_cli_flags, fi * 48 + 24, @ptr_of(val)) + w64(g_cli_flags, fi * 56 + 24, @ptr_of(val)) // value - w64(g_cli_flags, fi * 48 + 32, 1) // has_value + w64(g_cli_flags, fi * 56 + 32, 1) // has_value else: // Positional argument grow_cli_args(g_cli_arg_count + 1) @@ -282,19 +311,19 @@ fn cli_eq(cmd: string, expected: string) -> int: fn cli_get(name: string) -> string: fi := _cli_find_flag(name) - if fi >= 0 && r64(g_cli_flags, fi * 48 + 32) != 0: + if fi >= 0 && r64(g_cli_flags, fi * 56 + 32) != 0: // has_value // The return value is a string stored in a raw word; read it through // @str_of inside unsafe. unsafe: - return @str_of(r64(g_cli_flags, fi * 48 + 24)) + return @str_of(r64(g_cli_flags, fi * 56 + 24)) // value return "" fn cli_has(name: string) -> int: fi := _cli_find_flag(name) if fi >= 0: - return r64(g_cli_flags, fi * 48 + 32) // has_value + return r64(g_cli_flags, fi * 56 + 32) // has_value return 0 fn cli_arg(n: int) -> string: @@ -309,6 +338,41 @@ fn cli_arg_count() -> int: // --- Help --- +fn cli_print_flags(debug_only: int): + fi : ., mut = 0 + loop: + if fi >= g_cli_flag_count: + break + unsafe: + f_debug := r64(g_cli_flags, fi * 56 + 48) + if f_debug == debug_only: + f_short := @str_of(r64(g_cli_flags, fi * 56 + 8)) + f_long := @str_of(r64(g_cli_flags, fi * 56)) + f_desc := @str_of(r64(g_cli_flags, fi * 56 + 16)) + if str_len(f_desc) > 0: + print(" ") + if str_len(f_short) > 0: + print("-") + print(f_short) + print(", ") + else: + print(" ") + print("--") + print(f_long) + print(" ") + // Pad to 24 chars total (prefix = " " + short/" " + "--" + " ") + total : ., mut = 9 + if str_len(f_short) > 0: + total = 9 // " " + "-X, " + "--" + one space = 9 + total = total + str_len(f_long) + loop: + if total >= 24: + break + print(" ") + total = total + 1 + println(f_desc) + fi = fi + 1 + fn cli_help(): // usage: prog [-h] {cmd1,cmd2,...} [options] print("usage: ") @@ -376,32 +440,7 @@ fn cli_help(): println("options:") // Built-in -h, --help println(" -h, --help show this help message and exit") - fi : ., mut = 0 - loop: - if fi >= g_cli_flag_count: - break - unsafe: - f_short := @str_of(r64(g_cli_flags, fi * 48 + 8)) - f_long := @str_of(r64(g_cli_flags, fi * 48)) - f_desc := @str_of(r64(g_cli_flags, fi * 48 + 16)) - print(" ") - if str_len(f_short) > 0: - print("-") - print(f_short) - print(", ") - else: - print(" ") - print("--") - print(f_long) - // Pad to 24 chars total (prefix = " " + short/" " + "--") - total : ., mut = 8 - if str_len(f_short) > 0: - total = 8 // " " + "-X, " + "--" = 2+4+2 = 8 - total = total + str_len(f_long) - loop: - if total >= 24: - break - print(" ") - total = total + 1 - println(f_desc) - fi = fi + 1 + cli_print_flags(0) + println("") + println("debug options:") + cli_print_flags(1) From 32a6e001ed33cc7dd31b01bc3c79fc33dc142662 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 13:39:47 +0800 Subject: [PATCH 31/58] frontend: remove machine-specific leakage - split target-owned ABI builtins across x86_64, linux, and runtime; the target root only composes them - move classic value mapping to src/arch/x86_64 and keep the compiler mapping.cr as a target-neutral interface - add the kernel cell API; frontend table accesses no longer call w64/r64/w32/r32/load8/store8/_dyncpy directly - make the target descriptor own the backend command, import roots, and runtime source path - rename the combination root to x86_64-linux-elf (arch x system x container) and record all three in Core.toml - relock the four .ccr canaries after the compiler-side global order changed; ELF canary is unchanged --- build_selfhost_native.py | 32 +- .../2026-10-23-arbitrary-precision-int.md | 2 +- .../2026-10-23-frontend-machine-boundary.md | 103 +++- .../specs/2026-09-10-x86-instance-design.md | 2 + src/arch/hit/lower_to_core.cr | 2 +- src/arch/x86_64/backend_flags.cr | 2 +- src/arch/x86_64/regalloc.cr | 2 +- src/arch/x86_64/x86_frontend_abi.cr | 27 + src/arch/x86_64/x86_mapping.cr | 105 ++++ src/compiler/_import.cr | 7 + src/compiler/big_lit.cr | 26 +- src/compiler/ccr_io.cr | 406 ++++++------- src/compiler/ccr_lower.cr | 22 +- src/compiler/ccr_types.cr | 92 +-- src/compiler/checker.cr | 478 +++++++--------- src/compiler/cir_cache.cr | 296 +++++----- src/compiler/dataflow.cr | 132 ++--- src/compiler/diag.cr | 18 +- src/compiler/dump.cr | 38 +- src/compiler/dyn_arr.cr | 532 ++++++++--------- src/compiler/ext_mgr.cr | 14 +- src/compiler/globals.cr | 20 +- src/compiler/iface_axis.cr | 12 +- src/compiler/iface_registry.cr | 36 +- src/compiler/interp.cr | 536 +++++++++--------- src/compiler/ir_gen.cr | 266 ++++----- src/compiler/lexer.cr | 72 +-- src/compiler/literal.cr | 4 +- src/compiler/main.cr | 65 +-- src/compiler/mapping.cr | 101 +--- src/compiler/module.cr | 198 +++---- src/compiler/monomorph.cr | 120 ++-- src/compiler/opt.cr | 26 +- src/compiler/parser.cr | 232 ++++---- src/compiler/prelude.cr | 39 +- src/compiler/provenance_verify.cr | 38 +- src/compiler/ptr_analysis.cr | 108 ++-- src/compiler/purity_selftest.cr | 50 +- src/compiler/region_check.cr | 44 +- src/compiler/ty_shadow.cr | 106 ++-- src/compiler/type_engine.cr | 54 +- src/compiler/type_selftest.cr | 390 ++++++------- src/compiler/type_terms.cr | 36 +- src/kernel/bytes.cr | 41 ++ src/os/linux/linux_frontend_abi.cr | 19 + src/runtime/runtime_frontend_abi.cr | 27 + src/targets/x86_64-linux-elf/Core.toml | 4 + .../_import.cr | 1 + .../x86_64-linux-elf/frontend_target.cr | 65 +++ .../main.cr | 6 +- src/targets/x86_64-linux-elf/target_abi.cr | 14 + src/targets/x86_64-linux/Core.toml | 1 - tests/selfhost/test_backend_bootstrap.py | 2 +- tools/baseline/canary_values.tsv | 19 +- tools/baseline/parity_run.sh | 2 +- tools/v4_migrate.py | 2 +- 56 files changed, 2651 insertions(+), 2443 deletions(-) create mode 100644 src/arch/x86_64/x86_frontend_abi.cr create mode 100644 src/arch/x86_64/x86_mapping.cr create mode 100644 src/os/linux/linux_frontend_abi.cr create mode 100644 src/runtime/runtime_frontend_abi.cr create mode 100644 src/targets/x86_64-linux-elf/Core.toml rename src/targets/{x86_64-linux => x86_64-linux-elf}/_import.cr (99%) create mode 100644 src/targets/x86_64-linux-elf/frontend_target.cr rename src/targets/{x86_64-linux => x86_64-linux-elf}/main.cr (98%) create mode 100644 src/targets/x86_64-linux-elf/target_abi.cr delete mode 100644 src/targets/x86_64-linux/Core.toml diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 461ce018..05fe9592 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -19,7 +19,7 @@ 架构轴 `arch_x86_64_files` × 格式轴 `format_elf_files` × OS 轴 `os_linux_files` (波 1 落位:Task 1 三轴搬迁 + 组合根 / Task 3/4 frame 入架构轴 / Task 2/5/6 entry·callseq·syscall 入 OS 轴——**全轴已就位**)+ 组合根 - `x86_linux_target_files`(target triple 命名:project-mode 入口链,不入 + `x86_linux_elf_target_files`(target triple 命名:project-mode 入口链,不入 concat——见段注释)。 """ import sys, os, subprocess, io, contextlib @@ -35,6 +35,7 @@ common_files = [ 'src/runtime/arena_globals.cr', 'src/runtime/rt.cr', + 'src/kernel/bytes.cr', 'src/stdlib/io.cr', 'src/stdlib/fmt.cr', 'src/stdlib/cli.cr', @@ -126,15 +127,15 @@ 'src/arch/x86_64/corearch.cr', ] -# 组合根(x86_64-linux target triple):project-mode 入口链——`corec build -# src/targets/x86_64-linux` 的入口 = 该目录 main.cr(load_project 直读)+ +# 组合根(x86_64-linux-elf target triple):project-mode 入口链——`corec build +# src/targets/x86_64-linux-elf` 的入口 = 该目录 main.cr(load_project 直读)+ # _import.cr(load_imports 直读)+ Core.toml(工程名)。**不入 concat**: # main.cr 的 fn main 与 concat 追加的 wrapper 冲突,且其 .cr 经 project-mode # 独立收编(stage0/stage 链即此入口)。仅纳入清单存在性守卫。 -x86_linux_target_files = [ - 'src/targets/x86_64-linux/main.cr', - 'src/targets/x86_64-linux/_import.cr', - 'src/targets/x86_64-linux/Core.toml', +x86_linux_elf_target_files = [ + 'src/targets/x86_64-linux-elf/main.cr', + 'src/targets/x86_64-linux-elf/_import.cr', + 'src/targets/x86_64-linux-elf/Core.toml', ] @@ -316,6 +317,7 @@ def main(): corec_files = [ 'src/runtime/arena_globals.cr', 'src/runtime/rt.cr', + 'src/kernel/bytes.cr', 'src/stdlib/io.cr', 'src/stdlib/fmt.cr', 'src/runtime/bigint.cr', @@ -330,7 +332,13 @@ def main(): 'src/compiler/parser.cr', 'src/compiler/checker.cr', 'src/compiler/mapping.cr', + 'src/arch/x86_64/x86_mapping.cr', 'src/compiler/prelude.cr', + 'src/arch/x86_64/x86_frontend_abi.cr', + 'src/os/linux/linux_frontend_abi.cr', + 'src/runtime/runtime_frontend_abi.cr', + 'src/targets/x86_64-linux-elf/target_abi.cr', + 'src/targets/x86_64-linux-elf/frontend_target.cr', # R2 P0 类型项引擎(建层):类型项 DAG 表 + 构造去重 → 判定引擎(桩)→ 自测通道。 # 置于 checker 之后(计划 Step 9 指定位置);引擎尚无 checker 侧消费者, # 本批为纯增量(checker/ir_gen/后端零改动 = 产物零变化)。 @@ -395,13 +403,13 @@ def main(): # corearch — backend: .ccr → binary/asm # 段顺序 = 行为定义面:common → hit → kernel → 架构轴 → 格式轴 → OS 轴 - # → 后端收尾(与拆分前清单逐一对应)。组合根 x86_linux_target_files 不入 + # → 后端收尾(与拆分前清单逐一对应)。组合根 x86_linux_elf_target_files 不入 # concat(见段注释)——单独纳入存在性守卫。 corearch_files = (common_files + hit_engine_files + kernel_files + arch_x86_64_files + format_elf_files + os_linux_files + backend_support_files) guard_manifest(corearch_files, 'corearch') - guard_manifest(x86_linux_target_files, 'x86_64-linux target') + guard_manifest(x86_linux_elf_target_files, 'x86_64-linux-elf target') compile_and_assemble( concat(corearch_files, wrapper_fn='corearch_main'), label='corearch', @@ -427,6 +435,7 @@ def main(): 'src/stdlib/goroutine.cr', 'src/stdlib/chan.cr', 'src/stdlib/sched.cr', + 'src/kernel/bytes.cr', 'src/compiler/ast.cr', 'src/compiler/ccr_schema.cr', 'src/compiler/globals.cr', @@ -437,6 +446,11 @@ def main(): 'src/compiler/parser.cr', 'src/compiler/checker.cr', 'src/compiler/prelude.cr', + 'src/arch/x86_64/x86_frontend_abi.cr', + 'src/os/linux/linux_frontend_abi.cr', + 'src/runtime/runtime_frontend_abi.cr', + 'src/targets/x86_64-linux-elf/target_abi.cr', + 'src/targets/x86_64-linux-elf/frontend_target.cr', # R2 P1:checker 的判定入口引用桥接层(sh_term_of_ti;影子挂点已随 P5 T5 下线)→ # corelsp 也必须链接 引擎 + 桥接层(相对顺序与 corec 一致;corearch 无 checker 仍不含)。 'src/compiler/type_terms.cr', diff --git a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md index 27ef5983..45b302a4 100644 --- a/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md +++ b/docs/superpowers/plans/2026-10-23-arbitrary-precision-int.md @@ -214,7 +214,7 @@ python3 tests/selfhost/test_bigint_literals.py - `src/arch/x86_64/frame.cr` - `src/arch/x86_64/regalloc.cr` - `src/arch/x86_64/corearch.cr` -- `src/targets/x86_64-linux/main.cr` +- `src/targets/x86_64-linux-elf/main.cr` - 新建 `tests/selfhost/test_bigint_arith.py` ### 步骤 diff --git a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md index fd72ebcf..169a8058 100644 --- a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md +++ b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md @@ -55,7 +55,7 @@ cmp /tmp/corec2 /tmp/corec3 - 新建 `src/arch/x86_64/backend_globals.cr` - 新建 `src/arch/x86_64/backend_arrays.cr` - `build_selfhost_native.py` -- `src/targets/x86_64-linux/_import.cr` +- `src/targets/x86_64-linux-elf/_import.cr` ### 步骤 @@ -64,7 +64,7 @@ cmp /tmp/corec2 /tmp/corec3 3. 从 `src/compiler/globals.cr` 删除这些 `g_x86_*` 声明。保留 `g_opt_*`、`g_sgs`、`.so` 侧表和规格注记侧表。 4. 从 `src/compiler/dyn_arr.cr` 删除 x86 扩容函数。保留 `grow_so_side`、`grow_spec_side` 和其余前端扩容函数。 5. 在 `build_selfhost_native.py` 的 `arch_x86_64_files` 开头加入两个新文件,顺序为 `backend_globals.cr`、`backend_arrays.cr`、`regalloc.cr`。 -6. 在 `src/targets/x86_64-linux/_import.cr` 中加入 `import backend_globals` 和 `import backend_arrays`,位置在 `import regalloc` 之前。 +6. 在 `src/targets/x86_64-linux-elf/_import.cr` 中加入 `import backend_globals` 和 `import backend_arrays`,位置在 `import regalloc` 之前。 ### 验收 @@ -82,16 +82,16 @@ cmp /tmp/corec2 /tmp/corec3 - `src/compiler/backend_flags.cr` 移到 `src/arch/x86_64/backend_flags.cr` - `src/compiler/regalloc-consistency.cr` 移到 `docs/maintainer/design/regalloc-consistency.md` - `build_selfhost_native.py` -- `src/targets/x86_64-linux/_import.cr` +- `src/targets/x86_64-linux-elf/_import.cr` - 相关路径引用 ### 步骤 1. 移动文件,保留文件内部函数名和代码。 2. 更新 `backend_support_files` 中的路径。 -3. 更新 `src/targets/x86_64-linux/_import.cr` 的导入名。 +3. 更新 `src/targets/x86_64-linux-elf/_import.cr` 的导入名。 4. 搜索 `src/compiler/corearch.cr`、`src/compiler/backend_flags.cr` 和 `regalloc-consistency.cr` 的路径引用,全部改为新位置。 -5. 不修改 `src/targets/x86_64-linux/main.cr` 的行为。 +5. 不修改 `src/targets/x86_64-linux-elf/main.cr` 的行为。 ### 验收 @@ -196,7 +196,7 @@ fn mp_offset(index: int, ti: int) -> int - `src/compiler/ty_shadow.cr` - `src/compiler/ast.cr` - `build_selfhost_native.py` -- `src/targets/x86_64-linux/_import.cr` +- `src/targets/x86_64-linux-elf/_import.cr` ### 步骤 @@ -245,5 +245,94 @@ fn mp_offset(index: int, ti: int) -> int ## 未决问题 1. `extern` 的可选类型拒绝规则是否应移到独立的 FFI 映射层。当前保留在解析器,因为它描述外部接口的表示能力,而不是目标机器细节。 -2. `IR_ALLOC_ARRAY` 的元素步长 8 是否应进入 Mapping 接口。当前后端固定按 8 计算,前置改动需要同时修改 IR 语义和后端发射。 +2. `IR_ALLOC_ARRAY` 的元素步长已改用 `mp_stride(TI_INT)`;classic x86-64 的值仍是 8,但前端不再直接写 8。后端仍按目标实例的 8 字节槽发射,非经典映射需同时改后端。 3. 64 位整数上限是否解除,取决于多字整数的前端表示和后端运算是否完整覆盖。 + +## 任务 8:目标内建表与三轴描述(已实施) + +### 目标标识 + +三轴显式化,不再是只含架构和系统的组合名: + +```text +arch = x86_64 +system = linux +container = elf +target = x86_64-linux-elf +root = src/targets/x86_64-linux-elf/ +``` + +`Core.toml` 记录 `arch`、`system`、`container`;`src/targets/x86_64-linux-elf/frontend_target.cr` 提供 target name、import roots、backend command 和 runtime source 路径。 + +### 文件 + +- 新建 `src/targets/x86_64-linux-elf/frontend_target.cr` +- 新建 `src/targets/x86_64-linux-elf/target_abi.cr` +- 新建 `src/arch/x86_64/x86_frontend_abi.cr` +- 新建 `src/os/linux/linux_frontend_abi.cr` +- 新建 `src/runtime/runtime_frontend_abi.cr` +- 删除 `src/compiler/checker.cr` 的内建名表;`prelude.cr` 只保留表容器 +- `build_selfhost_native.py`、两个 `_import.cr` + +### 结果 + +- `checker.cr` 不再出现 `w64/r64/w32/load8/store8/load64/_dyncpy/load_str_ptr/store_str_ptr` 等 ABI 名字。 +- 固定宽度/裸内存内建归 `x86_64` 架构轴,`get_arg`/syscall 归 `linux` OS 轴,`alloc`/sched/fiber 归 runtime;target 根只组合三轴。 +- `prelude_builtin_return_ti` 仍只回答 syscall3/4;内建表顺序由 target 组合决定。 + +## 任务 9:Mapping 与语义字节收口(已实施) + +### 文件 + +- `src/compiler/mapping.cr` 改为纯接口 +- `src/arch/x86_64/x86_mapping.cr` 保存 classic x86-64 常量 +- `src/compiler/ir_gen.cr` +- `src/compiler/ptr_analysis.cr` +- `src/compiler/interp.cr` +- `src/compiler/provenance_verify.cr` + +### 结果 + +- `ir_gen` 的数组/元组分配、`ptr_access_width` 回退改走 `mp_stride`。 +- `ptr_analysis` 的指针加减改走 `mp_offset`。 +- 解释器的指针加法/减法/差值改走 `mp_offset` / `mp_stride`。 +- `MP_WORD_BYTES`、`MP_SLICE_BYTES`、char/bool 尺寸只存在于架构轴的 `x86_mapping.cr`;`src/compiler` 不再保存目标布局常量。 + +## 任务 10:前端自身的 cell 层(已实施) + +### 文件 + +- `src/kernel/bytes.cr` +- `src/compiler/dyn_arr.cr` +- `src/compiler/*.cr` + +### 结果 + +- `dyn_arr.cr` 删除 `w8/bu8/w32/r32/w64/r64/_dyncpy` 定义。 +- `src/kernel/bytes.cr` 提供 `cell_load/cell_store/cell_load32/cell_store32/cell_byte/cell_copy/...` 低层 API。 +- `src/compiler` 的表格访问改为 `cell_*`;`w64/r64/...` 只留在 kernel 低层和 target/backend 轴。 +- 这是一次机械 API 收口,不改变 cell 宽度;若将来非 64 位宿主,替换点在 `bytes.cr`。 + +## 任务 11:驱动与导入路径(已实施) + +### 文件 + +- `src/compiler/main.cr` +- `src/compiler/module.cr` +- `src/targets/x86_64-linux-elf/frontend_target.cr` + +### 结果 + +- `main.cr` 不再拼 `corearch` 命令、`--elf`、`--static`、`--link auto`;改由 `target_backend_command`。 +- `read_static_runtime_source` 不再写死 `src/runtime/rt.cr`;改由 target 描述。 +- `module.cr` 的 import 搜索根、`.so` index 路径改由 target 描述提供;前端不再写死 `src/format/elf`、`src/arch/x86_64`、`src/os/linux`、`src/targets/x86_64-linux-elf`。 +- `src/targets/x86_64-linux` 旧目录改名为 `src/targets/x86_64-linux-elf`;`test_backend_bootstrap.py`、`parity_run.sh` 同步。 + +### 本批验证(实测) + +- `python3 build_selfhost_native.py` 成功,三个 guard 通过。 +- `test_backend_bootstrap.py` 11/11,stage1/2/3 byte-identical。 +- `corec2`/`corec3` 自举 `cmp` 一致。 +- BigInt 五套、`test_view_builtins`、`test_ccr_types` 49/49、`test_native_memory` 6/6 通过。 +- canary D1 5/5;`.ccr` 四条按“编译器侧全局登记顺序变化”重锁,ELF canary 不变。 +- `test_opt_dex` 仍有 8 项 optional-dex 聚合/返回/全局路径失败;与本次 target/cell 边界无关,单独登记。 diff --git a/docs/superpowers/specs/2026-09-10-x86-instance-design.md b/docs/superpowers/specs/2026-09-10-x86-instance-design.md index 7522b6a2..7588c509 100644 --- a/docs/superpowers/specs/2026-09-10-x86-instance-design.md +++ b/docs/superpowers/specs/2026-09-10-x86-instance-design.md @@ -8,6 +8,8 @@ x86 实例是三个轴的组合: 架构 x86_64 × 格式 ELF × OS Linux ``` +组合根目录必须显式包含三部分:`src/targets/x86_64-linux-elf/`;`Core.toml` 同时记录 `arch = "x86_64"`、`system = "linux"`、`container = "elf"`。旧的两段名 `x86_64-linux` 只表示架构和系统,不能作为完整目标名。 + 架构轴提供指令、寄存器、编码和序列。格式轴提供容器、段、重定位和链接。OS 轴提供系统调用、入口和 ABI 约定。 三个轴都使用同一模式:通用机制加特化参数。换实例就是换轴组合。例如 `arm64 × ELF × Linux` 是换架构轴,`x86_64 × PE × Windows` 是换格式和 OS 轴。内核和其余轴零改动。 diff --git a/src/arch/hit/lower_to_core.cr b/src/arch/hit/lower_to_core.cr index 2ed1a49a..71e30167 100644 --- a/src/arch/hit/lower_to_core.cr +++ b/src/arch/hit/lower_to_core.cr @@ -34,7 +34,7 @@ // // This file is part of two builds: the corearch concat list, after common and // before the kernel and architecture files, and the project-mode unit -// src/targets/x86_64-linux/_import.cr. The import lines also let standalone +// src/targets/x86_64-linux-elf/_import.cr. The import lines also let standalone // `corec check` and project mode resolve symbols; concat builds strip them. import io diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index 9961935a..ba7daeb3 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -1,6 +1,6 @@ // 后端 CLI 旗标注册单源。 // concat 入口(corearch.cr)与组合根 project-mode 入口 -// (src/targets/x86_64-linux/main.cr)共用本函数,避免两处注册表漂移。 +// (src/targets/x86_64-linux-elf/main.cr)共用本函数,避免两处注册表漂移。 fn register_backend_flags(): cli_flag_bool("elf", "", "Output ELF binary (default)") cli_flag_bool("shared", "", "Output shared library (.so)") diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index e8796afa..24423564 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -898,7 +898,7 @@ fn alloc_registers(): // nod_* 访问器读 g_v7_nod_sem 的 28 字节语义字段,nod_edge_first 和 nod_edge_count // 读邻接域。REG 展开和函数边界回填(root span 到 g_ir_func_instr_start 和 // g_ir_func_instr_count)留在 loader。 -// 调用方是 corearch.cr 和组合根 src/targets/x86_64-linux/main.cr;load_ccr 成功后、 +// 调用方是 corearch.cr 和组合根 src/targets/x86_64-linux-elf/main.cr;load_ccr 成功后、 // 分派和发射前调用一次。 fn build_linear_schedule(): grow_ir_instrs(g_v7_nod_count) diff --git a/src/arch/x86_64/x86_frontend_abi.cr b/src/arch/x86_64/x86_frontend_abi.cr new file mode 100644 index 00000000..2fec6b8a --- /dev/null +++ b/src/arch/x86_64/x86_frontend_abi.cr @@ -0,0 +1,27 @@ +// === arch/x86_64/frontend_abi.cr === +// Architecture-axis frontend ABI builtins. +// +// Fixed-width raw memory and string-header view names are x86-64 facts. The +// semantic checker does not own them; it consumes the target composition table +// through prelude_builtin_*. + +fn x86_frontend_builtin_register(): + prelude_builtin_add("load8", TI_INT) + prelude_builtin_add("store8", TI_INT) + prelude_builtin_add("load64", TI_INT) + prelude_builtin_add("r64", TI_INT) + prelude_builtin_add("w32", TI_UNIT) + prelude_builtin_add("w64", TI_UNIT) + prelude_builtin_add("_dyncpy", TI_UNIT) + prelude_builtin_add("load_str_ptr", TI_STR) + prelude_builtin_add("store_str_ptr", TI_INT) + +fn x86_frontend_builtin_is_str_ptr_load(name: string) -> int: + if name == "load_str_ptr": + return 1 + return 0 + +fn x86_frontend_builtin_is_str_ptr_store(name: string) -> int: + if name == "store_str_ptr": + return 1 + return 0 diff --git a/src/arch/x86_64/x86_mapping.cr b/src/arch/x86_64/x86_mapping.cr new file mode 100644 index 00000000..82c0404f --- /dev/null +++ b/src/arch/x86_64/x86_mapping.cr @@ -0,0 +1,105 @@ +// === x86_mapping.cr === +// Classic x86-64 mapping for value extents and slot strides. +// This file is target-owned; the semantic front end only calls the mp_* +// interface in src/compiler/mapping.cr. A non-classic target replaces this +// implementation without changing the IR passes. + +X86_MP_WORD_BYTES : int = 8 +X86_MP_SLICE_BYTES : int = 16 + +// Value size in bytes. +fn x86_mp_size(ti: int) -> int: + if ti == TI_DEX_S: + return X86_MP_WORD_BYTES + // TI_DEX_S uses an 8-byte slot and is not in the type table. Row 8 is a + // placeholder; user types start at 9. + if ti == TI_CHAR: + return 4 + // not in type table + if ti < 0 || ti >= g_type_count: + return X86_MP_WORD_BYTES + k := get_type_kind(ti) + if k == TYP_BASE: + d := get_type_data(ti) + if d == TY_INT: + return X86_MP_WORD_BYTES + if d == TY_DEX: + return X86_MP_WORD_BYTES + if d == TY_BOOL: + return 1 + if d == TY_STRING: + return X86_MP_WORD_BYTES + if d == TY_UNIT: + return 0 + if d == TY_CHAR: + return 4 + return X86_MP_WORD_BYTES + if k == TYP_ARRAY: + elem := get_type_data(ti) + cnt := get_type_extra(ti) + return x86_mp_size(elem) * cnt + if k == TYP_PTR || k == TYP_REF: + return X86_MP_WORD_BYTES + if k == TYP_SLICE: + return X86_MP_SLICE_BYTES + if k == TYP_NAMED: + name_ni := get_type_data(ti) + si : ., mut = 0 + loop: + if si >= g_struct_count: + break + if si_name(si) == name_ni: + return x86_mp_extent(si_field_count(si), TI_INT) + si = si + 1 + return X86_MP_WORD_BYTES + return X86_MP_WORD_BYTES + +// Value alignment in bytes. +fn x86_mp_align(ti: int) -> int: + if ti == TI_DEX_S: + return X86_MP_WORD_BYTES + // TI_DEX_S has 8-byte alignment and is not in the type table. Row 8 is a + // placeholder. + if ti == TI_CHAR: + return 4 + if ti < 0 || ti >= g_type_count: + return X86_MP_WORD_BYTES + k := get_type_kind(ti) + if k == TYP_BASE: + d := get_type_data(ti) + if d == TY_INT: + return X86_MP_WORD_BYTES + if d == TY_DEX: + return X86_MP_WORD_BYTES + if d == TY_BOOL: + return 1 + if d == TY_STRING: + return X86_MP_WORD_BYTES + if d == TY_CHAR: + return 4 + return X86_MP_WORD_BYTES + if k == TYP_ARRAY: + return x86_mp_align(get_type_data(ti)) + if k == TYP_PTR || k == TYP_REF: + return X86_MP_WORD_BYTES + if k == TYP_SLICE: + return X86_MP_WORD_BYTES + if k == TYP_NAMED: + return X86_MP_WORD_BYTES + return X86_MP_WORD_BYTES + +// Slot stride for an array element or an indexed pointer. +fn x86_mp_stride(ti: int) -> int: + return X86_MP_WORD_BYTES + +// Size of a block containing count slots. +fn x86_mp_extent(count: int, ti: int) -> int: + return count * x86_mp_stride(ti) + +// Byte offset of an indexed slot. +fn x86_mp_offset(index: int, ti: int) -> int: + return index * x86_mp_stride(ti) + +// Byte offset of a struct field. +fn x86_mp_field_offset(field_index: int) -> int: + return field_index * X86_MP_WORD_BYTES diff --git a/src/compiler/_import.cr b/src/compiler/_import.cr index a66434cf..40d81208 100644 --- a/src/compiler/_import.cr +++ b/src/compiler/_import.cr @@ -5,6 +5,7 @@ import cli import ast import ccr_schema import globals +import bytes import dyn_arr import literal import big_lit @@ -12,7 +13,13 @@ import lexer import parser import checker import mapping +import x86_mapping import prelude +import x86_frontend_abi +import linux_frontend_abi +import runtime_frontend_abi +import target_abi +import frontend_target import type_terms import type_engine // Interface registry: the entry table and iface_* query API. It is placed after diff --git a/src/compiler/big_lit.cr b/src/compiler/big_lit.cr index 4f050cd9..99b1051f 100644 --- a/src/compiler/big_lit.cr +++ b/src/compiler/big_lit.cr @@ -19,18 +19,18 @@ fn dec_mul_add(dec: string, dec_len: int, base: int, digit: int) -> string: loop: if i >= n: break - v := (load8(dec, i) - 48) * base + carry - store8(out, i, v % 10 + 48) + v := (cell_load_byte(dec, i) - 48) * base + carry + cell_store_byte(out, i, v % 10 + 48) carry = v / 10 i = i + 1 pos : ., mut = n loop: if carry == 0: break - store8(out, pos, carry % 10 + 48) + cell_store_byte(out, pos, carry % 10 + 48) carry = carry / 10 pos = pos + 1 - store8(out, pos, 0) + cell_store_byte(out, pos, 0) g_big_dec_len = pos return out @@ -39,8 +39,8 @@ fn big_lit_decimal(s: string) -> string: sl := str_len(s) base : ., mut = 10 start : ., mut = 0 - if sl >= 2 && load8(s, 0) == 48: - pfx := load8(s, 1) + if sl >= 2 && cell_load_byte(s, 0) == 48: + pfx := cell_load_byte(s, 1) if pfx == 120 || pfx == 88: base = 16 start = 2 @@ -51,13 +51,13 @@ fn big_lit_decimal(s: string) -> string: base = 2 start = 2 dec := alloc(2) - store8(dec, 0, 48) + cell_store_byte(dec, 0, 48) dec_len : ., mut = 1 i : ., mut = start loop: if i >= sl: break - c := load8(s, i) + c := cell_load_byte(s, i) if c == 95: i = i + 1 continue @@ -77,20 +77,20 @@ fn big_lit_decimal(s: string) -> string: loop: if last <= 0: break - if load8(dec, last) != 48: + if cell_load_byte(dec, last) != 48: break last = last - 1 - if last == 0 && load8(dec, 0) == 48: + if last == 0 && cell_load_byte(dec, 0) == 48: z := alloc(2) - store8(z, 0, 48) + cell_store_byte(z, 0, 48) return z out := alloc(last + 2) j : ., mut = 0 loop: if last < 0: break - store8(out, j, load8(dec, last)) + cell_store_byte(out, j, cell_load_byte(dec, last)) last = last - 1 j = j + 1 - store8(out, j, 0) + cell_store_byte(out, j, 0) return out diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index e1fe4e93..3ac5c0a1 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -169,26 +169,26 @@ fn bw_byte(val: int, shift: int) -> int: return 0 fn buf_write_u32(buf: string, pos: int, val: int): - w32(buf, pos, val) + cell_store32(buf, pos, val) fn buf_write_i32(buf: string, pos: int, val: int): - w32(buf, pos, val) + cell_store32(buf, pos, val) fn buf_write_i64(buf: string, pos: int, val: int): - w64(buf, pos, val) + cell_store(buf, pos, val) fn buf_read_u32(buf: string, pos: int) -> int: - b0 := load8(buf, pos) - b1 := load8(buf, pos + 1) - b2 := load8(buf, pos + 2) - b3 := load8(buf, pos + 3) + b0 := cell_load_byte(buf, pos) + b1 := cell_load_byte(buf, pos + 1) + b2 := cell_load_byte(buf, pos + 2) + b3 := cell_load_byte(buf, pos + 3) return b0 + b1 * 256 + b2 * 65536 + b3 * 16777216 fn buf_read_i32(buf: string, pos: int) -> int: - b0 := load8(buf, pos) - b1 := load8(buf, pos + 1) - b2 := load8(buf, pos + 2) - b3 := load8(buf, pos + 3) + b0 := cell_load_byte(buf, pos) + b1 := cell_load_byte(buf, pos + 1) + b2 := cell_load_byte(buf, pos + 2) + b3 := cell_load_byte(buf, pos + 3) val := b0 + b1 * 256 + b2 * 65536 if b3 >= 128: return val + (b3 - 256) * 16777216 @@ -198,15 +198,15 @@ fn buf_read_i64(buf: string, pos: int) -> int: // Little-endian signed 64-bit. The low dword must be read UNSIGNED: // reading it signed (buf_read_i32) would double-subtract for negatives // (e.g. -7 = lo -7 + hi -1 -> -7 + -2^32, not -7). - b0 := load8(buf, pos) - b1 := load8(buf, pos + 1) - b2 := load8(buf, pos + 2) - b3 := load8(buf, pos + 3) + b0 := cell_load_byte(buf, pos) + b1 := cell_load_byte(buf, pos + 1) + b2 := cell_load_byte(buf, pos + 2) + b3 := cell_load_byte(buf, pos + 3) lo := b0 + b1 * 256 + b2 * 65536 + b3 * 16777216 - h0 := load8(buf, pos + 4) - h1 := load8(buf, pos + 5) - h2 := load8(buf, pos + 6) - h3 := load8(buf, pos + 7) + h0 := cell_load_byte(buf, pos + 4) + h1 := cell_load_byte(buf, pos + 5) + h2 := cell_load_byte(buf, pos + 6) + h3 := cell_load_byte(buf, pos + 7) hi : ., mut = h0 + h1 * 256 + h2 * 65536 if h3 >= 128: hi = hi + (h3 - 256) * 16777216 @@ -248,7 +248,7 @@ fn ccr_validate_i32_fields() -> int: if si >= g_sg_count: break f := si * ESZ_SG - if ccr_i32_fits(r64(g_sgs, f + OFF_SG_KIND)) == 0 || ccr_i32_fits(r64(g_sgs, f + OFF_SG_ENTER)) == 0 || ccr_i32_fits(r64(g_sgs, f + OFF_SG_EXIT)) == 0 || ccr_i32_fits(r64(g_sgs, f + OFF_SG_PARENT)) == 0: + if ccr_i32_fits(cell_load(g_sgs, f + OFF_SG_KIND)) == 0 || ccr_i32_fits(cell_load(g_sgs, f + OFF_SG_ENTER)) == 0 || ccr_i32_fits(cell_load(g_sgs, f + OFF_SG_EXIT)) == 0 || ccr_i32_fits(cell_load(g_sgs, f + OFF_SG_PARENT)) == 0: return 0 si = si + 1 return 1 @@ -264,7 +264,7 @@ fn ccr_func_root_sg(func_i: int) -> int: loop: if si >= g_sg_count: break - if r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_FUNC: + if cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_FUNC: if k == func_i: return si k = k + 1 @@ -295,8 +295,8 @@ fn ccr_sym_seg_size() -> int: loop: if fi >= g_ir_func_count: break - pc := r64(g_ir_func_param_count, fi * 8) - vc := r64(g_ir_func_var_count, fi * 8) + pc := cell_load(g_ir_func_param_count, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) sz = sz + ESZ_FUNC_HEAD_DISK + pc * 4 + 4 + vc * ESZ_VARDECL_DISK fi = fi + 1 sz = sz + 4 + g_ir_str_const_count * 4 // str_const_count + str_consts @@ -330,7 +330,7 @@ fn ccr_sym_seg_size() -> int: loop: if mi >= g_opt_meta_count: break - sz = sz + 8 + r32(g_opt_meta, mi * OPT_META_STRIDE + 4) + sz = sz + 8 + cell_load32(g_opt_meta, mi * OPT_META_STRIDE + 4) mi = mi + 1 return sz @@ -388,26 +388,26 @@ fn ccr_collect_edges(edge_counts: string, edge_offs: string) -> int: loop: if ni >= g_ir_instr_count: break - w64(edge_offs, ni * 8, total) + cell_store(edge_offs, ni * 8, total) cnt : ., mut = 0 - eid : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE) + eid : ., mut = cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE) loop: if eid < 0: break if eid >= g_df_edge_count: return -1 // Edge record out of range; reject a corrupted cache restore. - if r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_FROM) != ni: + if cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_FROM) != ni: return -1 - eto := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO) + eto := cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO) if eto <= ni: return -1 // Rule: to_nod must be greater than the source node. - if r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND) > 1: + if cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND) > 1: return -1 cnt = cnt + 1 - eid = r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT) - w64(edge_counts, ni * 8, cnt) + eid = cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT) + cell_store(edge_counts, ni * 8, cnt) total = total + cnt ni = ni + 1 return total @@ -418,16 +418,16 @@ fn ccr_fill_edge_buf(buf: string, edge_offs: string): loop: if ni >= g_ir_instr_count: break - slot : ., mut = r64(edge_offs, ni * 8) - eid : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE) + slot : ., mut = cell_load(edge_offs, ni * 8) + eid : ., mut = cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_FIRST_EDGE) loop: if eid < 0: break - eto := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO) - ekind := r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND) - w64(buf, slot * 8, eto + ekind * 4294967296) + eto := cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO) + ekind := cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND) + cell_store(buf, slot * 8, eto + ekind * 4294967296) slot = slot + 1 - eid = r64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT) + eid = cell_load(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT) ni = ni + 1 // EDG load buffer. After validation, keep the flat edge records for the object @@ -486,12 +486,12 @@ fn populate_nod_objects(): loop: if ii >= g_ir_instr_count: break - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, iri_op(ii)) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, iri_dest(ii)) - w64(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, iri_s1(ii)) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, iri_s2(ii)) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, iri_s3(ii)) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, iri_tk(ii)) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, iri_op(ii)) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, iri_dest(ii)) + cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, iri_s1(ii)) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, iri_s2(ii)) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, iri_s3(ii)) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, iri_tk(ii)) // 项索引(TYPE 段文件空间;-1 = 无项)。缓冲由 corec-only // 的 ccr_types.cr:ccr_nod_item_populate 装填(:本文件在 corearch 清单内, // 此处不得引桥接层符号),本函数纯搬运;缓冲短于节点数(不经 @@ -499,7 +499,7 @@ fn populate_nod_objects(): it0 : ., mut = -1 if ii < g_ccr_nod_item_count: it0 = buf_read_i32(g_ccr_nod_item, ii * 4) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it0) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it0) ii = ii + 1 // --- Save(写侧与 calc 侧一致;段表规范序、段体连续)--- @@ -620,7 +620,7 @@ fn save_ccr(path: string) -> int: loop: if ci >= sl: break - store8(buf, pos, str_load8(si, ci)) + cell_store_byte(buf, pos, str_load8(si, ci)) pos = pos + 1 ci = ci + 1 si = si + 1 @@ -637,13 +637,13 @@ fn save_ccr(path: string) -> int: loop: if gi >= g_ir_global_count: break - if r64(g_ir_globals, gi * 24 + 8) != gi: + if cell_load(g_ir_globals, gi * 24 + 8) != gi: return -1 - buf_write_u32(buf, pos, r64(g_ir_globals, gi * 24)) + buf_write_u32(buf, pos, cell_load(g_ir_globals, gi * 24)) pos = pos + 4 // name buf_write_u32(buf, pos, irv_type(gi)) pos = pos + 4 // type - buf_write_i64(buf, pos, r64(g_ir_globals, gi * 24 + 16)) + buf_write_i64(buf, pos, cell_load(g_ir_globals, gi * 24 + 16)) pos = pos + 8 // init_val gi = gi + 1 @@ -668,30 +668,30 @@ fn save_ccr(path: string) -> int: froot := ccr_func_root_sg(fi) if froot < 0: return -1 - vc := r64(g_ir_func_var_count, fi * 8) - vs := r64(g_ir_func_var_start, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) + vs := cell_load(g_ir_func_var_start, fi * 8) if vs != vcursor: return -1 // 声明区起点 != 前缀累计 -> 记录顺序错位 vcursor = vcursor + vc - pc := r64(g_ir_func_param_count, fi * 8) + pc := cell_load(g_ir_func_param_count, fi * 8) if pc > vc: return -1 // 条目块界实回填(内核 compute_live_ranges 已先行),函数条目 // 块 = ENT 文件序连续段 [es, es+ec)(SYM func 与 REG 根记录同源双写; // loader 块对照消费:pcnt==0 ↔ -1、ffe == 块首、fle == 块末) - es := r64(g_ir_func_entry_start, fi * 8) - ec := r64(g_ir_func_entry_count, fi * 8) + es := cell_load(g_ir_func_entry_start, fi * 8) + ec := cell_load(g_ir_func_entry_count, fi * 8) fe2 : ., mut = -1 le2 : ., mut = -1 if ec > 0: fe2 = es le2 = es + ec - 1 - buf_write_u32(buf, pos, r64(g_ir_func_name_idx, fi * 8)) + buf_write_u32(buf, pos, cell_load(g_ir_func_name_idx, fi * 8)) pos = pos + 4 buf_write_u32(buf, pos, pc) pos = pos + 4 - buf_write_u32(buf, pos, r64(g_ir_func_ret_type, fi * 8)) + buf_write_u32(buf, pos, cell_load(g_ir_func_ret_type, fi * 8)) pos = pos + 4 buf_write_i32(buf, pos, froot) pos = pos + 4 @@ -736,7 +736,7 @@ fn save_ccr(path: string) -> int: loop: if sci >= g_ir_str_const_count: break - buf_write_u32(buf, pos, r64(g_ir_str_consts, sci * 8)) + buf_write_u32(buf, pos, cell_load(g_ir_str_consts, sci * 8)) pos = pos + 4 sci = sci + 1 @@ -802,8 +802,8 @@ fn save_ccr(path: string) -> int: if mi >= g_opt_meta_count: break mo := mi * OPT_META_STRIDE - mk := r32(g_opt_meta, mo) // key (u8 padded to u64) - md_len := r32(g_opt_meta, mo + 4) // data length + mk := cell_load32(g_opt_meta, mo) // key (u8 padded to u64) + md_len := cell_load32(g_opt_meta, mo + 4) // data length buf_write_u32(buf, pos, mk) pos = pos + 4 buf_write_u32(buf, pos, md_len) @@ -812,7 +812,7 @@ fn save_ccr(path: string) -> int: loop: if di >= md_len: break - store8(buf, pos, load8(g_opt_meta, mo + 8 + di)) + cell_store_byte(buf, pos, cell_load_byte(g_opt_meta, mo + 8 + di)) pos = pos + 1 di = di + 1 mi = mi + 1 @@ -848,9 +848,9 @@ fn save_ccr(path: string) -> int: it1 = buf_read_i32(g_ccr_nod_item, ii * 4) buf_write_i32(buf, pos, it1) pos = pos + 4 - buf_write_u32(buf, pos, r64(edge_offs, ii * 8)) + buf_write_u32(buf, pos, cell_load(edge_offs, ii * 8)) pos = pos + 4 // first_edge - buf_write_u32(buf, pos, r64(edge_counts, ii * 8)) + buf_write_u32(buf, pos, cell_load(edge_counts, ii * 8)) pos = pos + 4 // edge_count ii = ii + 1 @@ -863,7 +863,7 @@ fn save_ccr(path: string) -> int: loop: if vz >= g_ir_var_count + 8: break - w64(vcnt, vz * 8, 0) + cell_store(vcnt, vz * 8, 0) vz = vz + 1 ej : ., mut = 0 loop: @@ -875,8 +875,8 @@ fn save_ccr(path: string) -> int: ele := ccr_ent_le(ej) eho := ccr_ent_home(ej) efl := ccr_ent_flags(ej) - ord := r64(vcnt, ev * 8) + 1 - w64(vcnt, ev * 8, ord) + ord := cell_load(vcnt, ev * 8) + 1 + cell_store(vcnt, ev * 8, ord) buf_write_i32(buf, pos, ev) pos = pos + 4 // var_id buf_write_u32(buf, pos, ord) @@ -909,10 +909,10 @@ fn save_ccr(path: string) -> int: if si2 >= g_sg_count: break f := si2 * ESZ_SG - sk2 := r64(g_sgs, f + OFF_SG_KIND) - sen := r64(g_sgs, f + OFF_SG_ENTER) - sex := r64(g_sgs, f + OFF_SG_EXIT) - spa := r64(g_sgs, f + OFF_SG_PARENT) + sk2 := cell_load(g_sgs, f + OFF_SG_KIND) + sen := cell_load(g_sgs, f + OFF_SG_ENTER) + sex := cell_load(g_sgs, f + OFF_SG_EXIT) + spa := cell_load(g_sgs, f + OFF_SG_PARENT) if sex < sen: return -1 // 未闭合 -> ncount 不可派生 @@ -928,8 +928,8 @@ fn save_ccr(path: string) -> int: // 一段;无 = -1)。头注释 3.5 语义:区间 = def_nod 归属,非全块。 fe2 : ., mut = -1 le2 : ., mut = -1 - bes := r64(g_ir_func_entry_start, rfunc * 8) - bec := r64(g_ir_func_entry_count, rfunc * 8) + bes := cell_load(g_ir_func_entry_start, rfunc * 8) + bec := cell_load(g_ir_func_entry_count, rfunc * 8) if sk2 == SG_FUNC: if bec > 0: fe2 = bes @@ -949,8 +949,8 @@ fn save_ccr(path: string) -> int: if sk2 == SG_FUNC: // 根 region span = 函数指令范围(REG = 文件里函数边界的唯一真源, // 与 func 表一致性校验,失配 = 内部状态漂移) - is2 := r64(g_ir_func_instr_start, rfunc * 8) - ic2 := r64(g_ir_func_instr_count, rfunc * 8) + is2 := cell_load(g_ir_func_instr_start, rfunc * 8) + ic2 := cell_load(g_ir_func_instr_count, rfunc * 8) if sen != is2 || sex != is2 + ic2: return -1 buf_write_i32(buf, pos, sk2) @@ -978,7 +978,7 @@ fn save_ccr(path: string) -> int: loop: if ei3 >= edge_total: break - ev := r64(edge_buf, ei3 * 8) + ev := cell_load(edge_buf, ei3 * 8) buf_write_u32(buf, pos, ev % 4294967296) pos = pos + 4 // to_nod buf_write_u32(buf, pos, ev / 4294967296) @@ -996,7 +996,7 @@ fn save_ccr(path: string) -> int: loop: if ct >= g_ccr_type_seg_len: break - store8(buf, pos, load8(g_ccr_type_seg, ct)) + cell_store_byte(buf, pos, cell_load_byte(g_ccr_type_seg, ct)) pos = pos + 1 ct = ct + 1 @@ -1012,7 +1012,7 @@ fn save_ccr(path: string) -> int: loop: if ict >= g_ccr_iface_seg_len: break - store8(buf, pos, load8(g_ccr_iface_seg, ict)) + cell_store_byte(buf, pos, cell_load_byte(g_ccr_iface_seg, ict)) pos = pos + 1 ict = ict + 1 @@ -1036,11 +1036,11 @@ fn save_ccr(path: string) -> int: fn inject_var_shift() -> int: if g_ir_func_count < 1: return 1 - vs := r64(g_ir_func_var_start, 0) + vs := cell_load(g_ir_func_var_start, 0) if vs < 1: return 1 // 无 globals 行可借位 -> 注入前置不满足 - w64(g_ir_func_var_start, 0, vs - 1) + cell_store(g_ir_func_var_start, 0, vs - 1) return 0 // Load: v9 only. Validate the header and canonical segment table, reject @@ -1048,7 +1048,7 @@ fn inject_var_shift() -> int: // segments in canonical order. TYPE and IFACE are required. // Loader output is semantic objects plus guards. The linear NOD-to-g_ir_instrs // rebuild is outside this file: build_linear_schedule in regalloc.cr is called by -// corearch.cr or src/targets/x86_64-linux/main.cr after a successful load. +// corearch.cr or src/targets/x86_64-linux-elf/main.cr after a successful load. // Parse order: STR -> SYM (globals and funcs rebuild the var namespace) -> REG // (derive nstart/ncount and function instruction bounds) -> NOD (40B; 28B // semantic fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT (28B @@ -1254,10 +1254,10 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if ci >= sl: break - store8(s, ci, load8(data, pos)) + cell_store_byte(s, ci, cell_load_byte(data, pos)) pos = pos + 1 ci = ci + 1 - store8(s, sl, 0) + cell_store_byte(s, sl, 0) str_intern(s) si = si + 1 @@ -1291,9 +1291,9 @@ fn load_ccr(data: string, fsize: int) -> int: irv_set_id(gvar, gvar) irv_set_type(gvar, gtype) g_ir_var_count = gvar + 1 - w64(g_ir_globals, gi * 24, gname_ni) - w64(g_ir_globals, gi * 24 + 8, gvar) - w64(g_ir_globals, gi * 24 + 16, ginit_val) + cell_store(g_ir_globals, gi * 24, gname_ni) + cell_store(g_ir_globals, gi * 24 + 8, gvar) + cell_store(g_ir_globals, gi * 24 + 16, ginit_val) g_ir_global_count = gi + 1 gi = gi + 1 @@ -1333,12 +1333,12 @@ fn load_ccr(data: string, fsize: int) -> int: // 每函数必有根 region(df_begin_func) if ffe < -1 || fle < -1 || (ffe == -1) != (fle == -1): return -1 - w64(g_ir_func_name_idx, fi * 8, fname) - w64(g_ir_func_param_count, fi * 8, fpc) - w64(g_ir_func_ret_type, fi * 8, fret) - w64(fn_meta, fi * 24, froot) - w64(fn_meta, fi * 24 + 8, ffe) - w64(fn_meta, fi * 24 + 16, fle) + cell_store(g_ir_func_name_idx, fi * 8, fname) + cell_store(g_ir_func_param_count, fi * 8, fpc) + cell_store(g_ir_func_ret_type, fi * 8, fret) + cell_store(fn_meta, fi * 24, froot) + cell_store(fn_meta, fi * 24 + 8, ffe) + cell_store(fn_meta, fi * 24 + 16, fle) // param_ents(i32 each;内存无消费者,只做越界拒绝) pj : ., mut = 0 loop: @@ -1361,7 +1361,7 @@ fn load_ccr(data: string, fsize: int) -> int: if fvc > (seg_end2 - pos) / ESZ_VARDECL_DISK: return -1 fvs2 := g_ir_var_count - w64(g_ir_func_var_start, fi * 8, fvs2) + cell_store(g_ir_func_var_start, fi * 8, fvs2) dj : ., mut = 0 loop: if dj >= fvc: @@ -1381,7 +1381,7 @@ fn load_ccr(data: string, fsize: int) -> int: dj = dj + 1 if g_ir_var_count - fvs2 != fvc: return -1 - w64(g_ir_func_var_count, fi * 8, fvc) + cell_store(g_ir_func_var_count, fi * 8, fvc) g_ir_func_count = fi + 1 fi = fi + 1 @@ -1401,7 +1401,7 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 scv := buf_read_u32(data, pos) pos = pos + 4 - w64(g_ir_str_consts, sci * 8, scv) + cell_store(g_ir_str_consts, sci * 8, scv) g_ir_str_const_count = sci + 1 sci = sci + 1 @@ -1428,16 +1428,16 @@ fn load_ccr(data: string, fsize: int) -> int: // 读回检查与 16 槽清零循环一并删除;段界核算保留(fc 越段,因此拒绝,判定约定)。 if fc > (seg_end2 - pos) / 8: return -1 - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni) - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni) + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) si_set_field_base(sti, g_si_f_used) // Zero generic slots - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 0) + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 0) zgi : ., mut = 0 loop: if zgi >= 4: break - w64(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + zgi * 8, 0) + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + zgi * 8, 0) zgi = zgi + 1 // Write field data(侧表;磁盘布局逐字段变长,格式不变) fi2 : ., mut = 0 @@ -1477,15 +1477,15 @@ fn load_ccr(data: string, fsize: int) -> int: // 循环删除;段界核算保留(判定约定) if vc > (seg_end2 - pos) / 8: return -1 - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni) - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) ei_set_variant_base(ei, g_ei_v_used) - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 0) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 0) zgi2 : ., mut = 0 loop: if zgi2 >= 4: break - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + zgi2 * 8, 0) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + zgi2 * 8, 0) zgi2 = zgi2 + 1 // Write variant data vi3 : ., mut = 0 @@ -1531,13 +1531,13 @@ fn load_ccr(data: string, fsize: int) -> int: // Allocate and store entry grow_opt_meta(mi + 1) mo := mi * OPT_META_STRIDE - w32(g_opt_meta, mo, mk) - w32(g_opt_meta, mo + 4, md_len) + cell_store32(g_opt_meta, mo, mk) + cell_store32(g_opt_meta, mo + 4, md_len) di : ., mut = 0 loop: if di >= md_len: break - store8(g_opt_meta, mo + 8 + di, load8(data, pos)) + cell_store_byte(g_opt_meta, mo + 8 + di, cell_load_byte(data, pos)) pos = pos + 1 di = di + 1 g_opt_meta_count = mi + 1 @@ -1582,17 +1582,17 @@ fn load_ccr(data: string, fsize: int) -> int: // 未闭合 -> ncount 不可派生 if rfe < -1 || rle < -1 || (rfe == -1) != (rle == -1): return -1 - w64(g_sgs, f + OFF_SG_KIND, rk) - w64(g_sgs, f + OFF_SG_ENTER, ren) - w64(g_sgs, f + OFF_SG_EXIT, rex) - w64(g_sgs, f + OFF_SG_PARENT, rp) - w64(g_sgs, f + OFF_SG_NSTART, ren) - w64(g_sgs, f + OFF_SG_NCOUNT, rex - ren) + cell_store(g_sgs, f + OFF_SG_KIND, rk) + cell_store(g_sgs, f + OFF_SG_ENTER, ren) + cell_store(g_sgs, f + OFF_SG_EXIT, rex) + cell_store(g_sgs, f + OFF_SG_PARENT, rp) + cell_store(g_sgs, f + OFF_SG_NSTART, ren) + cell_store(g_sgs, f + OFF_SG_NCOUNT, rex - ren) if rk == SG_FUNC: if rfcnt >= func_cnt: return -1 // 根记录多于函数记录 - if rfe != r64(fn_meta, rfcnt * 24 + 8) || rle != r64(fn_meta, rfcnt * 24 + 16): + if rfe != cell_load(fn_meta, rfcnt * 24 + 8) || rle != cell_load(fn_meta, rfcnt * 24 + 16): return -1 // REG 根记录条目范围 != SYM func 记录 rfcnt = rfcnt + 1 sg_i = sg_i + 1 @@ -1608,18 +1608,18 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if bfi >= func_cnt: break - rid := r64(fn_meta, bfi * 24) + rid := cell_load(fn_meta, bfi * 24) if rid < 0 || rid >= sg_n: return -1 fr := rid * ESZ_SG - if r64(g_sgs, fr + OFF_SG_KIND) != SG_FUNC: + if cell_load(g_sgs, fr + OFF_SG_KIND) != SG_FUNC: return -1 - ren := r64(g_sgs, fr + OFF_SG_ENTER) - rex := r64(g_sgs, fr + OFF_SG_EXIT) + ren := cell_load(g_sgs, fr + OFF_SG_ENTER) + rex := cell_load(g_sgs, fr + OFF_SG_EXIT) if rex < ren: return -1 - w64(g_ir_func_instr_start, bfi * 8, ren) - w64(g_ir_func_instr_count, bfi * 8, rex - ren) + cell_store(g_ir_func_instr_start, bfi * 8, ren) + cell_store(g_ir_func_instr_count, bfi * 8, rex - ren) bfi = bfi + 1 // === NOD: instructions(36B each)=== @@ -1666,15 +1666,15 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // edge_count // 对象缓冲写(语义字段 = 盘字节镜像,w32/w64 低 32/64 位存储,存取 // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest) - w64(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, tk) - w32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it2) // 项索引(-1 = 无) - w64(g_v7_nod_meta, ii * ESZ_NOD_META, nfe) - w64(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest) + cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, tk) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it2) // 项索引(-1 = 无) + cell_store(g_v7_nod_meta, ii * ESZ_NOD_META, nfe) + cell_store(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec) ii = ii + 1 g_v7_nod_count = instr_cnt @@ -1688,8 +1688,8 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if rfi >= func_cnt: break - ris := r64(g_ir_func_instr_start, rfi * 8) - ric := r64(g_ir_func_instr_count, rfi * 8) + ris := cell_load(g_ir_func_instr_start, rfi * 8) + ric := cell_load(g_ir_func_instr_count, rfi * 8) if ris < 0 || ric < 0 || ris + ric > instr_cnt: return -1 rfi = rfi + 1 @@ -1741,12 +1741,12 @@ fn load_ccr(data: string, fsize: int) -> int: // 定值条目 live_start == def // 内存闭区间表 mo2 := eii * ESZ_ENTRY - w32(g_ir_entries, mo2 + OFF_ENTRY_VAR, ev) - w32(g_ir_entries, mo2 + OFF_ENTRY_DEF, ed) - w32(g_ir_entries, mo2 + OFF_ENTRY_LS, els) - w32(g_ir_entries, mo2 + OFF_ENTRY_LE, ele - 1) - w32(g_ir_entries, mo2 + OFF_ENTRY_HOME, eho) - w32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_VAR, ev) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_DEF, ed) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LS, els) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LE, ele - 1) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_HOME, eho) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl) g_entry_count = eii + 1 eii = eii + 1 // 函数条目段界重建:条目按函数升序成块落盘(compute_entries func_i 升序), @@ -1759,10 +1759,10 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if pf >= func_cnt: break - fvs := r64(g_ir_func_var_start, pf * 8) - fvc := r64(g_ir_func_var_count, pf * 8) + fvs := cell_load(g_ir_func_var_start, pf * 8) + fvc := cell_load(g_ir_func_var_count, pf * 8) pstart := pe - w64(g_ir_func_entry_start, pf * 8, pe) + cell_store(g_ir_func_entry_start, pf * 8, pe) loop: if pe >= g_entry_count: break @@ -1771,9 +1771,9 @@ fn load_ccr(data: string, fsize: int) -> int: break pe = pe + 1 pcnt := pe - pstart - w64(g_ir_func_entry_count, pf * 8, pcnt) - ffe := r64(fn_meta, pf * 24 + 8) - fle := r64(fn_meta, pf * 24 + 16) + cell_store(g_ir_func_entry_count, pf * 8, pcnt) + ffe := cell_load(fn_meta, pf * 24 + 8) + fle := cell_load(fn_meta, pf * 24 + 16) if pcnt == 0: if ffe != -1 || fle != -1: return -1 @@ -1815,8 +1815,8 @@ fn load_ccr(data: string, fsize: int) -> int: // 邻接域读内核对象缓冲 g_v7_nod_meta(原 nod_edge_meta loader 局部, // 对象 = 内核持有物,EDG 检查 // 与对象数据 nod_edge_first/count 同源;检查逻辑零改动) - nfe := r64(g_v7_nod_meta, ei4 * ESZ_NOD_META) - nec := r64(g_v7_nod_meta, ei4 * ESZ_NOD_META + 8) + nfe := cell_load(g_v7_nod_meta, ei4 * ESZ_NOD_META) + nec := cell_load(g_v7_nod_meta, ei4 * ESZ_NOD_META + 8) if nfe != run_off: return -1 // 1 前缀累计失配(段错位/伪造) @@ -1842,7 +1842,7 @@ fn load_ccr(data: string, fsize: int) -> int: if eto <= ei4: return -1 // 3 后向/自环(拓扑不变量) - w64(v7_buf, (run_off + rec) * 8, eto + ekind * 4294967296) + cell_store(v7_buf, (run_off + rec) * 8, eto + ekind * 4294967296) rec = rec + 1 run_off = run_off + nec ei4 = ei4 + 1 @@ -1882,11 +1882,11 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end7): return -1 - w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)) + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)) pos = pos + 8 - w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_DATA, buf_read_i64(data, pos)) + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_DATA, buf_read_i64(data, pos)) pos = pos + 8 - w64(g_types, trow * ESZ_TYPE_ROW + OFF_TR_EXTRA, buf_read_i64(data, pos)) + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_EXTRA, buf_read_i64(data, pos)) pos = pos + 8 trow = trow + 1 g_type_count = trow_cnt @@ -1919,12 +1919,12 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 if ccr_type_term_ref_ok(ttag, ta, tb, tc2, td, tj) == 0: return -1 - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_TAG, ttag) - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_A, ta) - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_B, tb) - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_C, tc2) - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_D, td) - w64(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_TAG, ttag) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_A, ta) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_B, tb) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_C, tc2) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_D, td) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)) g_type_term_count = tj + 1 tj = tj + 1 if pos != seg_end7: @@ -1982,11 +1982,11 @@ fn load_ccr(data: string, fsize: int) -> int: if flit < -1: return -1 fo2 := ifr * ESZ_IFACE_ENTRY - w64(g_iface_entries, fo2 + OFF_IE_AK, fak) - w64(g_iface_entries, fo2 + OFF_IE_TI, fti) - w64(g_iface_entries, fo2 + OFF_IE_NAME, fname) - w64(g_iface_entries, fo2 + OFF_IE_LIT, flit) - w64(g_iface_entries, fo2 + OFF_IE_OPS, fops) + cell_store(g_iface_entries, fo2 + OFF_IE_AK, fak) + cell_store(g_iface_entries, fo2 + OFF_IE_TI, fti) + cell_store(g_iface_entries, fo2 + OFF_IE_NAME, fname) + cell_store(g_iface_entries, fo2 + OFF_IE_LIT, flit) + cell_store(g_iface_entries, fo2 + OFF_IE_OPS, fops) ifr = ifr + 1 g_iface_entry_count = ifc_nat g_iface_registry_ok = 1 @@ -2012,8 +2012,8 @@ fn load_ccr(data: string, fsize: int) -> int: if sterm < 0 || sterm >= tt_count(): return -1 // 项引用域(跨段) - w64(g_iface_shape_names, ifs * 8, sname) - w64(g_iface_shape_terms, ifs * 8, sterm) + cell_store(g_iface_shape_names, ifs * 8, sname) + cell_store(g_iface_shape_terms, ifs * 8, sterm) ifs = ifs + 1 g_iface_shape_count = ifc_shn @@ -2052,11 +2052,11 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if uz >= ESZ_IFACEINFO: break - w8(g_ifaces, ub + uz, 0) + cell_store8(g_ifaces, ub + uz, 0) uz = uz + 1 - w64(g_ifaces, ub + OFF_IF_NAME, uname) - w64(g_ifaces, ub + OFF_IF_METHOD_COUNT, umc) - w64(g_ifaces, ub + OFF_IF_GENERIC_COUNT, ugc) + cell_store(g_ifaces, ub + OFF_IF_NAME, uname) + cell_store(g_ifaces, ub + OFF_IF_METHOD_COUNT, umc) + cell_store(g_ifaces, ub + OFF_IF_GENERIC_COUNT, ugc) ium : ., mut = 0 loop: if ium >= umc: @@ -2079,11 +2079,11 @@ fn load_ccr(data: string, fsize: int) -> int: if mrt < -1 || mrt >= tt_count(): return -1 // 项引用域(跨段) - w64(g_ifaces, mb + OFF_IFM_NAME, mname) - w64(g_ifaces, mb + OFF_IFM_PARAM_COUNT, mpc) - w64(g_ifaces, mb + OFF_IFM_SELF_MODE, msm) - w64(g_ifaces, mb + OFF_IFM_RET_TERM, mrt) - w64(g_ifaces, mb + OFF_IFM_RET_NODE, -1) // corearch 无 AST,因此节点槽恒 -1 + cell_store(g_ifaces, mb + OFF_IFM_NAME, mname) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_COUNT, mpc) + cell_store(g_ifaces, mb + OFF_IFM_SELF_MODE, msm) + cell_store(g_ifaces, mb + OFF_IFM_RET_TERM, mrt) + cell_store(g_ifaces, mb + OFF_IFM_RET_NODE, -1) // corearch 无 AST,因此节点槽恒 -1 mq : ., mut = 0 loop: if mq >= MAX_IFACE_METHOD_PARAMS: @@ -2093,8 +2093,8 @@ fn load_ccr(data: string, fsize: int) -> int: if mt < -1 || mt >= tt_count(): return -1 // 项引用域(跨段) - w64(g_ifaces, mb + OFF_IFM_PARAM_TERMS + mq * 8, mt) - w64(g_ifaces, mb + OFF_IFM_PARAM_NODES + mq * 8, -1) // 节点槽恒 -1 + cell_store(g_ifaces, mb + OFF_IFM_PARAM_TERMS + mq * 8, mt) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_NODES + mq * 8, -1) // 节点槽恒 -1 mq = mq + 1 mp : ., mut = 0 loop: @@ -2104,7 +2104,7 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // 裸码(信息) if mc2 < -1: return -1 - w64(g_ifaces, mb + OFF_IFM_PARAM_TYPES + mp * 8, mc2) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_TYPES + mp * 8, mc2) mp = mp + 1 ium = ium + 1 ifu = ifu + 1 @@ -2130,8 +2130,8 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 if ity < 0 || ity >= g_str_count: return -1 - w64(g_impl_for, ifi * 16, itr) - w64(g_impl_for, ifi * 16 + 8, ity) + cell_store(g_impl_for, ifi * 16, itr) + cell_store(g_impl_for, ifi * 16 + 8, ity) ifi = ifi + 1 g_impl_for_count = ifc_imp @@ -2159,9 +2159,9 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 if mmg < 0 || mmg >= g_str_count: return -1 - w64(g_methods, ifm * 24, mtn) - w64(g_methods, ifm * 24 + 8, mmn) - w64(g_methods, ifm * 24 + 16, mmg) + cell_store(g_methods, ifm * 24, mtn) + cell_store(g_methods, ifm * 24 + 8, mmn) + cell_store(g_methods, ifm * 24 + 16, mmg) ifm = ifm + 1 g_method_count = ifc_met @@ -2245,11 +2245,11 @@ fn ccr_type_surface_dump(): print("row ") print_i(i) print(" kind ") - print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)) + print_i(cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)) print(" data ") - print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)) + print_i(cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)) print(" extra ") - print_i(r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)) + print_i(cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)) println("") i = i + 1 print("terms: ") @@ -2305,15 +2305,15 @@ fn ccr_iface_surface_dump(): print("native ") print_i(ei2) print(" ak ") - print_i(r64(g_iface_entries, eo2 + OFF_IE_AK)) + print_i(cell_load(g_iface_entries, eo2 + OFF_IE_AK)) print(" ti ") - print_i(r64(g_iface_entries, eo2 + OFF_IE_TI)) + print_i(cell_load(g_iface_entries, eo2 + OFF_IE_TI)) print(" name ") - print_i(r64(g_iface_entries, eo2 + OFF_IE_NAME)) + print_i(cell_load(g_iface_entries, eo2 + OFF_IE_NAME)) print(" lit ") - print_i(r64(g_iface_entries, eo2 + OFF_IE_LIT)) + print_i(cell_load(g_iface_entries, eo2 + OFF_IE_LIT)) print(" ops ") - print_i(r64(g_iface_entries, eo2 + OFF_IE_OPS)) + print_i(cell_load(g_iface_entries, eo2 + OFF_IE_OPS)) println("") ei2 = ei2 + 1 print("ifaceshapes: ") @@ -2326,9 +2326,9 @@ fn ccr_iface_surface_dump(): print("shape ") print_i(sh2) print(" name ") - print_i(r64(g_iface_shape_names, sh2 * 8)) + print_i(cell_load(g_iface_shape_names, sh2 * 8)) print(" term ") - print_i(r64(g_iface_shape_terms, sh2 * 8)) + print_i(cell_load(g_iface_shape_terms, sh2 * 8)) println("") sh2 = sh2 + 1 print("ifaceuser: ") @@ -2339,34 +2339,34 @@ fn ccr_iface_surface_dump(): if iu2 >= g_iface_count: break bo := iu2 * ESZ_IFACEINFO - mc3 := r64(g_ifaces, bo + OFF_IF_METHOD_COUNT) + mc3 := cell_load(g_ifaces, bo + OFF_IF_METHOD_COUNT) print("iface ") print_i(iu2) print(" name ") - print_i(r64(g_ifaces, bo + OFF_IF_NAME)) + print_i(cell_load(g_ifaces, bo + OFF_IF_NAME)) print(" methods ") print_i(mc3) print(" generics ") - print_i(r64(g_ifaces, bo + OFF_IF_GENERIC_COUNT)) + print_i(cell_load(g_ifaces, bo + OFF_IF_GENERIC_COUNT)) println("") mj2 : ., mut = 0 loop: if mj2 >= mc3: break mbo := bo + OFF_IF_METHODS + mj2 * ESZ_IFMETHOD - mpc := r64(g_ifaces, mbo + OFF_IFM_PARAM_COUNT) + mpc := cell_load(g_ifaces, mbo + OFF_IFM_PARAM_COUNT) print("method ") print_i(iu2) print(" ") print_i(mj2) print(" name ") - print_i(r64(g_ifaces, mbo + OFF_IFM_NAME)) + print_i(cell_load(g_ifaces, mbo + OFF_IFM_NAME)) print(" params ") print_i(mpc) print(" self ") - print_i(r64(g_ifaces, mbo + OFF_IFM_SELF_MODE)) + print_i(cell_load(g_ifaces, mbo + OFF_IFM_SELF_MODE)) print(" ret ") - print_i(r64(g_ifaces, mbo + OFF_IFM_RET_TERM)) + print_i(cell_load(g_ifaces, mbo + OFF_IFM_RET_TERM)) println("") mq2 : ., mut = 0 loop: @@ -2379,9 +2379,9 @@ fn ccr_iface_surface_dump(): print(" ") print_i(mq2) print(" code ") - print_i(r64(g_ifaces, mbo + OFF_IFM_PARAM_TYPES + mq2 * 8)) + print_i(cell_load(g_ifaces, mbo + OFF_IFM_PARAM_TYPES + mq2 * 8)) print(" term ") - print_i(r64(g_ifaces, mbo + OFF_IFM_PARAM_TERMS + mq2 * 8)) + print_i(cell_load(g_ifaces, mbo + OFF_IFM_PARAM_TERMS + mq2 * 8)) println("") mq2 = mq2 + 1 mj2 = mj2 + 1 @@ -2396,9 +2396,9 @@ fn ccr_iface_surface_dump(): print("impl ") print_i(ip2) print(" trait ") - print_i(r64(g_impl_for, ip2 * 16)) + print_i(cell_load(g_impl_for, ip2 * 16)) print(" type ") - print_i(r64(g_impl_for, ip2 * 16 + 8)) + print_i(cell_load(g_impl_for, ip2 * 16 + 8)) println("") ip2 = ip2 + 1 print("ifacemethods: ") @@ -2411,11 +2411,11 @@ fn ccr_iface_surface_dump(): print("gmethod ") print_i(im2) print(" type ") - print_i(r64(g_methods, im2 * 24)) + print_i(cell_load(g_methods, im2 * 24)) print(" method ") - print_i(r64(g_methods, im2 * 24 + 8)) + print_i(cell_load(g_methods, im2 * 24 + 8)) print(" mangled ") - print_i(r64(g_methods, im2 * 24 + 16)) + print_i(cell_load(g_methods, im2 * 24 + 16)) println("") im2 = im2 + 1 // 跨段引用域检查:逐接口/方法/有效形参的项槽 @@ -2433,15 +2433,15 @@ fn ccr_iface_surface_dump(): if ipc >= g_iface_count: break boc := ipc * ESZ_IFACEINFO - mcc := r64(g_ifaces, boc + OFF_IF_METHOD_COUNT) + mcc := cell_load(g_ifaces, boc + OFF_IF_METHOD_COUNT) mic : ., mut = 0 loop: if mic >= mcc: break mbc := boc + OFF_IF_METHODS + mic * ESZ_IFMETHOD - pcc := r64(g_ifaces, mbc + OFF_IFM_PARAM_COUNT) + pcc := cell_load(g_ifaces, mbc + OFF_IFM_PARAM_COUNT) slots = slots + 1 - rtc := r64(g_ifaces, mbc + OFF_IFM_RET_TERM) + rtc := cell_load(g_ifaces, mbc + OFF_IFM_RET_TERM) if rtc == -1: unbuilt = unbuilt + 1 if rtc >= 0: @@ -2459,7 +2459,7 @@ fn ccr_iface_surface_dump(): if pqc >= pcc: break slots = slots + 1 - ptc := r64(g_ifaces, mbc + OFF_IFM_PARAM_TERMS + pqc * 8) + ptc := cell_load(g_ifaces, mbc + OFF_IFM_PARAM_TERMS + pqc * 8) if ptc == -1: unbuilt = unbuilt + 1 if ptc >= 0: diff --git a/src/compiler/ccr_lower.cr b/src/compiler/ccr_lower.cr index 1879327f..cbb0bd2b 100644 --- a/src/compiler/ccr_lower.cr +++ b/src/compiler/ccr_lower.cr @@ -15,17 +15,17 @@ fn lower_to_ccr(): break idx := g_ir_instr_count grow_ir_instrs(idx + 1) - iri_set_op(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE)) - iri_set_dest(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST)) - iri_set_s1(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1)) - iri_set_s2(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2)) - iri_set_s3(idx, r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3)) + iri_set_op(idx, cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE)) + iri_set_dest(idx, cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST)) + iri_set_s1(idx, cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1)) + iri_set_s2(idx, cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2)) + iri_set_s3(idx, cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3)) // iri_tk is the derived code, not the stored slot value. The node slot // holds a type-term reference; sh_dfn_code_of_slots combines it with the // auxiliary code. The derived code preserves the previous mixed code. iri_set_tk(idx, sh_dfn_code_of_slots( - r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX))) + cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_TK), + cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX))) g_ir_instr_count = idx + 1 ni = ni + 1 @@ -35,10 +35,10 @@ fn lower_to_ccr(): loop: if fi >= g_ir_func_count: break - w64(g_ir_func_instr_start, fi * 8, - r64(g_df_func_node_start, fi * 8)) - w64(g_ir_func_instr_count, fi * 8, - r64(g_df_func_node_count, fi * 8)) + cell_store(g_ir_func_instr_start, fi * 8, + cell_load(g_df_func_node_start, fi * 8)) + cell_store(g_ir_func_instr_count, fi * 8, + cell_load(g_df_func_node_count, fi * 8)) fi = fi + 1 // After lowering, compute per-variable usage counts for optimization passes diff --git a/src/compiler/ccr_types.cr b/src/compiler/ccr_types.cr index 51ee6a5b..3f9384e7 100644 --- a/src/compiler/ccr_types.cr +++ b/src/compiler/ccr_types.cr @@ -67,7 +67,7 @@ fn ccr_iface_populate() -> int: loop: if ii >= g_iface_count: break - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) if mc < 0 || mc > MAX_IFACE_METHODS: print("error: IFACE segment: interface ") print(int_str(ii)) @@ -80,7 +80,7 @@ fn ccr_iface_populate() -> int: if mi >= mc: break mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) + pc := cell_load(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) if pc < 0 || pc > MAX_IFACE_METHOD_PARAMS: print("error: IFACE segment: method ") print(int_str(mi)) @@ -98,12 +98,12 @@ fn ccr_iface_populate() -> int: print(int_str(mi)) println(" return signature is not translatable to a type term (save refused)") return -1 - w64(g_ifaces, mbase + OFF_IFM_RET_TERM, rt) + cell_store(g_ifaces, mbase + OFF_IFM_RET_TERM, rt) pj : ., mut = 0 loop: if pj >= MAX_IFACE_METHOD_PARAMS: break - w64(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pj * 8, -1) // 未用槽 = -1 + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pj * 8, -1) // 未用槽 = -1 pj = pj + 1 pk : ., mut = 0 loop: @@ -119,7 +119,7 @@ fn ccr_iface_populate() -> int: print_i(pk) println(" signature is not translatable to a type term (save refused)") return -1 - w64(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pk * 8, pt) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_TERMS + pk * 8, pt) pk = pk + 1 mi = mi + 1 ii = ii + 1 @@ -135,34 +135,34 @@ fn ccr_type_seg_build() -> int: need := 4 + rc * ESZ_TYPE_ROW + 4 + tc * ESZ_TYPE_TERM_DISK buf := alloc(need + 64) pos : ., mut = 0 - w32(buf, pos, rc) + cell_store32(buf, pos, rc) pos = pos + 4 i : ., mut = 0 loop: if i >= rc: break - w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)) + cell_store(buf, pos, cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND)) pos = pos + 8 - w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)) + cell_store(buf, pos, cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA)) pos = pos + 8 - w64(buf, pos, r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)) + cell_store(buf, pos, cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA)) pos = pos + 8 i = i + 1 - w32(buf, pos, tc) + cell_store32(buf, pos, tc) pos = pos + 4 j : ., mut = 0 loop: if j >= tc: break - w64(buf, pos, tt_tag(j)) + cell_store(buf, pos, tt_tag(j)) pos = pos + 8 - w64(buf, pos, tt_a(j)) + cell_store(buf, pos, tt_a(j)) pos = pos + 8 - w64(buf, pos, tt_b(j)) + cell_store(buf, pos, tt_b(j)) pos = pos + 8 - w64(buf, pos, tt_c(j)) + cell_store(buf, pos, tt_c(j)) pos = pos + 8 - w64(buf, pos, tt_d(j)) + cell_store(buf, pos, tt_d(j)) pos = pos + 8 j = j + 1 g_ccr_type_seg = buf @@ -182,7 +182,7 @@ fn ccr_iface_seg_build() -> int: loop: if ii >= g_iface_count: break - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) need = need + 16 + mc * 80 ii = ii + 1 need = need + 4 + g_impl_for_count * 8 @@ -191,107 +191,107 @@ fn ccr_iface_seg_build() -> int: pos : ., mut = 0 // Section 1: native entries. The count is the constant IFACE_ENTRY_COUNT, not // g_iface_entry_count, so writer and loader use the same value. - w32(buf, pos, IFACE_ENTRY_COUNT) + cell_store32(buf, pos, IFACE_ENTRY_COUNT) pos = pos + 4 ei : ., mut = 0 loop: if ei >= IFACE_ENTRY_COUNT: break eo := ei * ESZ_IFACE_ENTRY - buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_AK)) + buf_write_i32(buf, pos, cell_load(g_iface_entries, eo + OFF_IE_AK)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_TI)) + buf_write_i32(buf, pos, cell_load(g_iface_entries, eo + OFF_IE_TI)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_NAME)) + buf_write_i32(buf, pos, cell_load(g_iface_entries, eo + OFF_IE_NAME)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_iface_entries, eo + OFF_IE_LIT)) + buf_write_i32(buf, pos, cell_load(g_iface_entries, eo + OFF_IE_LIT)) pos = pos + 4 - buf_write_i64(buf, pos, r64(g_iface_entries, eo + OFF_IE_OPS)) + buf_write_i64(buf, pos, cell_load(g_iface_entries, eo + OFF_IE_OPS)) pos = pos + 8 ei = ei + 1 // Section 2: cross-cutting shapes {name_ni, term}. - w32(buf, pos, g_iface_shape_count) + cell_store32(buf, pos, g_iface_shape_count) pos = pos + 4 si : ., mut = 0 loop: if si >= g_iface_shape_count: break - buf_write_i32(buf, pos, r64(g_iface_shape_names, si * 8)) + buf_write_i32(buf, pos, cell_load(g_iface_shape_names, si * 8)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_iface_shape_terms, si * 8)) + buf_write_i32(buf, pos, cell_load(g_iface_shape_terms, si * 8)) pos = pos + 4 si = si + 1 // Section 3: user interfaces, 16-byte header plus 80 bytes per method. - w32(buf, pos, g_iface_count) + cell_store32(buf, pos, g_iface_count) pos = pos + 4 fi : ., mut = 0 loop: if fi >= g_iface_count: break fo := fi * ESZ_IFACEINFO - buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_NAME)) + buf_write_i32(buf, pos, cell_load(g_ifaces, fo + OFF_IF_NAME)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_METHOD_COUNT)) + buf_write_i32(buf, pos, cell_load(g_ifaces, fo + OFF_IF_METHOD_COUNT)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_ifaces, fo + OFF_IF_GENERIC_COUNT)) + buf_write_i32(buf, pos, cell_load(g_ifaces, fo + OFF_IF_GENERIC_COUNT)) pos = pos + 4 buf_write_i32(buf, pos, 0) pos = pos + 4 // pad for alignment - mc2 := r64(g_ifaces, fo + OFF_IF_METHOD_COUNT) + mc2 := cell_load(g_ifaces, fo + OFF_IF_METHOD_COUNT) mj : ., mut = 0 loop: if mj >= mc2: break mo := fo + OFF_IF_METHODS + mj * ESZ_IFMETHOD - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_NAME)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_NAME)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_PARAM_COUNT)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_SELF_MODE)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_SELF_MODE)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_RET_TERM)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_RET_TERM)) pos = pos + 4 pk2 : ., mut = 0 loop: if pk2 >= MAX_IFACE_METHOD_PARAMS: break - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pk2 * 8)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pk2 * 8)) pos = pos + 4 pk2 = pk2 + 1 pq : ., mut = 0 loop: if pq >= MAX_IFACE_METHOD_PARAMS: break - buf_write_i32(buf, pos, r64(g_ifaces, mo + OFF_IFM_PARAM_TYPES + pq * 8)) + buf_write_i32(buf, pos, cell_load(g_ifaces, mo + OFF_IFM_PARAM_TYPES + pq * 8)) pos = pos + 4 pq = pq + 1 mj = mj + 1 fi = fi + 1 // Section 4: impl edges {trait_ni, type_ni}; declaration metadata only. - w32(buf, pos, g_impl_for_count) + cell_store32(buf, pos, g_impl_for_count) pos = pos + 4 gi2 : ., mut = 0 loop: if gi2 >= g_impl_for_count: break - buf_write_i32(buf, pos, r64(g_impl_for, gi2 * 16)) + buf_write_i32(buf, pos, cell_load(g_impl_for, gi2 * 16)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_impl_for, gi2 * 16 + 8)) + buf_write_i32(buf, pos, cell_load(g_impl_for, gi2 * 16 + 8)) pos = pos + 4 gi2 = gi2 + 1 // Section 5: method table {type_ni, method_ni, mangled_ni}, the data behind // iface_find_method. - w32(buf, pos, g_method_count) + cell_store32(buf, pos, g_method_count) pos = pos + 4 mi2 : ., mut = 0 loop: if mi2 >= g_method_count: break - buf_write_i32(buf, pos, r64(g_methods, mi2 * 24)) + buf_write_i32(buf, pos, cell_load(g_methods, mi2 * 24)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_methods, mi2 * 24 + 8)) + buf_write_i32(buf, pos, cell_load(g_methods, mi2 * 24 + 8)) pos = pos + 4 - buf_write_i32(buf, pos, r64(g_methods, mi2 * 24 + 16)) + buf_write_i32(buf, pos, cell_load(g_methods, mi2 * 24 + 16)) pos = pos + 4 mi2 = mi2 + 1 g_ccr_iface_seg = buf @@ -342,7 +342,7 @@ fn ccr_nod_item_populate() -> int: it : ., mut = -1 if sh_tk_face_of_code(iri_op(ii), tk) == 0: it = sh_term_of_ti(tk) - w32(g_ccr_nod_item, ii * 4, it) + cell_store32(g_ccr_nod_item, ii * 4, it) ii = ii + 1 return 0 @@ -377,13 +377,13 @@ fn ccr_iface_selftest_dump(): loop: if ii >= g_iface_count: break - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) mi : ., mut = 0 loop: if mi >= mc: break mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) + pc := cell_load(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) print("ifacesig ") print_i(ii) print(" ") diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index d14abace..c9d42d34 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -16,9 +16,9 @@ fn alloc_type(kind: int, data: int, extra: int) -> int: idx := g_type_count grow_types(idx + 1) - w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_KIND, kind) - w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_DATA, data) - w64(g_types, idx * ESZ_TYPE_ROW + OFF_TR_EXTRA, extra) + cell_store(g_types, idx * ESZ_TYPE_ROW + OFF_TR_KIND, kind) + cell_store(g_types, idx * ESZ_TYPE_ROW + OFF_TR_DATA, data) + cell_store(g_types, idx * ESZ_TYPE_ROW + OFF_TR_EXTRA, extra) g_type_count = idx + 1 return idx @@ -46,7 +46,7 @@ fn named_dedup_init(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_named_dedup = nb g_named_dedup_cap = nc @@ -70,7 +70,7 @@ fn named_dedup_probe(name_idx: int) -> int: p : ., mut = tt_mod(name_idx, cap) slot : ., mut = -1 loop: - k := r64(g_named_dedup, p * 16) + k := cell_load(g_named_dedup, p * 16) if k < 0: slot = p break @@ -94,7 +94,7 @@ fn named_dedup_rehash(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_named_dedup = nb g_named_dedup_cap = nc @@ -103,11 +103,11 @@ fn named_dedup_rehash(): loop: if j >= old_cap: break - k := r64(old, j * 16) + k := cell_load(old, j * 16) if k >= 0: s := named_dedup_probe(k) - w64(g_named_dedup, s * 16, k) - w64(g_named_dedup, s * 16 + 8, r64(old, j * 16 + 8)) + cell_store(g_named_dedup, s * 16, k) + cell_store(g_named_dedup, s * 16 + 8, cell_load(old, j * 16 + 8)) g_named_dedup_count = g_named_dedup_count + 1 j = j + 1 @@ -128,17 +128,17 @@ fn alloc_named_type(name_idx: int) -> int: if name_idx < 0: return alloc_type(TYP_NAMED, name_idx, 0) slot := named_dedup_slot(name_idx) - if r64(g_named_dedup, slot * 16) == name_idx: - return r64(g_named_dedup, slot * 16 + 8) + if cell_load(g_named_dedup, slot * 16) == name_idx: + return cell_load(g_named_dedup, slot * 16 + 8) ti := alloc_type(TYP_NAMED, name_idx, 0) // 回写前重探(见文件头注3) slot2 := named_dedup_slot(name_idx) - if r64(g_named_dedup, slot2 * 16) == name_idx: + if cell_load(g_named_dedup, slot2 * 16) == name_idx: // 防御(当前不可达):既有登记优先 -> 本次刚分配的行成为未引用行(不写侧表、不计数)。 // 反例写法(无条件写 slot2)会覆盖活跃条目 = 丢登记。 - return r64(g_named_dedup, slot2 * 16 + 8) - w64(g_named_dedup, slot2 * 16, name_idx) - w64(g_named_dedup, slot2 * 16 + 8, ti) + return cell_load(g_named_dedup, slot2 * 16 + 8) + cell_store(g_named_dedup, slot2 * 16, name_idx) + cell_store(g_named_dedup, slot2 * 16 + 8, ti) g_named_dedup_count = g_named_dedup_count + 1 return ti @@ -177,9 +177,9 @@ fn named_dedup_verify() -> int: named_rows = named_rows + 1 ni := get_type_data(ti) slot := named_dedup_slot(ni) - if r64(g_named_dedup, slot * 16) != ni: + if cell_load(g_named_dedup, slot * 16) != ni: bad = bad + 1 - else if r64(g_named_dedup, slot * 16 + 8) != ti: + else if cell_load(g_named_dedup, slot * 16 + 8) != ti: bad = bad + 1 ti = ti + 1 // 2. 侧表到类型表,并检查唯一性 @@ -187,9 +187,9 @@ fn named_dedup_verify() -> int: loop: if e >= g_named_dedup_cap: break - ni2 := r64(g_named_dedup, e * 16) + ni2 := cell_load(g_named_dedup, e * 16) if ni2 >= 0: - ti2 := r64(g_named_dedup, e * 16 + 8) + ti2 := cell_load(g_named_dedup, e * 16 + 8) if get_type_kind(ti2) != TYP_NAMED: bad = bad + 1 else if get_type_data(ti2) != ni2: @@ -211,9 +211,9 @@ fn named_dedup_verify() -> int: if get_type_kind(sti) == TYP_NAMED: ident_checked = ident_checked + 1 slot3 := named_dedup_slot(ni3) - if r64(g_named_dedup, slot3 * 16) != ni3: + if cell_load(g_named_dedup, slot3 * 16) != ni3: bad = bad + 1 - else if r64(g_named_dedup, slot3 * 16 + 8) != res_type_node(ai): + else if cell_load(g_named_dedup, slot3 * 16 + 8) != res_type_node(ai): bad = bad + 1 ai = ai + 1 print("[named-dedup] named_rows=") @@ -263,68 +263,22 @@ fn init_types(): // 段内容 = 表本体(生产编译下恰 6 条)。详细边界见 iface_shape_builtin_init 注。 iface_shape_builtin_init() -// ── Runtime builtin declarations (no .cr body, implemented in rt.s) ── -g_rt_builtin_count : int, mut -g_rt_builtin_names : string, mut -g_rt_builtin_ret_types : string, mut - -fn init_builtins(): - g_rt_builtin_count = 0 - g_rt_builtin_names = alloc(8 * 8) - g_rt_builtin_ret_types = alloc(8 * 8) - - builtin_add("load8", TI_INT) - builtin_add("store8", TI_INT) - builtin_add("load64", TI_INT) - builtin_add("r64", TI_INT) - builtin_add("alloc", TI_STR) - builtin_add("get_arg", TI_STR) - builtin_add("w32", TI_UNIT) - builtin_add("w64", TI_UNIT) - builtin_add("_dyncpy", TI_UNIT) - builtin_add("load_str_ptr", TI_STR) - builtin_add("store_str_ptr", TI_INT) - builtin_add("sched_call_0", TI_INT) - builtin_add("sched_call_1", TI_INT) - builtin_add("sched_call_2", TI_INT) - builtin_add("sched_call_3", TI_INT) - builtin_add("sched_call_4", TI_INT) - builtin_add("fiber_switch", TI_INT) - builtin_add("fiber_init", TI_INT) - builtin_add("sched_go", TI_INT) - builtin_add("m_start_workers", TI_UNIT) - builtin_add("g_set_curg", TI_UNIT) - builtin_add("g_get_curg", TI_INT) - -fn builtin_add(name: string, ret_ti: int): - ni := str_intern(name) - if g_rt_builtin_count * 8 + 8 > str_len(g_rt_builtin_names): - nc := g_rt_builtin_count * 2 + 16 - nb := alloc(nc * 8) - _dyncpy(g_rt_builtin_names, g_rt_builtin_count * 8, nb) - g_rt_builtin_names = nb - if g_rt_builtin_count * 8 + 8 > str_len(g_rt_builtin_ret_types): - nc := g_rt_builtin_count * 2 + 16 - nb := alloc(nc * 8) - _dyncpy(g_rt_builtin_ret_types, g_rt_builtin_count * 8, nb) - g_rt_builtin_ret_types = nb - w64(g_rt_builtin_names, g_rt_builtin_count * 8, ni) - w64(g_rt_builtin_ret_types, g_rt_builtin_count * 8, ret_ti) - g_rt_builtin_count = g_rt_builtin_count + 1 +// Runtime builtin declarations moved to the target prelude. The checker only +// consumes g_rt_builtin_* through prelude_builtin_init and the shared table. fn get_type_kind(ti: int) -> int: if ti >= 0 && ti < g_type_count: - return r64(g_types, ti * 24) + return cell_load(g_types, ti * 24) return -1 fn get_type_data(ti: int) -> int: if ti >= 0 && ti < g_type_count: - return r64(g_types, ti * 24 + 8) + return cell_load(g_types, ti * 24 + 8) return 0 fn get_type_extra(ti: int) -> int: if ti >= 0 && ti < g_type_count: - return r64(g_types, ti * 24 + 16) + return cell_load(g_types, ti * 24 + 16) return 0 // ─── 常量档数组长度约束 ─── @@ -372,7 +326,7 @@ fn array_len_walk(ti_a: int, ti_b: int, dir: int) -> int: loop: if fi >= fc: break - if array_len_walk(r64(g_gen_apply_data, (fs1 + fi) * 8), r64(g_gen_apply_data, (fs2 + fi) * 8), dir) == 0: + if array_len_walk(cell_load(g_gen_apply_data, (fs1 + fi) * 8), cell_load(g_gen_apply_data, (fs2 + fi) * 8), dir) == 0: return 0 fi = fi + 1 return 1 @@ -382,8 +336,8 @@ fn array_len_walk(ti_a: int, ti_b: int, dir: int) -> int: // 基型不同:身份判定负责 as1 := get_type_extra(ti_a) as2 := get_type_extra(ti_b) - ac1 := r64(g_gen_apply_data, as1 * 8) - ac2 := r64(g_gen_apply_data, as2 * 8) + ac1 := cell_load(g_gen_apply_data, as1 * 8) + ac2 := cell_load(g_gen_apply_data, as2 * 8) if ac1 != ac2: return 1 // 实参数不同:身份判定负责 @@ -391,7 +345,7 @@ fn array_len_walk(ti_a: int, ti_b: int, dir: int) -> int: loop: if ai >= ac1: break - if array_len_walk(r64(g_gen_apply_data, (as1 + 1 + ai) * 8), r64(g_gen_apply_data, (as2 + 1 + ai) * 8), dir) == 0: + if array_len_walk(cell_load(g_gen_apply_data, (as1 + 1 + ai) * 8), cell_load(g_gen_apply_data, (as2 + 1 + ai) * 8), dir) == 0: return 0 ai = ai + 1 return 1 @@ -575,7 +529,7 @@ fn scan_for_yield(node: int) -> int: loop: if i >= sc: break - sn := r64(g_block_stmts, (ss + i) * 8) + sn := cell_load(g_block_stmts, (ss + i) * 8) if scan_for_yield(sn) != 0: return 1 i = i + 1 @@ -617,7 +571,7 @@ fn loop_body_has_break(node: int) -> int: loop: if i >= sc: break - if loop_body_has_break(r64(g_block_stmts, (ss + i) * 8)) != 0: + if loop_body_has_break(cell_load(g_block_stmts, (ss + i) * 8)) != 0: return 1 i = i + 1 return 0 @@ -705,7 +659,7 @@ fn stmt_cannot_fall_through(node: int) -> int: loop: if i >= sc: break - if stmt_cannot_fall_through(r64(g_block_stmts, (ss + i) * 8)) != 0: + if stmt_cannot_fall_through(cell_load(g_block_stmts, (ss + i) * 8)) != 0: return 1 i = i + 1 return 0 @@ -746,7 +700,7 @@ fn stmt_diverges(node: int) -> int: loop: if i >= sc: break - if stmt_diverges(r64(g_block_stmts, (ss + i) * 8)) != 0: + if stmt_diverges(cell_load(g_block_stmts, (ss + i) * 8)) != 0: return 1 i = i + 1 return 0 @@ -761,13 +715,13 @@ struct SymEntry: fn push_scope(): grow_scope_bounds(g_scope_depth + 1) - w64(g_scope_bounds, g_scope_depth * 8, g_sym_count) + cell_store(g_scope_bounds, g_scope_depth * 8, g_sym_count) g_scope_depth = g_scope_depth + 1 fn pop_scope(): if g_scope_depth > 0: g_scope_depth = g_scope_depth - 1 - g_sym_count = r64(g_scope_bounds, g_scope_depth * 8) + g_sym_count = cell_load(g_scope_bounds, g_scope_depth * 8) fn def_sym(name_idx: int, kind: int, type_idx: int, node_idx: int): grow_syms(g_sym_count + 1) @@ -840,11 +794,11 @@ g_cur_fn_ret_checked : int, mut fn check_error(code: int, msg: string, line: int, col: int): grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, code) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, msg) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, code) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, msg) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) g_diag_count = g_diag_count + 1 // --- Borrow checking --- @@ -855,7 +809,7 @@ fn find_borrow_entry(var_ni: int) -> int: loop: if i >= g_borrow_count: return -1 - if r64(g_borrow_vars, i * 8) == var_ni: + if cell_load(g_borrow_vars, i * 8) == var_ni: return i i = i + 1 return -1 @@ -865,67 +819,67 @@ fn check_borrow(var_ni: int, is_mut: int) -> bool: if bi >= 0: if is_mut != 0: // &mut x: fail if any borrow exists - if r64(g_borrow_refs, bi * 8) > 0 || r64(g_borrow_muts, bi * 8) != 0: + if cell_load(g_borrow_refs, bi * 8) > 0 || cell_load(g_borrow_muts, bi * 8) != 0: return false - w64(g_borrow_muts, bi * 8, 1) + cell_store(g_borrow_muts, bi * 8, 1) return true else: // &x: fail if mutable borrow exists - if r64(g_borrow_muts, bi * 8) != 0: + if cell_load(g_borrow_muts, bi * 8) != 0: return false - w64(g_borrow_refs, bi * 8, r64(g_borrow_refs, bi * 8) + 1) + cell_store(g_borrow_refs, bi * 8, cell_load(g_borrow_refs, bi * 8) + 1) return true // First borrow of this variable grow_borrow_vars(g_borrow_count + 1) - w64(g_borrow_vars, g_borrow_count * 8, var_ni) - w64(g_borrow_refs, g_borrow_count * 8, 0) - w64(g_borrow_muts, g_borrow_count * 8, 0) + cell_store(g_borrow_vars, g_borrow_count * 8, var_ni) + cell_store(g_borrow_refs, g_borrow_count * 8, 0) + cell_store(g_borrow_muts, g_borrow_count * 8, 0) if is_mut != 0: - w64(g_borrow_muts, g_borrow_count * 8, 1) + cell_store(g_borrow_muts, g_borrow_count * 8, 1) else: - w64(g_borrow_refs, g_borrow_count * 8, 1) + cell_store(g_borrow_refs, g_borrow_count * 8, 1) g_borrow_count = g_borrow_count + 1 return true fn check_use(var_ni: int) -> bool: bi := find_borrow_entry(var_ni) if bi >= 0: - if r64(g_borrow_refs, bi * 8) > 0 || r64(g_borrow_muts, bi * 8) != 0: + if cell_load(g_borrow_refs, bi * 8) > 0 || cell_load(g_borrow_muts, bi * 8) != 0: return false return true fn push_borrow_scope(): grow_borrow_markers(g_borrow_scope_depth + 1) - w64(g_borrow_scope_markers, g_borrow_scope_depth * 8, g_holder_count) + cell_store(g_borrow_scope_markers, g_borrow_scope_depth * 8, g_holder_count) g_borrow_scope_depth = g_borrow_scope_depth + 1 fn pop_borrow_scope(): if g_borrow_scope_depth > 0: g_borrow_scope_depth = g_borrow_scope_depth - 1 - marker := r64(g_borrow_scope_markers, g_borrow_scope_depth * 8) + marker := cell_load(g_borrow_scope_markers, g_borrow_scope_depth * 8) // Release all borrows held from marker to end loop: if g_holder_count <= marker: break g_holder_count = g_holder_count - 1 - borrowed_ni := r64(g_holder_borrowed, g_holder_count * 8) - is_mut := r64(g_holder_is_mut, g_holder_count * 8) + borrowed_ni := cell_load(g_holder_borrowed, g_holder_count * 8) + is_mut := cell_load(g_holder_is_mut, g_holder_count * 8) bi := find_borrow_entry(borrowed_ni) if bi >= 0: if is_mut != 0: - w64(g_borrow_muts, bi * 8, 0) + cell_store(g_borrow_muts, bi * 8, 0) else: - if r64(g_borrow_refs, bi * 8) > 0: - w64(g_borrow_refs, bi * 8, r64(g_borrow_refs, bi * 8) - 1) + if cell_load(g_borrow_refs, bi * 8) > 0: + cell_store(g_borrow_refs, bi * 8, cell_load(g_borrow_refs, bi * 8) - 1) // Clean up entry if no more borrows - if r64(g_borrow_refs, bi * 8) == 0 && r64(g_borrow_muts, bi * 8) == 0: + if cell_load(g_borrow_refs, bi * 8) == 0 && cell_load(g_borrow_muts, bi * 8) == 0: si : ., mut = bi loop: if si + 1 >= g_borrow_count: break - w64(g_borrow_vars, si * 8, r64(g_borrow_vars, (si + 1) * 8)) - w64(g_borrow_refs, si * 8, r64(g_borrow_refs, (si + 1) * 8)) - w64(g_borrow_muts, si * 8, r64(g_borrow_muts, (si + 1) * 8)) + cell_store(g_borrow_vars, si * 8, cell_load(g_borrow_vars, (si + 1) * 8)) + cell_store(g_borrow_refs, si * 8, cell_load(g_borrow_refs, (si + 1) * 8)) + cell_store(g_borrow_muts, si * 8, cell_load(g_borrow_muts, (si + 1) * 8)) si = si + 1 g_borrow_count = g_borrow_count - 1 @@ -937,9 +891,9 @@ fn pop_unsafe_scope(): fn record_borrow_holder(borrower_ni: int, borrowed_ni: int, is_mut: int): grow_holder(g_holder_count + 1) - w64(g_holder_borrowers, g_holder_count * 8, borrower_ni) - w64(g_holder_borrowed, g_holder_count * 8, borrowed_ni) - w64(g_holder_is_mut, g_holder_count * 8, is_mut) + cell_store(g_holder_borrowers, g_holder_count * 8, borrower_ni) + cell_store(g_holder_borrowed, g_holder_count * 8, borrowed_ni) + cell_store(g_holder_is_mut, g_holder_count * 8, is_mut) g_holder_count = g_holder_count + 1 fn borrow_var_name(node: int) -> int: @@ -1026,7 +980,7 @@ fn res_type_node(node: int) -> int: loop: if ai >= arg_count: break - w64(args, ai * 8, res_type_node(an)) + cell_store(args, ai * 8, res_type_node(an)) ai = ai + 1 an = an + 1 // 实例化点的结构/枚举泛型约束检查(`Box[T: I]` 的 `Box[P]`)。 @@ -1035,12 +989,12 @@ fn res_type_node(node: int) -> int: // Store args in g_gen_apply_data: [count, arg1, arg2, ...] data_start := g_gen_apply_data_count grow_gen_apply_data(data_start + 1 + arg_count) - w64(g_gen_apply_data, data_start * 8, arg_count) + cell_store(g_gen_apply_data, data_start * 8, arg_count) ai2 : ., mut = 0 loop: if ai2 >= arg_count: break - w64(g_gen_apply_data, (data_start + 1 + ai2) * 8, r64(args, ai2 * 8)) + cell_store(g_gen_apply_data, (data_start + 1 + ai2) * 8, cell_load(args, ai2 * 8)) ai2 = ai2 + 1 g_gen_apply_data_count = data_start + 1 + arg_count return alloc_type(TYP_GENERIC_APPLY, base_ti, data_start) @@ -1071,7 +1025,7 @@ fn find_iface(name_ni: int) -> int: loop: if i >= g_iface_count: return -1 - if r64(g_ifaces, i * ESZ_IFACEINFO + OFF_IF_NAME) == name_ni: + if cell_load(g_ifaces, i * ESZ_IFACEINFO + OFF_IF_NAME) == name_ni: return i i = i + 1 return -1 @@ -1267,7 +1221,7 @@ fn gen_constr_seen_init(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_constr_seen = nb g_constr_seen_cap = nc @@ -1282,7 +1236,7 @@ fn gen_constr_seen_probe(k: int) -> int: p : ., mut = tt_mod(k, cap) ret : ., mut = -1 loop: - kk := r64(g_constr_seen, p * 16) + kk := cell_load(g_constr_seen, p * 16) if kk < 0: ret = p break @@ -1303,7 +1257,7 @@ fn gen_constr_seen_rehash(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_constr_seen = nb g_constr_seen_cap = nc @@ -1312,11 +1266,11 @@ fn gen_constr_seen_rehash(): loop: if j >= old_cap: break - kk := r64(old, j * 16) + kk := cell_load(old, j * 16) if kk >= 0: s := gen_constr_seen_probe(kk) - w64(g_constr_seen, s * 16, kk) - w64(g_constr_seen, s * 16 + 8, 1) + cell_store(g_constr_seen, s * 16, kk) + cell_store(g_constr_seen, s * 16 + 8, 1) g_constr_seen_count = g_constr_seen_count + 1 j = j + 1 @@ -1327,10 +1281,10 @@ fn gen_constr_seen_add(k: int) -> int: if (g_constr_seen_count + 1) * 2 >= g_constr_seen_cap: gen_constr_seen_rehash() s := gen_constr_seen_probe(k) - if r64(g_constr_seen, s * 16) == k: + if cell_load(g_constr_seen, s * 16) == k: return 0 - w64(g_constr_seen, s * 16, k) - w64(g_constr_seen, s * 16 + 8, 1) + cell_store(g_constr_seen, s * 16, k) + cell_store(g_constr_seen, s * 16 + 8, 1) g_constr_seen_count = g_constr_seen_count + 1 return 1 @@ -1359,7 +1313,7 @@ fn gen_inst_constr_check(app_node: int, sa: int, ea: int, args: string, arg_coun else: c_ni = ei_gen_constr(ea, i) if c_ni >= 0: - arg_ti := r64(args, i * 8) + arg_ti := cell_load(args, i * 8) if arg_ti >= 0: if gen_inst_constr_satisfied(c_ni, arg_ti) == 0: if gen_constr_seen_add(app_node * MAX_GENERICS + i) != 0: @@ -1406,8 +1360,8 @@ fn collect_decls(): gname_ni := si_generic_name(i, gj) g_ti := alloc_type(TYP_GENERIC_PARAM, gname_ni, 0) grow_gen_params(g_gen_param_count + 2) - w64(g_gen_params, g_gen_param_count * 8, gname_ni) - w64(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) + cell_store(g_gen_params, g_gen_param_count * 8, gname_ni) + cell_store(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) g_gen_param_count = g_gen_param_count + 2 def_sym(gname_ni, SYM_TYPE, g_ti, -1) gj = gj + 1 @@ -1417,7 +1371,7 @@ fn collect_decls(): loop: if i >= g_iface_count: break - name_idx := r64(g_ifaces, i * ESZ_IFACEINFO + OFF_IF_NAME) + name_idx := cell_load(g_ifaces, i * ESZ_IFACEINFO + OFF_IF_NAME) type_idx := alloc_named_type(name_idx) def_sym(name_idx, SYM_TYPE, type_idx, -1) i = i + 1 @@ -1461,8 +1415,8 @@ fn collect_decls(): gname_ni := ei_generic_name(i, gj) g_ti := alloc_type(TYP_GENERIC_PARAM, gname_ni, 0) grow_gen_params(g_gen_param_count + 2) - w64(g_gen_params, g_gen_param_count * 8, gname_ni) - w64(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) + cell_store(g_gen_params, g_gen_param_count * 8, gname_ni) + cell_store(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) g_gen_param_count = g_gen_param_count + 2 def_sym(gname_ni, SYM_TYPE, g_ti, -1) gj = gj + 1 @@ -1473,8 +1427,8 @@ fn collect_decls(): loop: if i >= g_type_alias_count: break - name_idx := r64(g_type_aliases, i * 16) - type_node := r64(g_type_aliases, i * 16 + 8) + name_idx := cell_load(g_type_aliases, i * 16) + type_node := cell_load(g_type_aliases, i * 16 + 8) ti := res_type_node(type_node) def_sym(name_idx, SYM_TYPE, ti, -1) i = i + 1 @@ -1500,8 +1454,8 @@ fn collect_decls(): gname_ni := fi_generic_name(i, gj) g_ti := alloc_type(TYP_GENERIC_PARAM, gname_ni, 0) grow_gen_params(g_gen_param_count + 2) - w64(g_gen_params, g_gen_param_count * 8, gname_ni) - w64(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) + cell_store(g_gen_params, g_gen_param_count * 8, gname_ni) + cell_store(g_gen_params, (g_gen_param_count + 1) * 8, g_ti) g_gen_param_count = g_gen_param_count + 2 def_sym(gname_ni, SYM_TYPE, g_ti, -1) gj = gj + 1 @@ -1609,7 +1563,7 @@ fn collect_decls(): loop: if i >= g_global_let_count: break - node := r64(g_global_lets, i * 8) + node := cell_load(g_global_lets, i * 8) name_idx := ast_a(node) // EXPR_LET: a = name idx type_node := ast_b(node) // EXPR_LET: b = type node (-1 if none) ti := TI_UNIT @@ -1622,8 +1576,8 @@ fn collect_decls(): loop: if mi >= g_mod_count: break - alias_ni := r64(g_mods, mi * 24) - fileid_ni := r64(g_mods, mi * 24 + 8) + alias_ni := cell_load(g_mods, mi * 24) + fileid_ni := cell_load(g_mods, mi * 24 + 8) def_sym(alias_ni, SYM_MODULE, fileid_ni, -1) mi = mi + 1 // Register mod path declarations (mod foo::bar;) @@ -1631,13 +1585,13 @@ fn collect_decls(): loop: if pi >= g_mod_path_count: break - mpn := r64(g_mod_path_names, pi * 8) + mpn := cell_load(g_mod_path_names, pi * 8) def_sym(mpn, SYM_MODULE, mpn, -1) pi = pi + 1 // Build module function lookup table for qualified access (e.g., mymath.add) main_fni : ., mut = 0 if g_file_count > 0: - main_fni = r64(g_files, 0) + main_fni = cell_load(g_files, 0) g_mod_func_count = 0 g_mod_func_cap = 0 fi : ., mut = 0 @@ -1647,14 +1601,14 @@ fn collect_decls(): fn_node := fi_ast_node(fi) fn_line := ast_line(fn_node) if fn_line > 0 && fn_line <= g_line_count: - fileid_ni := r64(g_line_fileid, (fn_line - 1) * 8) + fileid_ni := cell_load(g_line_fileid, (fn_line - 1) * 8) if fileid_ni != main_fni && fileid_ni != 0 && g_line_count > 0: grow_mod_funcs(g_mod_func_count + 1) - w64(g_mod_func_fileids, g_mod_func_count * 8, fileid_ni) - w64(g_mod_func_names, g_mod_func_count * 8, fi_name(fi)) + cell_store(g_mod_func_fileids, g_mod_func_count * 8, fileid_ni) + cell_store(g_mod_func_names, g_mod_func_count * 8, fi_name(fi)) fn_si := find_sym(fi_name(fi)) if fn_si >= 0: - w64(g_mod_func_tis, g_mod_func_count * 8, sym_type(fn_si)) + cell_store(g_mod_func_tis, g_mod_func_count * 8, sym_type(fn_si)) g_mod_func_count = g_mod_func_count + 1 fi = fi + 1 @@ -1667,7 +1621,7 @@ fn is_func_generic(fi: int, name_idx: int) -> bool: loop: if gi >= fi_generic_count(fi): return false - if r64(g_funcs, fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) == name_idx: + if cell_load(g_funcs, fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) == name_idx: return true gi = gi + 1 return false @@ -1718,18 +1672,18 @@ fn ti_has_generic_param(ti: int) -> int: loop: if i >= cnt: break - if ti_has_generic_param(r64(g_gen_apply_data, (st + i) * 8)) != 0: + if ti_has_generic_param(cell_load(g_gen_apply_data, (st + i) * 8)) != 0: return 1 i = i + 1 return 0 if k == TYP_GENERIC_APPLY: st := get_type_extra(ti) - cnt := r64(g_gen_apply_data, st * 8) + cnt := cell_load(g_gen_apply_data, st * 8) i : ., mut = 0 loop: if i >= cnt: break - if ti_has_generic_param(r64(g_gen_apply_data, (st + 1 + i) * 8)) != 0: + if ti_has_generic_param(cell_load(g_gen_apply_data, (st + 1 + i) * 8)) != 0: return 1 i = i + 1 return 0 @@ -1781,14 +1735,14 @@ fn type_display(ti: int) -> string: s = type_display(base) s = s + "[" start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) i : ., mut = 0 loop: if i >= cnt: break if i > 0: s = s + ", " - s = s + type_display(r64(g_gen_apply_data, (start + 1 + i) * 8)) + s = s + type_display(cell_load(g_gen_apply_data, (start + 1 + i) * 8)) i = i + 1 s = s + "]" return s @@ -1874,7 +1828,7 @@ fn res_call_type(node: int, func_fi: int) -> int: loop: if ai >= ac: break - w64(args, ai * 8, res_call_type(an, func_fi)) + cell_store(args, ai * 8, res_call_type(an, func_fi)) ai = ai + 1 an = an + 1 // 调用位点的实例化检查(与 res_type_node 同源;本节的存在理由 @@ -1882,12 +1836,12 @@ fn res_call_type(node: int, func_fi: int) -> int: gen_inst_constr_check(node, find_struct(name_idx), find_enum(name_idx), args, ac) ds := g_gen_apply_data_count grow_gen_apply_data(ds + 1 + ac) - w64(g_gen_apply_data, ds * 8, ac) + cell_store(g_gen_apply_data, ds * 8, ac) ai2 : ., mut = 0 loop: if ai2 >= ac: break - w64(g_gen_apply_data, (ds + 1 + ai2) * 8, r64(args, ai2 * 8)) + cell_store(g_gen_apply_data, (ds + 1 + ai2) * 8, cell_load(args, ai2 * 8)) ai2 = ai2 + 1 g_gen_apply_data_count = ds + 1 + ac return alloc_type(TYP_GENERIC_APPLY, base_ti, ds) @@ -1932,13 +1886,13 @@ fn unify_types(pattern: int, concrete: int) -> bool: loop: if mi >= g_gen_map_count: break - if r64(g_gen_map_names, mi * 8) == name_idx: + if cell_load(g_gen_map_names, mi * 8) == name_idx: // 参序归一:源 = concrete(实参),目标 = 已绑定项 - return type_compat_strict(concrete, r64(g_gen_map_types, mi * 8)) == 1 + return type_compat_strict(concrete, cell_load(g_gen_map_types, mi * 8)) == 1 mi = mi + 1 grow_gen_map(g_gen_map_count + 1) - w64(g_gen_map_names, g_gen_map_count * 8, name_idx) - w64(g_gen_map_types, g_gen_map_count * 8, concrete) + cell_store(g_gen_map_names, g_gen_map_count * 8, name_idx) + cell_store(g_gen_map_types, g_gen_map_count * 8, concrete) g_gen_map_count = g_gen_map_count + 1 return true return false @@ -1948,15 +1902,15 @@ fn unify_types(pattern: int, concrete: int) -> bool: return false ps := get_type_extra(pattern) cs := get_type_extra(concrete) - pc := r64(g_gen_apply_data, ps * 8) - cc := r64(g_gen_apply_data, cs * 8) + pc := cell_load(g_gen_apply_data, ps * 8) + cc := cell_load(g_gen_apply_data, cs * 8) if pc != cc: return false ai : ., mut = 0 loop: if ai >= pc: break - if !unify_types(r64(g_gen_apply_data, (ps + 1 + ai) * 8), r64(g_gen_apply_data, (cs + 1 + ai) * 8)): + if !unify_types(cell_load(g_gen_apply_data, (ps + 1 + ai) * 8), cell_load(g_gen_apply_data, (cs + 1 + ai) * 8)): return false ai = ai + 1 return true @@ -1974,14 +1928,14 @@ fn substitute_return_type(ti: int) -> int: loop: if mi >= g_gen_map_count: break - if r64(g_gen_map_names, mi * 8) == name_idx: - return r64(g_gen_map_types, mi * 8) + if cell_load(g_gen_map_names, mi * 8) == name_idx: + return cell_load(g_gen_map_types, mi * 8) mi = mi + 1 return ti if k == TYP_GENERIC_APPLY: base := get_type_data(ti) start := get_type_extra(ti) - count := r64(g_gen_apply_data, start * 8) + count := cell_load(g_gen_apply_data, start * 8) // 两趟(同 res_type_node:递归代换会追加嵌套应用携带值,因此先算进暂存再认领块) subs : string, mut if count > 0: @@ -1990,16 +1944,16 @@ fn substitute_return_type(ti: int) -> int: loop: if ai >= count: break - w64(subs, ai * 8, substitute_return_type(r64(g_gen_apply_data, (start + 1 + ai) * 8))) + cell_store(subs, ai * 8, substitute_return_type(cell_load(g_gen_apply_data, (start + 1 + ai) * 8))) ai = ai + 1 new_start := g_gen_apply_data_count grow_gen_apply_data(new_start + 1 + count) - w64(g_gen_apply_data, new_start * 8, count) + cell_store(g_gen_apply_data, new_start * 8, count) ai2 : ., mut = 0 loop: if ai2 >= count: break - w64(g_gen_apply_data, (new_start + 1 + ai2) * 8, r64(subs, ai2 * 8)) + cell_store(g_gen_apply_data, (new_start + 1 + ai2) * 8, cell_load(subs, ai2 * 8)) ai2 = ai2 + 1 g_gen_apply_data_count = new_start + 1 + count return alloc_type(TYP_GENERIC_APPLY, base, new_start) @@ -2076,7 +2030,7 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in break constr_idx := fi * MAX_GENERICS + gci if constr_idx < g_generic_constr_count: - iface_ni := r64(g_generic_constr, constr_idx * 8) + iface_ni := cell_load(g_generic_constr, constr_idx * 8) if iface_ni >= 0: pname_ni := fi_generic_name(fi, gci) concrete_ti : ., mut = -1 @@ -2084,8 +2038,8 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in loop: if hmi >= g_gen_map_count: break - if r64(g_gen_map_names, hmi * 8) == pname_ni: - concrete_ti = r64(g_gen_map_types, hmi * 8) + if cell_load(g_gen_map_names, hmi * 8) == pname_ni: + concrete_ti = cell_load(g_gen_map_types, hmi * 8) break hmi = hmi + 1 if concrete_ti >= 0: @@ -2116,7 +2070,7 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in if gc_b > 0: bstart := g_gen_binds_count grow_gen_binds(bstart + 1 + gc_b) - w64(g_gen_binds, bstart * 8, gc_b) + cell_store(g_gen_binds, bstart * 8, gc_b) gi_b : ., mut = 0 loop: if gi_b >= gc_b: @@ -2127,11 +2081,11 @@ fn infer_gen_call(fi: int, call_node: int, first_arg: int, arg_count: int) -> in loop: if hmi_b >= g_gen_map_count: break - if r64(g_gen_map_names, hmi_b * 8) == pname_b: - tv_b = r64(g_gen_map_types, hmi_b * 8) + if cell_load(g_gen_map_names, hmi_b * 8) == pname_b: + tv_b = cell_load(g_gen_map_types, hmi_b * 8) break hmi_b = hmi_b + 1 - w64(g_gen_binds, (bstart + 1 + gi_b) * 8, tv_b) + cell_store(g_gen_binds, (bstart + 1 + gi_b) * 8, tv_b) gi_b = gi_b + 1 g_gen_binds_count = bstart + 1 + gc_b ast_set_int_val(call_node, bstart + 1) @@ -2176,7 +2130,7 @@ fn check_func(fi: int): loop: if gi >= fi_generic_count(fi): break - gname_idx := r64(g_funcs, fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) + gname_idx := cell_load(g_funcs, fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) g_ti := alloc_type(TYP_GENERIC_PARAM, gname_idx, 0) def_sym(gname_idx, SYM_TYPE, g_ti, -1) gi = gi + 1 @@ -2218,7 +2172,7 @@ fn check_func(fi: int): loop: if di >= fn_len: break - if load8(fn_name, di) == 46: + if cell_load_byte(fn_name, di) == 46: dot_pos = di break // '.' = 46 @@ -2370,8 +2324,8 @@ fn check_impl_for(): loop: if pi >= g_impl_for_count: break - iface_ni := r64(g_impl_for, pi * 16) - type_ni := r64(g_impl_for, pi * 16 + 8) + iface_ni := cell_load(g_impl_for, pi * 16) + type_ni := cell_load(g_impl_for, pi * 16 + 8) // Find interface by name ii := find_iface(iface_ni) if ii < 0: @@ -2379,14 +2333,14 @@ fn check_impl_for(): check_error(EC_N_UNDEFINED, "Undefined interface '" + iface_name + "'", 0, 0) pi = pi + 1 continue - method_count := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + method_count := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) mi : ., mut = 0 loop: if mi >= method_count: break mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - method_ni := r64(g_ifaces, mbase + OFF_IFM_NAME) - method_pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) + method_ni := cell_load(g_ifaces, mbase + OFF_IFM_NAME) + method_pc := cell_load(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) method_name := istr_get(method_ni) iface_name2 := istr_get(iface_ni) @@ -2454,13 +2408,13 @@ fn grow_dyn_type_sets(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_dyn_type_sets, g_dyn_type_set_cap * 8, nb) + cell_copy(g_dyn_type_sets, g_dyn_type_set_cap * 8, nb) g_dyn_type_sets = nb g_dyn_type_set_cap = nc fn dyn_set_type(var_idx: int, ti: int): grow_dyn_type_sets(var_idx + 1) - old_bits := r64(g_dyn_type_sets, var_idx * 8) + old_bits := cell_load(g_dyn_type_sets, var_idx * 8) bit : ., mut = 1 if ti >= 0 && ti < 64: bit = 1 @@ -2470,14 +2424,14 @@ fn dyn_set_type(var_idx: int, ti: int): break bit = bit * 2 shl = shl - 1 - w64(g_dyn_type_sets, var_idx * 8, old_bits + bit) + cell_store(g_dyn_type_sets, var_idx * 8, old_bits + bit) if var_idx >= g_dyn_type_set_count: g_dyn_type_set_count = var_idx + 1 fn dyn_has_type(var_idx: int, ti: int) -> int: if var_idx < 0 || var_idx >= g_dyn_type_set_count: return 0 - set := r64(g_dyn_type_sets, var_idx * 8) + set := cell_load(g_dyn_type_sets, var_idx * 8) bit : ., mut = 1 if ti >= 0 && ti < 64: bit = 1 @@ -2512,7 +2466,7 @@ fn union_bitmaps(a: int, b: int) -> int: fn validate_dyn_method(si: int, method_ni: int, line: int, col: int): if si < 0 || si >= g_dyn_type_set_count: return - set := r64(g_dyn_type_sets, si * 8) + set := cell_load(g_dyn_type_sets, si * 8) if set == 0: return ti : ., mut = 0 @@ -2592,7 +2546,7 @@ fn s1p_src_frag(line: int) -> string: loop: if p >= g_source_len: break - c := load8(g_source, p) + c := cell_load_byte(g_source, p) if ln == line && start < 0: start = p if start >= 0 && c == 10: @@ -2996,8 +2950,8 @@ fn infer_expr(node: int) -> int: loop: if mfi >= g_mod_func_count: break - if r64(g_mod_func_fileids, mfi * 8) == fileid_ni && r64(g_mod_func_names, mfi * 8) == method_ni: - func_ni = r64(g_mod_func_names, mfi * 8) + if cell_load(g_mod_func_fileids, mfi * 8) == fileid_ni && cell_load(g_mod_func_names, mfi * 8) == method_ni: + func_ni = cell_load(g_mod_func_names, mfi * 8) ast_set_data(node, func_ni) call_flags := ast_type_val(node) if call_flags == CALL_FLAG_INLINE: @@ -3021,7 +2975,7 @@ fn infer_expr(node: int) -> int: fi_mod := find_func(func_ni) s1p_arg_ring(fi_mod, first_arg, arg_count, 0, 0) if mod_found_mfi >= 0: - return r64(g_mod_func_tis, mod_found_mfi * 8) + return cell_load(g_mod_func_tis, mod_found_mfi * 8) return TI_UNIT obj_ti := infer_expr(obj) @@ -3051,8 +3005,8 @@ fn infer_expr(node: int) -> int: loop: if mi >= g_method_count: break - if r64(g_methods, mi * 24) == struct_ni && r64(g_methods, mi * 24 + 8) == method_ni: - func_ni = r64(g_methods, mi * 24 + 16) + if cell_load(g_methods, mi * 24) == struct_ni && cell_load(g_methods, mi * 24 + 8) == method_ni: + func_ni = cell_load(g_methods, mi * 24 + 16) ast_set_data(node, func_ni) break mi = mi + 1 @@ -3064,28 +3018,28 @@ fn infer_expr(node: int) -> int: loop: if gci2 >= gc2: break - gname_idx2 := r64(g_funcs, g_checker_current_fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gci2 * 8) + gname_idx2 := cell_load(g_funcs, g_checker_current_fi * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gci2 * 8) if gname_idx2 == gen_ni: constr_idx2 := g_checker_current_fi * MAX_GENERICS + gci2 if constr_idx2 < g_generic_constr_count: - iface_ni2 := r64(g_generic_constr, constr_idx2 * 8) + iface_ni2 := cell_load(g_generic_constr, constr_idx2 * 8) if iface_ni2 >= 0: ii2 := find_iface(iface_ni2) if ii2 >= 0: - imc2 := r64(g_ifaces, ii2 * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + imc2 := cell_load(g_ifaces, ii2 * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) imi2 : ., mut = 0 loop: if imi2 >= imc2: break imbase2 := ii2 * ESZ_IFACEINFO + OFF_IF_METHODS + imi2 * ESZ_IFMETHOD - if r64(g_ifaces, imbase2 + OFF_IFM_NAME) == method_ni: + if cell_load(g_ifaces, imbase2 + OFF_IFM_NAME) == method_ni: // 调用点记录泛型形参名 ni 和标记位,实例化时由 // monomorph 按具体类型查方法表,解析为真实函数名。 // 不再合成 "T.m" 字符串;名字拼接和克隆期文本替换会让 // 实例体内的调用目标悬空。 ast_set_data(node, gen_ni) ast_set_type_val(node, CALL_FLAG_IFACE_METHOD) - iface_ret2 := r64(g_ifaces, imbase2 + OFF_IFM_RET_TI) + iface_ret2 := cell_load(g_ifaces, imbase2 + OFF_IFM_RET_TI) // 值经单表 `ty_code_to_ti`;本站点值域 = parser 写入的 // `unpack_type(返回型节点)`(parser.cr:1701,即 TY_* 码);域 = {INT..CHAR} // (缺 NEVER/7,因此落尾 TI_UNIT)。显式化:现状 6 行用的是 `iface_ret2 == TY_*` @@ -3230,9 +3184,9 @@ fn infer_expr(node: int) -> int: loop: if bi >= g_rt_builtin_count: break - if r64(g_rt_builtin_names, bi * 8) == func_ni: + if cell_load(g_rt_builtin_names, bi * 8) == func_ni: infer_call_args(first_arg) // 未完成项:实参必须被推断(本 return 路径原先跳过) - return r64(g_rt_builtin_ret_types, bi * 8) + return cell_load(g_rt_builtin_ret_types, bi * 8) bi = bi + 1 // Not found in symbol table or builtins — report error name := istr_get(func_ni) @@ -3251,7 +3205,7 @@ fn infer_expr(node: int) -> int: loop: if i >= stmt_count: break - sn := r64(g_block_stmts, (stmt_start + i) * 8) + sn := cell_load(g_block_stmts, (stmt_start + i) * 8) res = infer_expr(sn) i = i + 1 // Track the last-statement type for debugging @@ -3274,7 +3228,7 @@ fn infer_expr(node: int) -> int: pre_dyn_save : string, mut = "" if pre_dyn_count > 0: pre_dyn_save = alloc(pre_dyn_count * 8) - _dyncpy(g_dyn_type_sets, pre_dyn_count * 8, pre_dyn_save) + cell_copy(g_dyn_type_sets, pre_dyn_count * 8, pre_dyn_save) // --- Process then branch --- push_borrow_scope() then_ti := infer_expr(then_node) @@ -3284,16 +3238,16 @@ fn infer_expr(node: int) -> int: then_dyn_save : string, mut = "" if then_dyn_count > 0: then_dyn_save = alloc(then_dyn_count * 8) - _dyncpy(g_dyn_type_sets, then_dyn_count * 8, then_dyn_save) + cell_copy(g_dyn_type_sets, then_dyn_count * 8, then_dyn_save) // --- Restore pre-if state (zero stale entries beyond pre_dyn_count) --- g_dyn_type_set_count = pre_dyn_count if pre_dyn_count > 0: - _dyncpy(pre_dyn_save, pre_dyn_count * 8, g_dyn_type_sets) + cell_copy(pre_dyn_save, pre_dyn_count * 8, g_dyn_type_sets) iz : ., mut = pre_dyn_count loop: if iz >= then_dyn_count: break - w64(g_dyn_type_sets, iz * 8, 0) + cell_store(g_dyn_type_sets, iz * 8, 0) iz = iz + 1 // --- Process else branch (if any) --- if else_node >= 0: @@ -3312,12 +3266,12 @@ fn infer_expr(node: int) -> int: break then_bits : ., mut = 0 if mi < then_dyn_count: - then_bits = r64(then_dyn_save, mi * 8) + then_bits = cell_load(then_dyn_save, mi * 8) else_bits : ., mut = 0 if mi < g_dyn_type_set_count: - else_bits = r64(g_dyn_type_sets, mi * 8) + else_bits = cell_load(g_dyn_type_sets, mi * 8) merged := union_bitmaps(then_bits, else_bits) - w64(g_dyn_type_sets, mi * 8, merged) + cell_store(g_dyn_type_sets, mi * 8, merged) mi = mi + 1 g_dyn_type_set_count = merge_count // 参序归一:合并结果类型 = then_ti(下方 return then_ti),因此源 = else 侧, @@ -3570,7 +3524,7 @@ fn infer_expr(node: int) -> int: def_sym(var_ni, SYM_LOCAL, ti, -1) if ti == TI_DYN && val_node >= 0: grow_dyn_type_sets(g_sym_count) - w64(g_dyn_type_sets, (g_sym_count - 1) * 8, 0) + cell_store(g_dyn_type_sets, (g_sym_count - 1) * 8, 0) dyn_set_type(g_sym_count - 1, val_ti) return TI_UNIT @@ -3643,7 +3597,7 @@ fn infer_expr(node: int) -> int: if an2 >= 0: ev = infer_expr(ast_a(an2)) an2 = ast_b(an2) - w64(tis, ai * 8, ev) + cell_store(tis, ai * 8, ev) ai = ai + 1 ds := g_gen_apply_data_count grow_gen_apply_data(ds + arg_count) @@ -3651,7 +3605,7 @@ fn infer_expr(node: int) -> int: loop: if ai >= arg_count: break - w64(g_gen_apply_data, (ds + ai) * 8, r64(tis, ai * 8)) + cell_store(g_gen_apply_data, (ds + ai) * 8, cell_load(tis, ai * 8)) ai = ai + 1 g_gen_apply_data_count = ds + arg_count at = alloc_type(TYP_TUPLE, arg_count, ds) @@ -3692,7 +3646,7 @@ fn infer_expr(node: int) -> int: if is_struct_generic(si, ft_name_idx) && get_type_kind(obj_ti) == TYP_GENERIC_APPLY: base_ti := get_type_data(obj_ti) ga_start := get_type_extra(obj_ti) - ga_count := r64(g_gen_apply_data, ga_start * 8) + ga_count := cell_load(g_gen_apply_data, ga_start * 8) // Find which generic param index gpi : ., mut = 0 loop: @@ -3701,7 +3655,7 @@ fn infer_expr(node: int) -> int: if si_generic_name(si, gpi) == ft_name_idx: // Use the corresponding arg from the generic apply if gpi < ga_count: - return r64(g_gen_apply_data, (ga_start + 1 + gpi) * 8) + return cell_load(g_gen_apply_data, (ga_start + 1 + gpi) * 8) break gpi = gpi + 1 return res_type_node(ft_node) @@ -3716,7 +3670,7 @@ fn infer_expr(node: int) -> int: data_start := get_type_extra(actual_ti) if ast_data(node) != idx: ast_set_data(node, idx) - return r64(g_gen_apply_data, (data_start + idx) * 8) + return cell_load(g_gen_apply_data, (data_start + idx) * 8) return TI_UNIT if ast_kind(node) == EXPR_INDEX: @@ -3832,7 +3786,7 @@ fn infer_expr(node: int) -> int: loop: if i >= c: break - w64(didx, i * 8, i) + cell_store(didx, i * 8, i) i = i + 1 if si >= 0 && (named != 0 || c == 0): // ── 名字 -> 声明下标:未知字段 / 重复字段 / 缺字段 ── @@ -3842,7 +3796,7 @@ fn infer_expr(node: int) -> int: loop: if j >= fc: break - w64(used, j * 8, 0) + cell_store(used, j * 8, 0) j = j + 1 if named != 0: i = 0 @@ -3854,16 +3808,16 @@ fn infer_expr(node: int) -> int: if jdi < 0: check_error(EC_TS_UNKNOWN_FIELD, "Unknown field '" + istr_get(nn) + "' in struct literal " + istr_get(name_ni), ast_line(w0 + i), ast_col(w0 + i)) else: - if r64(used, jdi * 8) != 0: + if cell_load(used, jdi * 8) != 0: check_error(EC_TS_FIELD_DUP, "Field '" + istr_get(nn) + "' initialized more than once", ast_line(w0 + i), ast_col(w0 + i)) - w64(used, jdi * 8, 1) - w64(didx, i * 8, jdi) + cell_store(used, jdi * 8, 1) + cell_store(didx, i * 8, jdi) i = i + 1 j = 0 loop: if j >= fc: break - if r64(used, j * 8) == 0: + if cell_load(used, j * 8) == 0: // 只报第一个缺字段(同一程序员错误不刷屏) check_error(EC_TS_MISSING_FIELD, "Missing field '" + istr_get(si_field_name(si, j)) + "' in struct literal " + istr_get(name_ni), ast_line(node), ast_col(node)) break @@ -3876,7 +3830,7 @@ fn infer_expr(node: int) -> int: if i >= c: break if w0 >= 0: - w64(vts, i * 8, infer_expr(w0 + i)) + cell_store(vts, i * 8, infer_expr(w0 + i)) i = i + 1 if si >= 0: gc := si_generic_count(si) @@ -3888,27 +3842,27 @@ fn infer_expr(node: int) -> int: loop: if i >= c: break - jdi := r64(didx, i * 8) + jdi := cell_load(didx, i * 8) if jdi >= 0 && jdi < si_field_count(si): dtn := si_field_type_node(si, jdi) if dtn >= 0 && ast_kind(dtn) == EXPR_IDENT && is_struct_generic(si, ast_int_val(dtn)): pt := alloc_type(TYP_GENERIC_PARAM, ast_int_val(dtn), 0) - if !unify_types(pt, r64(vts, i * 8)): - check_error(EC_TS_FIELD_TYPE, "Field '" + istr_get(si_field_name(si, jdi)) + "': expected " + istr_get(ast_int_val(dtn)) + ", got " + type_display(r64(vts, i * 8)), ast_line(w0 + i), ast_col(w0 + i)) + if !unify_types(pt, cell_load(vts, i * 8)): + check_error(EC_TS_FIELD_TYPE, "Field '" + istr_get(si_field_name(si, jdi)) + "': expected " + istr_get(ast_int_val(dtn)) + ", got " + type_display(cell_load(vts, i * 8)), ast_line(w0 + i), ast_col(w0 + i)) i = i + 1 // ── 非参数声明类型:值类型 vs 声明类型比对(两侧任一含未实例化泛型参数 -> 跳过)── i = 0 loop: if i >= c: break - jdi := r64(didx, i * 8) + jdi := cell_load(didx, i * 8) if jdi >= 0 && jdi < si_field_count(si): dtn := si_field_type_node(si, jdi) is_param : ., mut = 0 if dtn >= 0 && ast_kind(dtn) == EXPR_IDENT && is_struct_generic(si, ast_int_val(dtn)): is_param = 1 if is_param == 0 && type_node_mentions_struct_param(si, dtn) == 0: - vt := r64(vts, i * 8) + vt := cell_load(vts, i * 8) if ti_has_generic_param(vt) == 0: dt := res_type_node(dtn) if ti_has_generic_param(dt) == 0: @@ -3935,15 +3889,15 @@ fn infer_expr(node: int) -> int: loop: if mi >= g_gen_map_count: break - if r64(g_gen_map_names, mi * 8) == pni: - bind = r64(g_gen_map_types, mi * 8) + if cell_load(g_gen_map_names, mi * 8) == pni: + bind = cell_load(g_gen_map_types, mi * 8) break mi = mi + 1 if bind >= 0: - w64(g_gen_apply_data, (ds + 1 + found) * 8, bind) + cell_store(g_gen_apply_data, (ds + 1 + found) * 8, bind) found = found + 1 g = g + 1 - w64(g_gen_apply_data, ds * 8, found) + cell_store(g_gen_apply_data, ds * 8, found) g_gen_apply_data_count = ds + 1 + found return alloc_type(TYP_GENERIC_APPLY, base_ti, ds) return alloc_named_type(name_ni) @@ -4043,7 +3997,7 @@ fn infer_expr(node: int) -> int: wn : ., mut = -1 if elem_idx >= 0: wn = ast_a(elem_idx + e) - w64(tis, e * 8, infer_expr(wn)) + cell_store(tis, e * 8, infer_expr(wn)) e = e + 1 data_start := g_gen_apply_data_count grow_gen_apply_data(data_start + ec) @@ -4051,7 +4005,7 @@ fn infer_expr(node: int) -> int: loop: if e >= ec: break - w64(g_gen_apply_data, (data_start + e) * 8, r64(tis, e * 8)) + cell_store(g_gen_apply_data, (data_start + e) * 8, cell_load(tis, e * 8)) e = e + 1 g_gen_apply_data_count = data_start + ec return alloc_type(TYP_TUPLE, ec, data_start) @@ -4241,7 +4195,7 @@ fn check_all(): // First pass: collect declarations collect_decls() - init_builtins() + prelude_builtin_init() // SYM_SO_FN is materialized by the side-table fallback in find_gsym and // find_so_fn on first reference, which naturally happens after the reset. @@ -4252,8 +4206,8 @@ fn check_all(): loop: if ri2 >= g_rt_builtin_count: break - ni2 := r64(g_rt_builtin_names, ri2 * 8) - ti2 := r64(g_rt_builtin_ret_types, ri2 * 8) + ni2 := cell_load(g_rt_builtin_names, ri2 * 8) + ti2 := cell_load(g_rt_builtin_ret_types, ri2 * 8) if find_gsym(ni2) < 0: // skip if already defined by user si2 := g_sym_count @@ -4278,7 +4232,7 @@ fn check_all(): loop: if i >= g_global_let_count: break - check_global_let(r64(g_global_lets, i * 8)) + check_global_let(cell_load(g_global_lets, i * 8)) i = i + 1 // Check impl-for relationships @@ -4504,7 +4458,7 @@ fn df_func_of_node(pos: int) -> int: loop: if irf >= g_ir_func_count: break - if r64(g_df_func_node_start, irf * 8) <= pos: + if cell_load(g_df_func_node_start, irf * 8) <= pos: best = irf irf = irf + 1 return best @@ -4527,14 +4481,14 @@ fn compute_all_purity(): loop: if z >= fcount: break - w64(local, z * 8, 0) - w64(pure, z * 8, 0) + cell_store(local, z * 8, 0) + cell_store(pure, z * 8, 0) z = z + 1 z = 0 loop: if z >= g_ir_instr_count: break - w64(callee, z * 8, -1) + cell_store(callee, z * 8, -1) z = z + 1 // ── 1) 逐 IR 函数扫体:局部效应位 + 调用边解析 ── @@ -4546,38 +4500,38 @@ fn compute_all_purity(): map_ok : ., mut = 1 if sfi < 0: map_ok = 0 - else if r64(g_ir_func_name_idx, irf * 8) != fi_name(sfi): + else if cell_load(g_ir_func_name_idx, irf * 8) != fi_name(sfi): map_ok = 0 else if ast_kind(fi_ast_node(sfi)) == EXPR_EXTERN: map_ok = 0 if map_ok == 0: // 映射漂移/extern 声明体(无可生成的体):保守判不纯 if sfi >= 0: - w64(local, sfi * 8, 1) + cell_store(local, sfi * 8, 1) irf = irf + 1 continue - istart := r64(g_ir_func_instr_start, irf * 8) - icnt := r64(g_ir_func_instr_count, irf * 8) + istart := cell_load(g_ir_func_instr_start, irf * 8) + icnt := cell_load(g_ir_func_instr_count, irf * 8) ii := istart loop: if ii >= istart + icnt: break op := iri_op(ii) if purity_op_effect(op) != 0: - w64(local, sfi * 8, 1) + cell_store(local, sfi * 8, 1) if op == IR_CALL: cf := find_func(iri_s3(ii)) if cf < 0: - w64(local, sfi * 8, 1) + cell_store(local, sfi * 8, 1) // 不可解析(runtime builtin),因此不纯 else: - w64(callee, ii * 8, cf) + cell_store(callee, ii * 8, cf) if op == IR_INLINE: cf2 := find_func(iri_s1(ii)) if cf2 < 0: - w64(local, sfi * 8, 1) + cell_store(local, sfi * 8, 1) else: - w64(callee, ii * 8, cf2) + cell_store(callee, ii * 8, cf2) ii = ii + 1 irf = irf + 1 @@ -4587,12 +4541,12 @@ fn compute_all_purity(): loop: if si >= g_sg_count: break - if r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_UNSAFE: - uf := df_func_of_node(r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART)) + if cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) == SG_UNSAFE: + uf := df_func_of_node(cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART)) if uf >= 0: usf := src_func_of_ir(uf) if usf >= 0: - w64(local, usf * 8, 1) + cell_store(local, usf * 8, 1) si = si + 1 // ── 3) 不动点:全不纯起点升纯(保守单调,语义见头注 4)── @@ -4607,23 +4561,23 @@ fn compute_all_purity(): break sf := src_func_of_ir(irf) if sf >= 0: - if r64(local, sf * 8) == 0: - if r64(pure, sf * 8) == 0: - ist2 := r64(g_ir_func_instr_start, irf * 8) - ic2 := r64(g_ir_func_instr_count, irf * 8) + if cell_load(local, sf * 8) == 0: + if cell_load(pure, sf * 8) == 0: + ist2 := cell_load(g_ir_func_instr_start, irf * 8) + ic2 := cell_load(g_ir_func_instr_count, irf * 8) allpure : ., mut = 1 ii2 := ist2 loop: if ii2 >= ist2 + ic2: break - c := r64(callee, ii2 * 8) + c := cell_load(callee, ii2 * 8) if c >= 0: - if r64(pure, c * 8) == 0: + if cell_load(pure, c * 8) == 0: allpure = 0 break ii2 = ii2 + 1 if allpure != 0: - w64(pure, sf * 8, 1) + cell_store(pure, sf * 8, 1) changed = 1 irf = irf + 1 @@ -4639,17 +4593,17 @@ fn compute_all_purity(): loop: if gi >= g_purity_inst_count: break - if r64(g_purity_inst, gi * 24) == fi: + if cell_load(g_purity_inst, gi * 24) == fi: ninst = ninst + 1 - nf := r64(g_purity_inst, gi * 24 + 16) + nf := cell_load(g_purity_inst, gi * 24 + 16) if nf >= 0 && nf < fcount: - if r64(pure, nf * 8) == 0: + if cell_load(pure, nf * 8) == 0: allp = 0 else: allp = 0 gi = gi + 1 if ninst > 0 && allp != 0: - w64(pure, fi * 8, 1) + cell_store(pure, fi * 8, 1) fi = fi + 1 // ── 5) 写回 ── @@ -4657,7 +4611,7 @@ fn compute_all_purity(): loop: if fi >= fcount: break - fi_set_ispure(fi, r64(pure, fi * 8)) + fi_set_ispure(fi, cell_load(pure, fi * 8)) fi = fi + 1 // 自测辅助:按名查纯度(-1 = 无此函数,与 0/1 区分,防查不到被当不纯)。 diff --git a/src/compiler/cir_cache.cr b/src/compiler/cir_cache.cr index 8011ef71..8295a77d 100644 --- a/src/compiler/cir_cache.cr +++ b/src/compiler/cir_cache.cr @@ -124,12 +124,12 @@ fn cir_hash_running_binary() -> int: loop: if i + 8 > n: break - h = h * 1099511628211 + load64(buf, i) + h = h * 1099511628211 + cell_load64(buf, i) i = i + 8 loop: if i >= n: break - h = h * 1099511628211 + load8(buf, i) + h = h * 1099511628211 + cell_load_byte(buf, i) i = i + 1 total = total + n r1 := syscall3(3, fd, 0, 0) // close(fd) @@ -146,8 +146,8 @@ fn cir_cache_sg_count(node_start: int, node_count: int) -> int: loop: if si >= g_sg_count: break - kind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) - nstart := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + kind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) + nstart := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) if kind != SG_FUNC && nstart >= node_start && nstart < node_start + node_count: count = count + 1 si = si + 1 @@ -156,11 +156,11 @@ fn cir_cache_sg_count(node_start: int, node_count: int) -> int: // Encode a region parent as an index within the serialized nested-region // list. -1 denotes the function SG recreated by df_begin_func. fn cir_cache_sg_parent(sg_idx: int, node_start: int, node_count: int) -> int: - parent := r64(g_sgs, sg_idx * ESZ_SG + OFF_SG_PARENT) + parent := cell_load(g_sgs, sg_idx * ESZ_SG + OFF_SG_PARENT) if parent < 0: return -1 - parent_kind := r64(g_sgs, parent * ESZ_SG + OFF_SG_KIND) - parent_start := r64(g_sgs, parent * ESZ_SG + OFF_SG_NSTART) + parent_kind := cell_load(g_sgs, parent * ESZ_SG + OFF_SG_KIND) + parent_start := cell_load(g_sgs, parent * ESZ_SG + OFF_SG_NSTART) if parent_kind == SG_FUNC && parent_start == node_start: return -1 @@ -169,8 +169,8 @@ fn cir_cache_sg_parent(sg_idx: int, node_start: int, node_count: int) -> int: loop: if si >= sg_idx: break - kind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) - nstart := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + kind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) + nstart := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) if kind != SG_FUNC && nstart >= node_start && nstart < node_start + node_count: if si == parent: return local @@ -199,16 +199,16 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: fn_node := fi_ast_node(source_fi) fp := func_fingerprint(fn_node) sig := sig_fingerprint(fn_node) - name_ni := r64(g_ir_func_name_idx, ir_fi * 8) + name_ni := cell_load(g_ir_func_name_idx, ir_fi * 8) name := istr_get(name_ni) name_len := str_len(name) - var_start := r64(g_ir_func_var_start, ir_fi * 8) - var_count := r64(g_ir_func_var_count, ir_fi * 8) - node_start := r64(g_df_func_node_start, ir_fi * 8) - node_count := r64(g_df_func_node_count, ir_fi * 8) - instr_start := r64(g_ir_func_instr_start, ir_fi * 8) - instr_count := r64(g_ir_func_instr_count, ir_fi * 8) + var_start := cell_load(g_ir_func_var_start, ir_fi * 8) + var_count := cell_load(g_ir_func_var_count, ir_fi * 8) + node_start := cell_load(g_df_func_node_start, ir_fi * 8) + node_count := cell_load(g_df_func_node_count, ir_fi * 8) + instr_start := cell_load(g_ir_func_instr_start, ir_fi * 8) + instr_count := cell_load(g_ir_func_instr_count, ir_fi * 8) // Serialize in memory and issue one write. The previous per-field writes // made a full self-host build perform millions of syscalls on its cache. @@ -222,7 +222,7 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: // Each record is 24 bytes: {rel_from, rel_to, kind}, with signed offsets // relative to node_start. The next pointers are not stored; the loader // rebuilds them through df_add_edge_kind in the cold-path insertion order. - edge_start := r64(g_df_func_edge_start, ir_fi * 8) + edge_start := cell_load(g_df_func_edge_start, ir_fi * 8) own_edge_count : ., mut = g_df_edge_count - edge_start if own_edge_count < 0: own_edge_count = 0 @@ -233,7 +233,7 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: loop: if size_si >= g_ir_str_const_count: break - size_ni := r64(g_ir_str_consts, size_si * 8) + size_ni := cell_load(g_ir_str_consts, size_si * 8) total_size = total_size + 8 + str_len(istr_get(size_ni)) size_si = size_si + 1 // v13: nested SG records (kind/enter/exit/parent/nstart/ncount). @@ -245,7 +245,7 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: loop: if size_ri >= g_cir_rec_count: break - total_size = total_size + 8 + str_len(istr_get(r64(g_cir_rec_ids, size_ri * 8))) + total_size = total_size + 8 + str_len(istr_get(cell_load(g_cir_rec_ids, size_ri * 8))) size_ri = size_ri + 1 // Trailer: {var_start, node_start}. Both are validated before restore. total_size = total_size + 16 @@ -260,45 +260,45 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: g_cir_write_pos = 0 // Write header - w64_cir(fd, CIR_CACHE_MAGIC) - w64_cir(fd, CIR_CACHE_VER) - w64_cir(fd, cid) // v17: 编译器身份(重建即失效) - w64_cir(fd, fp) - w64_cir(fd, sig) - w64_cir(fd, name_len) + cir_store_cell(fd, CIR_CACHE_MAGIC) + cir_store_cell(fd, CIR_CACHE_VER) + cir_store_cell(fd, cid) // v17: 编译器身份(重建即失效) + cir_store_cell(fd, fp) + cir_store_cell(fd, sig) + cir_store_cell(fd, name_len) write_fd(fd, name, name_len) // Determine var range for this function - w64_cir(fd, var_count) + cir_store_cell(fd, var_count) vi : ., mut = 0 loop: if vi >= var_count: break v_idx := var_start + vi - w64_cir(fd, irv_name(v_idx)) - w64_cir(fd, irv_id(v_idx)) - w64_cir(fd, irv_type(v_idx)) + cir_store_cell(fd, irv_name(v_idx)) + cir_store_cell(fd, irv_id(v_idx)) + cir_store_cell(fd, irv_type(v_idx)) vi = vi + 1 // Write function's node range - w64_cir(fd, node_count) + cir_store_cell(fd, node_count) ni2 : ., mut = 0 loop: if ni2 >= node_count: break n := node_start + ni2 - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE)) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST)) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1)) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2)) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3)) // Store the derived code only. The term-reference slot is a process-local // type-term index and cannot cross processes or resets. The auxiliary code // is derived from the same pair and is not stored either. - w64_cir(fd, sh_dfn_code_of_slots(r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX))) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE)) - w64_cir(fd, r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT)) + cir_store_cell(fd, sh_dfn_code_of_slots(cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK), + cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX))) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE)) + cir_store_cell(fd, cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT)) // The term and auxiliary slots are pure functions of the opcode and derived // code. The loader rederives them at the same point in the node sequence as // the cold path, keeping term indexes aligned. @@ -306,65 +306,65 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: // Persist nested SG metadata with offsets relative to this function's // node range. Parent indices are local to this serialized section. - w64_cir(fd, sg_count) + cir_store_cell(fd, sg_count) sg_i : ., mut = 0 loop: if sg_i >= g_sg_count: break - kind := r64(g_sgs, sg_i * ESZ_SG + OFF_SG_KIND) - nstart := r64(g_sgs, sg_i * ESZ_SG + OFF_SG_NSTART) + kind := cell_load(g_sgs, sg_i * ESZ_SG + OFF_SG_KIND) + nstart := cell_load(g_sgs, sg_i * ESZ_SG + OFF_SG_NSTART) if kind != SG_FUNC && nstart >= node_start && nstart < node_start + node_count: - w64_cir(fd, kind) - w64_cir(fd, r64(g_sgs, sg_i * ESZ_SG + OFF_SG_ENTER) - node_start) - w64_cir(fd, r64(g_sgs, sg_i * ESZ_SG + OFF_SG_EXIT) - node_start) - w64_cir(fd, cir_cache_sg_parent(sg_i, node_start, node_count)) - w64_cir(fd, nstart - node_start) - w64_cir(fd, r64(g_sgs, sg_i * ESZ_SG + OFF_SG_NCOUNT)) + cir_store_cell(fd, kind) + cir_store_cell(fd, cell_load(g_sgs, sg_i * ESZ_SG + OFF_SG_ENTER) - node_start) + cir_store_cell(fd, cell_load(g_sgs, sg_i * ESZ_SG + OFF_SG_EXIT) - node_start) + cir_store_cell(fd, cir_cache_sg_parent(sg_i, node_start, node_count)) + cir_store_cell(fd, nstart - node_start) + cir_store_cell(fd, cell_load(g_sgs, sg_i * ESZ_SG + OFF_SG_NCOUNT)) sg_i = sg_i + 1 // Write only this function's edges. Endpoints are signed offsets relative to // node_start; the next pointers are rebuilt during load. - w64_cir(fd, own_edge_count) + cir_store_cell(fd, own_edge_count) ei : ., mut = 0 loop: if ei >= own_edge_count: break e := edge_start + ei - w64_cir(fd, r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_FROM) - node_start) - w64_cir(fd, r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) - node_start) - w64_cir(fd, r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND)) + cir_store_cell(fd, cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_FROM) - node_start) + cir_store_cell(fd, cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) - node_start) + cir_store_cell(fd, cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND)) ei = ei + 1 // Write function's instruction range - w64_cir(fd, instr_count) + cir_store_cell(fd, instr_count) ii : ., mut = 0 loop: if ii >= instr_count: break inst := instr_start + ii - w64_cir(fd, iri_op(inst)) - w64_cir(fd, iri_dest(inst)) - w64_cir(fd, iri_s1(inst)) - w64_cir(fd, iri_s2(inst)) - w64_cir(fd, iri_s3(inst)) - w64_cir(fd, iri_tk(inst)) + cir_store_cell(fd, iri_op(inst)) + cir_store_cell(fd, iri_dest(inst)) + cir_store_cell(fd, iri_s1(inst)) + cir_store_cell(fd, iri_s2(inst)) + cir_store_cell(fd, iri_s3(inst)) + cir_store_cell(fd, iri_tk(inst)) ii = ii + 1 // Write string constants used by this function - w64_cir(fd, g_ir_str_const_count) + cir_store_cell(fd, g_ir_str_const_count) si : ., mut = 0 loop: if si >= g_ir_str_const_count: break - ni3 := r64(g_ir_str_consts, si * 8) + ni3 := cell_load(g_ir_str_consts, si * 8) s := istr_get(ni3) sl := str_len(s) - w64_cir(fd, sl) + cir_store_cell(fd, sl) vi = 0 loop: if vi >= sl: break - w8_cir(fd, load8(s, vi)) + cir_store_byte(fd, cell_load_byte(s, vi)) vi = vi + 1 si = si + 1 @@ -373,28 +373,28 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: // The first field is the writer's g_str_count at the start of the generation // window. The reader requires the same count before replay; a mismatch means // the two sides diverged earlier, so the snapshot is rejected. - w64_cir(fd, g_cir_rec_base) - w64_cir(fd, g_cir_rec_count) + cir_store_cell(fd, g_cir_rec_base) + cir_store_cell(fd, g_cir_rec_count) ri : ., mut = 0 loop: if ri >= g_cir_rec_count: break - rs := istr_get(r64(g_cir_rec_ids, ri * 8)) + rs := istr_get(cell_load(g_cir_rec_ids, ri * 8)) rl := str_len(rs) - w64_cir(fd, rl) + cir_store_cell(fd, rl) rj : ., mut = 0 loop: if rj >= rl: break - w8_cir(fd, load8(rs, rj)) + cir_store_byte(fd, cell_load_byte(rs, rj)) rj = rj + 1 ri = ri + 1 // Trailer: {var_start, node_start}. The var baseline is always validated. The // node baseline is required only when the snapshot contains an out-of-range // relative endpoint. - w64_cir(fd, var_start) - w64_cir(fd, node_start) + cir_store_cell(fd, var_start) + cir_store_cell(fd, node_start) written := syscall3(1, fd, g_cir_write_buf, g_cir_write_pos) syscall3(3, fd, 0, 0) @@ -403,12 +403,12 @@ fn save_cir_cache(path: string, source_fi: int, ir_fi: int) -> int: return 0 // Write helpers for the in-memory cache serializer. -fn w64_cir(fd: int, val: int): - w64(g_cir_write_buf, g_cir_write_pos, val) +fn cir_store_cell(fd: int, val: int): + cell_store(g_cir_write_buf, g_cir_write_pos, val) g_cir_write_pos = g_cir_write_pos + 8 -fn w8_cir(fd: int, val: int): - store8(g_cir_write_buf, g_cir_write_pos, val) +fn cir_store_byte(fd: int, val: int): + cell_store_byte(g_cir_write_buf, g_cir_write_pos, val) g_cir_write_pos = g_cir_write_pos + 1 fn write_fd(fd: int, data: string, len: int): @@ -416,7 +416,7 @@ fn write_fd(fd: int, data: string, len: int): loop: if write_pos >= len: break - store8(g_cir_write_buf, g_cir_write_pos, load8(data, write_pos)) + cell_store_byte(g_cir_write_buf, g_cir_write_pos, cell_load_byte(data, write_pos)) g_cir_write_pos = g_cir_write_pos + 1 write_pos = write_pos + 1 @@ -468,7 +468,7 @@ fn cir_read_snapshot(path: string) -> int: // NULL-8. The read EFAULT result is the safer guard. if nread != fsize: return -1 - w64(g_cir_read_buf, -8, fsize + 1) // 长度头 = fsize(str_len 口径同 read_file) + cell_store(g_cir_read_buf, -8, fsize + 1) // 长度头 = fsize(str_len 口径同 read_file) return fsize // Load a .cir cache file and restore DFG state. @@ -486,11 +486,11 @@ fn load_cir_cache(path: string, func_idx: int) -> int: pos : ., mut = 0 // Validate header - magic := r64(data, pos) + magic := cell_load(data, pos) pos = pos + 8 if magic != CIR_CACHE_MAGIC: return -1 - ver := r64(data, pos) + ver := cell_load(data, pos) pos = pos + 8 if ver != CIR_CACHE_VER: return -1 @@ -500,14 +500,14 @@ fn load_cir_cache(path: string, func_idx: int) -> int: // target source. Reject it here. Identity 0 is also rejected; save does not // write such entries. cid := cir_compiler_identity() - entry_cid := r64(data, pos) + entry_cid := cell_load(data, pos) pos = pos + 8 if cid == 0 || entry_cid != cid: return -1 - fp := r64(data, pos) + fp := cell_load(data, pos) pos = pos + 8 - sig := r64(data, pos) + sig := cell_load(data, pos) pos = pos + 8 // Verify fingerprints against current AST. @@ -521,7 +521,7 @@ fn load_cir_cache(path: string, func_idx: int) -> int: return -1 // Verify function identity (name) - name_len := r64(data, pos) + name_len := cell_load(data, pos) pos = pos + 8 // Skip name string (we already know which function we tried to load) pos = pos + name_len @@ -539,53 +539,53 @@ fn load_cir_cache(path: string, func_idx: int) -> int: // crash or a partial restore. p : ., mut = pos var_p := p - p_var_count := r64(data, p) + p_var_count := cell_load(data, p) p = p + 8 + p_var_count * 24 if p > dlen: return -1 node_p := p - p_node_count := r64(data, p) + p_node_count := cell_load(data, p) p = p + 8 + p_node_count * 64 if p > dlen: return -1 sg_p := p - p_sg_count := r64(data, p) + p_sg_count := cell_load(data, p) p = p + 8 + p_sg_count * 48 if p > dlen: return -1 edge_p := p - p_edge_count := r64(data, p) + p_edge_count := cell_load(data, p) p = p + 8 + p_edge_count * 24 if p > dlen: return -1 instr_p := p - p_instr_count := r64(data, p) + p_instr_count := cell_load(data, p) p = p + 8 + p_instr_count * 48 if p > dlen: return -1 str_p := p - p_str_count := r64(data, p) + p_str_count := cell_load(data, p) p = p + 8 p_si : ., mut = 0 loop: if p_si >= p_str_count: break - sl_p := r64(data, p) + sl_p := cell_load(data, p) p = p + 8 + sl_p if p > dlen: return -1 p_si = p_si + 1 // Intern journal section during the read-only scan: {base count, record count, records}. j_p := p - p_j_base := r64(data, p) + p_j_base := cell_load(data, p) p = p + 8 - p_j_count := r64(data, p) + p_j_count := cell_load(data, p) p = p + 8 p_ji : ., mut = 0 loop: if p_ji >= p_j_count: break - jl_p := r64(data, p) + jl_p := cell_load(data, p) p = p + 8 + jl_p if p > dlen: return -1 @@ -596,8 +596,8 @@ fn load_cir_cache(path: string, func_idx: int) -> int: // returns a miss. if p + 16 > dlen: return -1 - var_start_w := r64(data, p) - node_start_w := r64(data, p + 8) + var_start_w := cell_load(data, p) + node_start_w := cell_load(data, p + 8) // The var range is the only mirror domain that cannot be remapped cheaply: // var indexes appear in node dest/s1..s3 and instruction s1..s3. Always // validate it. A mismatch means the reader and writer are in different states, @@ -617,8 +617,8 @@ fn load_cir_cache(path: string, func_idx: int) -> int: if ei_v >= p_edge_count: break eo_v := edge_p + 8 + ei_v * 24 - rf_v := r64(data, eo_v) - rt_v := r64(data, eo_v + 8) + rf_v := cell_load(data, eo_v) + rt_v := cell_load(data, eo_v + 8) if rf_v >= p_node_count: return -1 // An endpoint at or past node_count is invalid. @@ -646,22 +646,22 @@ fn load_cir_cache(path: string, func_idx: int) -> int: loop: if tz >= p_node_count: break - w64(tally, tz * 8, 0) + cell_store(tally, tz * 8, 0) tz = tz + 1 ei_t : ., mut = 0 loop: if ei_t >= p_edge_count: break - rf_t := r64(data, edge_p + 8 + ei_t * 24) + rf_t := cell_load(data, edge_p + 8 + ei_t * 24) if rf_t >= 0: - w64(tally, rf_t * 8, r64(tally, rf_t * 8) + 1) + cell_store(tally, rf_t * 8, cell_load(tally, rf_t * 8) + 1) ei_t = ei_t + 1 nj : ., mut = 0 loop: if nj >= p_node_count: break - ec_disk := r64(data, node_p + 8 + nj * 64 + 56) - if ec_disk != r64(tally, nj * 8): + ec_disk := cell_load(data, node_p + 8 + nj * 64 + 56) + if ec_disk != cell_load(tally, nj * 8): return -1 nj = nj + 1 @@ -681,7 +681,7 @@ fn load_cir_cache(path: string, func_idx: int) -> int: loop: if ji >= p_j_count: break - jl2 := r64(data, jp) + jl2 := cell_load(data, jp) jp = jp + 8 js := str_sub(data, jp, jl2) jp = jp + jl2 @@ -689,17 +689,17 @@ fn load_cir_cache(path: string, func_idx: int) -> int: ji = ji + 1 // Restore vars - var_count := r64(data, pos) + var_count := cell_load(data, pos) pos = pos + 8 vi : ., mut = 0 loop: if vi >= var_count: break - name_ni := r64(data, pos) + name_ni := cell_load(data, pos) pos = pos + 8 - v_id := r64(data, pos) + v_id := cell_load(data, pos) pos = pos + 8 - v_type := r64(data, pos) + v_type := cell_load(data, pos) pos = pos + 8 // Ensure var array is large enough if v_id >= g_ir_var_count: @@ -711,7 +711,7 @@ fn load_cir_cache(path: string, func_idx: int) -> int: vi = vi + 1 // Restore nodes - node_count := r64(data, pos) + node_count := cell_load(data, pos) pos = pos + 8 base_node := g_df_node_count grow_df_nodes(g_df_node_count + node_count) @@ -726,16 +726,16 @@ fn load_cir_cache(path: string, func_idx: int) -> int: if ni >= node_count: break n := base_node + ni - w64(g_df_node_region, n * 8, g_cur_sg) - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE, r64(data, pos)) + cell_store(g_df_node_region, n * 8, g_cur_sg) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE, cell_load(data, pos)) pos = pos + 8 - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST, r64(data, pos)) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST, cell_load(data, pos)) pos = pos + 8 - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1, r64(data, pos)) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S1, cell_load(data, pos)) pos = pos + 8 - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2, r64(data, pos)) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S2, cell_load(data, pos)) pos = pos + 8 - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3, r64(data, pos)) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3, cell_load(data, pos)) pos = pos + 8 // The snapshot stores one tk field, the derived code. Both slots are // rederived from it at the same point in the node sequence as the cold @@ -743,35 +743,35 @@ fn load_cir_cache(path: string, func_idx: int) -> int: // grows in the same order and cold and warm runs get the same term // indexes. Term references are not read from disk because they are // process-local indexes. - tk_disk := r64(data, pos) + tk_disk := cell_load(data, pos) pos = pos + 8 // The load variant does not set the build-failure bit. A stored code may // fall outside the warm process's smaller type table; a cache hit must not // turn that into a compile failure. See sh_tk_split_load in ty_shadow.cr. - sh_tk_split_load(r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE), tk_disk) - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK, g_sh_slot_term) - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX, g_sh_slot_aux) + sh_tk_split_load(cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE), tk_disk) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK, g_sh_slot_term) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX, g_sh_slot_aux) // The stored first_edge and edge_count are not trusted because edge indexes // shift with the new node baseline. Zero both and let df_add_edge_kind // rebuild them. The stored values were already checked by the per-node // witness during the pre-scan. - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE, -1) - w64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT, 0) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_FIRST_EDGE, -1) + cell_store(g_df_nodes, n * ESZ_DFNODE + OFF_DF_EDGE_COUNT, 0) pos = pos + 16 // 跳过盘上两字段 // Record the producer for variables defined by this node. grow_df_arrays // fills new slots with -1, so a variable with no producer in the snapshot // stays -1. Only overwrite variables that the snapshot actually defines. // The old zero-filled slots read as node 0 and kept ghost edges. - dest := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST) + dest := cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_DEST) if dest >= 0: grow_df_arrays(dest + 1) - w64(g_df_var_producer, dest * 8, n) + cell_store(g_df_var_producer, dest * 8, n) ni = ni + 1 g_df_node_count = base_node + node_count // Restore nested SG metadata (v13). The current function SG was opened by // df_begin_func; cached parent=-1 refers to that SG. - sg_count := r64(data, pos) + sg_count := cell_load(data, pos) pos = pos + 8 sg_base := g_sg_count sg_i : ., mut = 0 @@ -780,28 +780,28 @@ fn load_cir_cache(path: string, func_idx: int) -> int: break new_sg := g_sg_count grow_sg(new_sg + 1) - kind := r64(data, pos) + kind := cell_load(data, pos) pos = pos + 8 - enter_rel := r64(data, pos) + enter_rel := cell_load(data, pos) pos = pos + 8 - exit_rel := r64(data, pos) + exit_rel := cell_load(data, pos) pos = pos + 8 - parent_rel := r64(data, pos) + parent_rel := cell_load(data, pos) pos = pos + 8 - nstart_rel := r64(data, pos) + nstart_rel := cell_load(data, pos) pos = pos + 8 - ncount := r64(data, pos) + ncount := cell_load(data, pos) pos = pos + 8 parent := g_cur_sg if parent_rel >= 0: parent = sg_base + parent_rel f := new_sg * ESZ_SG - w64(g_sgs, f + OFF_SG_KIND, kind) - w64(g_sgs, f + OFF_SG_ENTER, base_node + enter_rel) - w64(g_sgs, f + OFF_SG_EXIT, base_node + exit_rel) - w64(g_sgs, f + OFF_SG_PARENT, parent) - w64(g_sgs, f + OFF_SG_NSTART, base_node + nstart_rel) - w64(g_sgs, f + OFF_SG_NCOUNT, ncount) + cell_store(g_sgs, f + OFF_SG_KIND, kind) + cell_store(g_sgs, f + OFF_SG_ENTER, base_node + enter_rel) + cell_store(g_sgs, f + OFF_SG_EXIT, base_node + exit_rel) + cell_store(g_sgs, f + OFF_SG_PARENT, parent) + cell_store(g_sgs, f + OFF_SG_NSTART, base_node + nstart_rel) + cell_store(g_sgs, f + OFF_SG_NCOUNT, ncount) g_sg_count = new_sg + 1 // Later (inner) records overwrite the enclosing region mapping. @@ -810,7 +810,7 @@ fn load_cir_cache(path: string, func_idx: int) -> int: loop: if rn >= rend: break - w64(g_df_node_region, rn * 8, new_sg) + cell_store(g_df_node_region, rn * 8, new_sg) rn = rn + 1 sg_i = sg_i + 1 @@ -819,17 +819,17 @@ fn load_cir_cache(path: string, func_idx: int) -> int: // and insertion order as the cold path, so each node's list and edge_count match // the writer. Relative endpoints remain correct when a partial rebuild shifts // the node baseline. - own_edge_count := r64(data, pos) + own_edge_count := cell_load(data, pos) pos = pos + 8 ei : ., mut = 0 loop: if ei >= own_edge_count: break - rel_from := r64(data, pos) + rel_from := cell_load(data, pos) pos = pos + 8 - rel_to := r64(data, pos) + rel_to := cell_load(data, pos) pos = pos + 8 - e_kind := r64(data, pos) + e_kind := cell_load(data, pos) pos = pos + 8 // Signed mapping: a negative endpoint points to an earlier function or a // global node. Bounds and the baseline witness were checked in the pre-scan. @@ -837,7 +837,7 @@ fn load_cir_cache(path: string, func_idx: int) -> int: ei = ei + 1 // Restore instructions - instr_count := r64(data, pos) + instr_count := cell_load(data, pos) pos = pos + 8 base_instr := g_ir_instr_count grow_ir_instrs(g_ir_instr_count + instr_count) @@ -846,29 +846,29 @@ fn load_cir_cache(path: string, func_idx: int) -> int: if ii >= instr_count: break inst := base_instr + ii - iri_set_op(inst, r64(data, pos)) + iri_set_op(inst, cell_load(data, pos)) pos = pos + 8 - iri_set_dest(inst, r64(data, pos)) + iri_set_dest(inst, cell_load(data, pos)) pos = pos + 8 - iri_set_s1(inst, r64(data, pos)) + iri_set_s1(inst, cell_load(data, pos)) pos = pos + 8 - iri_set_s2(inst, r64(data, pos)) + iri_set_s2(inst, cell_load(data, pos)) pos = pos + 8 - iri_set_s3(inst, r64(data, pos)) + iri_set_s3(inst, cell_load(data, pos)) pos = pos + 8 - iri_set_tk(inst, r64(data, pos)) + iri_set_tk(inst, cell_load(data, pos)) pos = pos + 8 ii = ii + 1 g_ir_instr_count = base_instr + instr_count // Restore string constants (used by nodes/instrs) - str_count := r64(data, pos) + str_count := cell_load(data, pos) pos = pos + 8 si : ., mut = 0 loop: if si >= str_count: break - sl := r64(data, pos) + sl := cell_load(data, pos) pos = pos + 8 s := str_sub(data, pos, sl) pos = pos + sl diff --git a/src/compiler/dataflow.cr b/src/compiler/dataflow.cr index 8b8124db..86e11b25 100644 --- a/src/compiler/dataflow.cr +++ b/src/compiler/dataflow.cr @@ -22,15 +22,15 @@ fn init_df(): if fi >= g_func_count: break grow_df_arrays(fi + 1) - w64(g_df_func_node_start, fi * 8, -1) - w64(g_df_func_node_count, fi * 8, 0) + cell_store(g_df_func_node_start, fi * 8, -1) + cell_store(g_df_func_node_count, fi * 8, 0) fi = fi + 1 vi : ., mut = 0 loop: if vi >= g_ir_var_count: break grow_df_arrays(vi + 1) - w64(g_df_var_producer, vi * 8, -1) + cell_store(g_df_var_producer, vi * 8, -1) vi = vi + 1 // --- Subgraph management (bare pointer model) --- @@ -38,23 +38,23 @@ fn init_df(): fn sg_push(kind: int): grow_sg(g_sg_count + 1) idx := g_sg_count - w64(g_sgs, idx * ESZ_SG + OFF_SG_KIND, kind) - w64(g_sgs, idx * ESZ_SG + OFF_SG_ENTER, g_df_node_count) - w64(g_sgs, idx * ESZ_SG + OFF_SG_EXIT, -1) - w64(g_sgs, idx * ESZ_SG + OFF_SG_NSTART, g_df_node_count) - w64(g_sgs, idx * ESZ_SG + OFF_SG_NCOUNT, 0) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_KIND, kind) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_ENTER, g_df_node_count) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_EXIT, -1) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_NSTART, g_df_node_count) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_NCOUNT, 0) // Parent: find innermost currently open subgraph parent := -1 pi := idx - 1 loop: if pi < 0: break - if r64(g_sgs, pi * ESZ_SG + OFF_SG_EXIT) < 0: + if cell_load(g_sgs, pi * ESZ_SG + OFF_SG_EXIT) < 0: // still open parent = pi break pi = pi - 1 - w64(g_sgs, idx * ESZ_SG + OFF_SG_PARENT, parent) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_PARENT, parent) g_cur_sg = idx // new innermost open region g_sg_count = idx + 1 @@ -71,22 +71,22 @@ fn sg_pop(): loop: if pi < 0: break - if r64(g_sgs, pi * ESZ_SG + OFF_SG_EXIT) < 0: + if cell_load(g_sgs, pi * ESZ_SG + OFF_SG_EXIT) < 0: idx = pi break pi = pi - 1 if idx < 0: return - w64(g_sgs, idx * ESZ_SG + OFF_SG_EXIT, g_df_node_count) - w64(g_sgs, idx * ESZ_SG + OFF_SG_NCOUNT, - g_df_node_count - r64(g_sgs, idx * ESZ_SG + OFF_SG_NSTART)) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_EXIT, g_df_node_count) + cell_store(g_sgs, idx * ESZ_SG + OFF_SG_NCOUNT, + g_df_node_count - cell_load(g_sgs, idx * ESZ_SG + OFF_SG_NSTART)) // Loop termination dependency: the region exit node (last node created in // the region, i.e. the exit label) must happen after the region's last // side effect — a VSDG state edge enforcing loop-exit ordering. // 循环终止依赖(kind=1)与序列头推进不在本处连接,状态依赖边序列统一在 // 全部 IR 生成结束后重建(df_replay_state_chain;时点理由见该函数头注): // 被调函数真纯度只有拿到全程序 IR 体才能算,状态依赖关系不能边生成边连接。 - g_cur_sg = r64(g_sgs, idx * ESZ_SG + OFF_SG_PARENT) + g_cur_sg = cell_load(g_sgs, idx * ESZ_SG + OFF_SG_PARENT) // --- Node creation --- @@ -99,22 +99,22 @@ fn df_create_node(opcode: int, dest: int, src1: int, src2: int, src3: int, tk_te nid := g_df_node_count grow_df_nodes(nid + 1) grow_df_node_region(nid + 1) - w64(g_df_node_region, nid * 8, g_cur_sg) // owning region (-1 = none) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_OPCODE, opcode) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_DEST, dest) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S1, src1) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S2, src2) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S3, src3) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_TK, tk_term) // type-term reference - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_AUX, tk_aux) // auxiliary code - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_FIRST_EDGE, -1) - w64(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_EDGE_COUNT, 0) + cell_store(g_df_node_region, nid * 8, g_cur_sg) // owning region (-1 = none) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_OPCODE, opcode) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_DEST, dest) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S1, src1) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S2, src2) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_S3, src3) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_TK, tk_term) // type-term reference + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_AUX, tk_aux) // auxiliary code + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_FIRST_EDGE, -1) + cell_store(g_df_nodes, nid * ESZ_DFNODE + OFF_DF_EDGE_COUNT, 0) g_df_node_count = nid + 1 // Record that this node produces `dest` if dest >= 0: grow_df_arrays(dest + 1) - w64(g_df_var_producer, dest * 8, nid) + cell_store(g_df_var_producer, dest * 8, nid) // Add edges for src fields that are IR variables (based on opcode) // 第 6 参 = 辅码(IR_BOUNDS_CHECK 的 `!= 0` = 动态上限旗标,单槽化后旗标在 @@ -131,14 +131,14 @@ fn df_add_edge_kind(from_id: int, to_id: int, kind: int): return eid := g_df_edge_count grow_df_edges(eid + 1) - w64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_FROM, from_id) - w64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO, to_id) - w64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND, kind) - old_first := r64(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_FIRST_EDGE) - w64(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT, old_first) - w64(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_FIRST_EDGE, eid) - old_cnt := r64(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_EDGE_COUNT) - w64(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_EDGE_COUNT, old_cnt + 1) + cell_store(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_FROM, from_id) + cell_store(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_TO, to_id) + cell_store(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_KIND, kind) + old_first := cell_load(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_FIRST_EDGE) + cell_store(g_df_edges, eid * ESZ_DFEDGE + OFF_DFE_NEXT, old_first) + cell_store(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_FIRST_EDGE, eid) + old_cnt := cell_load(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_EDGE_COUNT) + cell_store(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_EDGE_COUNT, old_cnt + 1) g_df_edge_count = eid + 1 // Data edge (def-use). This is the common case. @@ -180,14 +180,14 @@ fn df_connect_state(node_id: int, opcode: int, s3: int): // The replay keeps node creation order as program order, resets the chain head // per function, and closes loop and for regions at OFF_SG_EXIT. fn df_state_close_regions_at(head: string, next: string, pos: int): - sg := r64(head, pos * 8) + sg := cell_load(head, pos * 8) loop: if sg < 0: break // A region closes at the node after its last node. last_node is the // node immediately before the close position. last_node := pos - 1 - nstart := r64(g_sgs, sg * ESZ_SG + OFF_SG_NSTART) + nstart := cell_load(g_sgs, sg * ESZ_SG + OFF_SG_NSTART) if last_node >= nstart: // 终止边源必须在区内(循环体无副作用时序列头是区前的 store,连过去等于 // 宣称循环退出依赖于该区不含的副作用)。 @@ -196,7 +196,7 @@ fn df_state_close_regions_at(head: string, next: string, pos: int): // 序列头推进到区退出节点:循环之后的副作用必须依赖循环终止 // (spec 4.2:不终止的循环必须终止整图)。 g_last_state_node = last_node - sg = r64(next, sg * 8) + sg = cell_load(next, sg * 8) fn df_replay_state_chain(): if g_ir_func_count <= 0: @@ -210,25 +210,25 @@ fn df_replay_state_chain(): loop: if i > g_df_node_count: break - w64(head, i * 8, -1) + cell_store(head, i * 8, -1) i = i + 1 si : ., mut = 0 loop: if si >= g_sg_count: break - k := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) + k := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) if k == SG_LOOP || k == SG_FOR: - ex := r64(g_sgs, si * ESZ_SG + OFF_SG_EXIT) + ex := cell_load(g_sgs, si * ESZ_SG + OFF_SG_EXIT) if ex >= 0 && ex <= g_df_node_count: - w64(next, si * 8, r64(head, ex * 8)) - w64(head, ex * 8, si) + cell_store(next, si * 8, cell_load(head, ex * 8)) + cell_store(head, ex * 8, si) si = si + 1 irf : ., mut = 0 loop: if irf >= g_ir_func_count: break - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) g_last_state_node = -1 // 每函数独立的状态依赖序列(与 df_begin_func 同语义) n : ., mut = 0 loop: @@ -237,8 +237,8 @@ fn df_replay_state_chain(): if n > 0: df_state_close_regions_at(head, next, start + n) pos := start + n - df_connect_state(pos, r64(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_OPCODE), - r64(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_S3)) + df_connect_state(pos, cell_load(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_OPCODE), + cell_load(g_df_nodes, pos * ESZ_DFNODE + OFF_DF_S3)) n = n + 1 // 函数末端关闭位(SG_FUNC 恒在此;循环落在函数末句时该环也在此),只在此 // 处理:下一函数走 n=0 时跳过 start -> 每个关闭位恰好处理一次。函数 0 的 @@ -268,7 +268,7 @@ fn df_use_var(consumer_node: int, var_idx: int): // where dest=-1 but src1 is a high arena ID variable). if var_idx >= g_df_cap: grow_df_arrays(var_idx + 1) - producer := r64(g_df_var_producer, var_idx * 8) + producer := cell_load(g_df_var_producer, var_idx * 8) if producer >= 0: df_add_edge(producer, consumer_node) @@ -411,7 +411,7 @@ fn grow_var_use_count(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * 8) - _dyncpy(g_var_use_count, g_var_use_count_cap * 8, nb) + cell_copy(g_var_use_count, g_var_use_count_cap * 8, nb) g_var_use_count = nb g_var_use_count_cap = nc @@ -423,15 +423,15 @@ fn compute_usage_counts(): if ei >= g_df_edge_count: break // State edges are not data consumers — skip them for usage counts - if r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: + if cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: ei = ei + 1 continue - from_id := r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) - dest := r64(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_DEST) + from_id := cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) + dest := cell_load(g_df_nodes, from_id * ESZ_DFNODE + OFF_DF_DEST) if dest >= 0: grow_var_use_count(dest + 1) - prev := r64(g_var_use_count, dest * 8) - w64(g_var_use_count, dest * 8, prev + 1) + prev := cell_load(g_var_use_count, dest * 8) + cell_store(g_var_use_count, dest * 8, prev + 1) ei = ei + 1 // --- Mark function boundary in graph --- @@ -439,18 +439,18 @@ fn compute_usage_counts(): fn df_begin_func(func_idx: int): if func_idx >= 0: grow_df_arrays(func_idx + 1) - w64(g_df_func_node_start, func_idx * 8, g_df_node_count) + cell_store(g_df_func_node_start, func_idx * 8, g_df_node_count) // Record the edge start for this function. Edges created after this // point belong to the function; df_begin_func and df_end_func do not // create edges, and the state chain is replayed later. - w64(g_df_func_edge_start, func_idx * 8, g_df_edge_count) + cell_store(g_df_func_edge_start, func_idx * 8, g_df_edge_count) sg_push(SG_FUNC) g_last_state_node = -1 // fresh state chain per function fn df_end_func(func_idx: int): if func_idx >= 0: - start := r64(g_df_func_node_start, func_idx * 8) - w64(g_df_func_node_count, func_idx * 8, g_df_node_count - start) + start := cell_load(g_df_func_node_start, func_idx * 8) + cell_store(g_df_func_node_count, func_idx * 8, g_df_node_count - start) sg_pop() // --- DOT output --- @@ -468,7 +468,7 @@ fn df_graph_to_dot() -> string: loop: if si >= g_sg_count: break - skind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) + skind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) sname : ., mut = "region" if skind == SG_IF: sname = "if" @@ -487,8 +487,8 @@ fn df_graph_to_dot() -> string: dump_buf_append(sname) dump_buf_append("\";\n") // Nodes in this region: - n0 := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) - n1 := r64(g_sgs, si * ESZ_SG + OFF_SG_EXIT) + n0 := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + n1 := cell_load(g_sgs, si * ESZ_SG + OFF_SG_EXIT) if n1 >= 0: // skip unclosed (EXIT<0) entries ni : ., mut = n0 @@ -507,9 +507,9 @@ fn df_graph_to_dot() -> string: loop: if ni >= g_df_node_count: break - n_op := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) - n_dest := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) - n_s3 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3) + n_op := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) + n_dest := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) + n_s3 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3) label : ., mut = df_opcode_name(n_op, n_s3) if n_dest >= 0: vname := get_ir_var_name(n_dest) @@ -527,9 +527,9 @@ fn df_graph_to_dot() -> string: loop: if ei >= g_df_edge_count: break - e_from := r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) - e_to := r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_TO) - if r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: + e_from := cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) + e_to := cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_TO) + if cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: dump_buf_append(" n") dump_buf_append(int_str(e_from)) dump_buf_append(" -> n") diff --git a/src/compiler/diag.cr b/src/compiler/diag.cr index 91bf26ab..28155256 100644 --- a/src/compiler/diag.cr +++ b/src/compiler/diag.cr @@ -22,7 +22,7 @@ fn read_source_line(line: int) -> string: break j = j + 1 return str_sub(g_source, i, j - i) - if load8(g_source, i) == 10: + if cell_load_byte(g_source, i) == 10: cur = cur + 1 start = i + 1 i = i + 1 @@ -35,7 +35,7 @@ fn source_line_count() -> int: loop: if i >= slen: break - if load8(g_source, i) == 10: + if cell_load_byte(g_source, i) == 10: count = count + 1 i = i + 1 return count @@ -71,7 +71,7 @@ fn print_source_line(line: int, col: int, annotation: string): loop: if ci2 + 3 > alen: break - c := load8(annotation, ci2) + c := cell_load_byte(annotation, ci2) if c == 226: // Start of the em dash U+2014, three UTF-8 bytes. cutoff = ci2 @@ -132,7 +132,7 @@ fn pad_diag_num(num: int) -> string: // Line number to file id through g_line_fileid. -1 is out of range. fn diag_fileid_for_line(line: int) -> int: if line >= 1 && line <= g_line_count: - return r64(g_line_fileid, (line - 1) * 8) + return cell_load(g_line_fileid, (line - 1) * 8) return -1 fn print_diagnostics(): @@ -143,10 +143,10 @@ fn print_diagnostics(): loop: if di >= g_diag_count: break - ec := r64(g_diags, di * DIAG_REC_SIZE) - msg := load_str_ptr(g_diags, di * DIAG_REC_SIZE + 8) - ln := r64(g_diags, di * DIAG_REC_SIZE + 16) - cl := r64(g_diags, di * DIAG_REC_SIZE + 24) + ec := cell_load(g_diags, di * DIAG_REC_SIZE) + msg := cell_load_str(g_diags, di * DIAG_REC_SIZE + 8) + ln := cell_load(g_diags, di * DIAG_REC_SIZE + 16) + cl := cell_load(g_diags, di * DIAG_REC_SIZE + 24) cat : ., mut = ec / 1000 num : ., mut = ec % 1000 print("error[") @@ -173,7 +173,7 @@ fn print_parse_errors(): loop: if ei2 >= g_error_count: break - println("error: " + istr_get(r64(g_errors, ei2 * 8))) + println("error: " + istr_get(cell_load(g_errors, ei2 * 8))) ei2 = ei2 + 1 // Fail-closed diagnostic gate exemptions. // Diagnostics are blocking by default. main.cr calls this function for every diff --git a/src/compiler/dump.cr b/src/compiler/dump.cr index c7937432..4c36ae9a 100644 --- a/src/compiler/dump.cr +++ b/src/compiler/dump.cr @@ -20,7 +20,7 @@ fn dump_buf_grow(needed: int): nc = nc * 2 nb := alloc(nc) if g_dump_pos > 0: - _dyncpy(g_dump_buf, g_dump_pos, nb) + cell_copy(g_dump_buf, g_dump_pos, nb) g_dump_buf = nb g_dump_cap = nc @@ -31,15 +31,15 @@ fn dump_buf_append(s: string): loop: if i >= sl: break - store8(g_dump_buf, g_dump_pos + i, load8(s, i)) + cell_store_byte(g_dump_buf, g_dump_pos + i, cell_load_byte(s, i)) i = i + 1 g_dump_pos = g_dump_pos + sl fn dump_buf_finish() -> string: out := alloc(g_dump_pos + 1) if g_dump_pos > 0: - _dyncpy(g_dump_buf, g_dump_pos, out) - store8(out, g_dump_pos, 0) + cell_copy(g_dump_buf, g_dump_pos, out) + cell_store_byte(out, g_dump_pos, 0) return out fn ir_var_str(var_idx: int) -> string: @@ -275,23 +275,23 @@ fn cir_text_dump() -> string: loop: if fi >= g_ir_func_count: break - name_ni := r64(g_ir_func_name_idx, fi * 8) + name_ni := cell_load(g_ir_func_name_idx, fi * 8) dump_buf_append("Function: ") dump_buf_append(istr_get(name_ni)) dump_buf_append("\n") - start := r64(g_ir_func_instr_start, fi * 8) - count := r64(g_ir_func_instr_count, fi * 8) + start := cell_load(g_ir_func_instr_start, fi * 8) + count := cell_load(g_ir_func_instr_count, fi * 8) // Region list for this function (regions whose DFNode start falls in // this function's instruction range) ri : ., mut = 0 loop: if ri >= g_sg_count: break - rkind := r64(g_sgs, ri * ESZ_SG + OFF_SG_KIND) - rstart := r64(g_sgs, ri * ESZ_SG + OFF_SG_NSTART) - rend := r64(g_sgs, ri * ESZ_SG + OFF_SG_EXIT) + rkind := cell_load(g_sgs, ri * ESZ_SG + OFF_SG_KIND) + rstart := cell_load(g_sgs, ri * ESZ_SG + OFF_SG_NSTART) + rend := cell_load(g_sgs, ri * ESZ_SG + OFF_SG_EXIT) if rstart >= start && rstart < start + count: - rend := r64(g_sgs, ri * ESZ_SG + OFF_SG_EXIT) + rend := cell_load(g_sgs, ri * ESZ_SG + OFF_SG_EXIT) if rend >= 0: // skip unclosed (EXIT<0) regions rname : ., mut = "?" @@ -341,9 +341,9 @@ fn cir_text_dump() -> string: loop: if ei >= g_df_edge_count: break - if r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: - e_from := r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) - e_to := r64(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_TO) + if cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_KIND) != 0: + e_from := cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_FROM) + e_to := cell_load(g_df_edges, ei * ESZ_DFEDGE + OFF_DFE_TO) dump_buf_append(" state: n") dump_buf_append(int_str(e_from)) dump_buf_append(" -> n") @@ -377,12 +377,12 @@ fn df_tk_term_dump() -> string: loop: if n0 >= total: break - t0 := r64(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_TK) + t0 := cell_load(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_TK) if t0 >= 0: with_term = with_term + 1 if t0 >= tt_count(): bad_term = bad_term + 1 - if r64(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_AUX) != 0: + if cell_load(g_df_nodes, n0 * ESZ_DFNODE + OFF_DF_AUX) != 0: auxn = auxn + 1 n0 = n0 + 1 dump_buf_append("[df-tk-terms] nodes=") @@ -418,9 +418,9 @@ fn df_tk_term_dump() -> string: loop: if n >= total: break - op := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) - t := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK) - ax := r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX) + op := cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) + t := cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_TK) + ax := cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_AUX) tk := sh_dfn_code_of_slots(t, ax) dump_buf_append(int_str(n)) dump_buf_append("\t") diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index 5ff899bb..2069df0d 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -1,68 +1,7 @@ // === dyn_arr.cr === -// Byte helpers + ESZ/OFF constants + grow functions + string pool. - -// ============================================================ -// Byte helpers -// ============================================================ -fn w8(buf: string, pos: int, val: int): - store8(buf, pos, val % 256) -fn bu8(buf: string, pos: int) -> int: - return load8(buf, pos) % 256 -fn w32(buf: string, pos: int, val: int): - b0 : ., mut = val % 256 - t1 : ., mut = val / 256 - b1 : ., mut = t1 % 256 - t2 : ., mut = t1 / 256 - b2 : ., mut = t2 % 256 - t3 : ., mut = t2 / 256 - b3 : ., mut = t3 % 256 - // Borrow chain for two's complement of negative val (no 4294967296 constant) - if b0 < 0: - b0 = b0 + 256 - b1 = b1 - 1 - if b1 < 0: - b1 = b1 + 256 - b2 = b2 - 1 - if b2 < 0: - b2 = b2 + 256 - b3 = b3 - 1 - if b3 < 0: - b3 = b3 + 256 - store8(buf,pos,b0) - store8(buf,pos+1,b1) - store8(buf,pos+2,b2) - store8(buf,pos+3,b3) -fn r32(buf: string, pos: int) -> int: - b0 := bu8(buf,pos) - b1 := bu8(buf,pos+1) - b2 := bu8(buf,pos+2) - b3 := bu8(buf,pos+3) - v := b0 + b1*256 + b2*65536 - if b3 >= 128: - v = v + (b3 - 256) * 16777216 - else: - v = v + b3 * 16777216 - return v -fn w64(buf: string, pos: int, val: int): - cur : ., mut = val - i : ., mut = 0 - loop: - if i >= 8: - break - byte : ., mut = cur % 256 - if byte < 0: - byte = byte + 256 - store8(buf, pos + i, byte) - cur = (cur - byte) / 256 - i = i + 1 -fn r64(buf: string, pos: int) -> int: - // The low dword is unsigned. Reading it through r32 double-subtracts - // 2^32 for negative values, e.g. -1 becomes -1 + (-1 * 2^32). - lo := bu8(buf,pos) + bu8(buf,pos+1)*256 + bu8(buf,pos+2)*65536 + bu8(buf,pos+3)*16777216 - hi := r32(buf,pos+4) - hi_part := hi * 65536 - hi_part = hi_part * 65536 - return lo + hi_part +// Compiler table constants + grow functions + string pool. +// Byte helpers (w8/bu8/w32/r32/w64/r64/_dyncpy) live in src/kernel/bytes.cr; +// they are the low-level cell layer, not frontend semantics. // ============================================================ // Element sizes (bytes per element) @@ -270,10 +209,10 @@ fn iface_shape_grow(needed: int): if nc < needed: nc = needed + 8 nb := alloc(nc * 8) - _dyncpy(g_iface_shape_names, g_iface_shape_cap * 8, nb) + cell_copy(g_iface_shape_names, g_iface_shape_cap * 8, nb) g_iface_shape_names = nb nt := alloc(nc * 8) - _dyncpy(g_iface_shape_terms, g_iface_shape_cap * 8, nt) + cell_copy(g_iface_shape_terms, g_iface_shape_cap * 8, nt) g_iface_shape_terms = nt g_iface_shape_cap = nc @@ -296,17 +235,6 @@ OFF_EV_TYPE_COUNT : int = 136 OFF_EV_TYPE_NODES : int = 144 OFF_EV_SIZE : int = 272 -// ============================================================ -// Copy helper -// ============================================================ -fn _dyncpy(src: string, nbytes: int, dst: string): - ci : ., mut = 0 - loop: - if ci >= nbytes: - break - w8(dst, ci, bu8(src, ci)) - ci = ci + 1 - // ============================================================ // Grow helpers for all arrays // ============================================================ @@ -325,7 +253,7 @@ fn grow_tokens(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_TOKEN) - _dyncpy(g_tokens, g_tok_cap * ESZ_TOKEN, nb) + cell_copy(g_tokens, g_tok_cap * ESZ_TOKEN, nb) g_tokens = nb g_tok_cap = nc @@ -338,7 +266,7 @@ fn grow_ast(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_ASTNODE) - _dyncpy(g_ast, g_ast_cap * ESZ_ASTNODE, nb) + cell_copy(g_ast, g_ast_cap * ESZ_ASTNODE, nb) g_ast = nb g_ast_cap = nc @@ -351,7 +279,7 @@ fn grow_syms(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * ESZ_SYMENTRY) - _dyncpy(g_syms, g_sym_cap * ESZ_SYMENTRY, nb) + cell_copy(g_syms, g_sym_cap * ESZ_SYMENTRY, nb) g_syms = nb g_sym_cap = nc @@ -366,7 +294,7 @@ fn grow_types(needed: int): // Row size comes from ESZ_TYPE_ROW in globals.cr, shared with alloc_type and // TYPE-segment serialization. nb := alloc(nc * ESZ_TYPE_ROW) - _dyncpy(g_types, g_type_cap * ESZ_TYPE_ROW, nb) + cell_copy(g_types, g_type_cap * ESZ_TYPE_ROW, nb) g_types = nb g_type_cap = nc @@ -379,7 +307,7 @@ fn grow_funcs(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * ESZ_FUNCINFO) - _dyncpy(g_funcs, g_func_cap * ESZ_FUNCINFO, nb) + cell_copy(g_funcs, g_func_cap * ESZ_FUNCINFO, nb) g_funcs = nb g_func_cap = nc @@ -392,7 +320,7 @@ fn grow_structs(needed: int): if nc < needed: nc = needed + 32 nb := alloc(nc * ESZ_STRUCTINFO) - _dyncpy(g_structs, g_struct_cap * ESZ_STRUCTINFO, nb) + cell_copy(g_structs, g_struct_cap * ESZ_STRUCTINFO, nb) g_structs = nb g_struct_cap = nc @@ -405,7 +333,7 @@ fn grow_enums(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * ESZ_ENUMINFO) - _dyncpy(g_enums, g_enum_cap * ESZ_ENUMINFO, nb) + cell_copy(g_enums, g_enum_cap * ESZ_ENUMINFO, nb) g_enums = nb g_enum_cap = nc @@ -418,7 +346,7 @@ fn grow_ifaces(needed: int): if nc < needed: nc = needed + 4 nb := alloc(nc * ESZ_IFACEINFO) - _dyncpy(g_ifaces, g_iface_cap * ESZ_IFACEINFO, nb) + cell_copy(g_ifaces, g_iface_cap * ESZ_IFACEINFO, nb) g_ifaces = nb g_iface_cap = nc @@ -431,7 +359,7 @@ fn grow_ir_vars(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_IRVAR) - _dyncpy(g_ir_vars, g_ir_var_cap * ESZ_IRVAR, nb) + cell_copy(g_ir_vars, g_ir_var_cap * ESZ_IRVAR, nb) g_ir_vars = nb g_ir_var_cap = nc @@ -444,7 +372,7 @@ fn grow_ir_instrs(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_IRINSTR) - _dyncpy(g_ir_instrs, g_ir_instr_cap * ESZ_IRINSTR, nb) + cell_copy(g_ir_instrs, g_ir_instr_cap * ESZ_IRINSTR, nb) g_ir_instrs = nb g_ir_instr_cap = nc @@ -457,7 +385,7 @@ fn grow_ir_locals(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 16) - _dyncpy(g_ir_locals, g_ir_local_cap * 16, nb) + cell_copy(g_ir_locals, g_ir_local_cap * 16, nb) g_ir_locals = nb g_ir_local_cap = nc @@ -470,7 +398,7 @@ fn grow_ir_globals(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 24) - _dyncpy(g_ir_globals, g_ir_global_cap * 24, nb) + cell_copy(g_ir_globals, g_ir_global_cap * 24, nb) g_ir_globals = nb g_ir_global_cap = nc @@ -483,7 +411,7 @@ fn grow_ir_str_consts(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_ir_str_consts, g_ir_str_const_cap * 8, nb) + cell_copy(g_ir_str_consts, g_ir_str_const_cap * 8, nb) g_ir_str_consts = nb g_ir_str_const_cap = nc @@ -496,7 +424,7 @@ fn grow_errors(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_errors, g_err_cap * 8, nb) + cell_copy(g_errors, g_err_cap * 8, nb) g_errors = nb g_err_cap = nc @@ -509,7 +437,7 @@ fn grow_block_stmts(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_block_stmts, g_block_stmt_cap * 8, nb) + cell_copy(g_block_stmts, g_block_stmt_cap * 8, nb) g_block_stmts = nb g_block_stmt_cap = nc @@ -525,25 +453,25 @@ fn grow_ir_func_meta(needed: int): nc = needed + 64 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_ir_func_name_idx, g_ir_func_name_idx_cap*8, n1) + cell_copy(g_ir_func_name_idx, g_ir_func_name_idx_cap*8, n1) g_ir_func_name_idx = n1 n2 := alloc(sz) - _dyncpy(g_ir_func_ret_type, g_ir_func_ret_type_cap*8, n2) + cell_copy(g_ir_func_ret_type, g_ir_func_ret_type_cap*8, n2) g_ir_func_ret_type = n2 n3 := alloc(sz) - _dyncpy(g_ir_func_instr_start, g_ir_func_instr_start_cap*8, n3) + cell_copy(g_ir_func_instr_start, g_ir_func_instr_start_cap*8, n3) g_ir_func_instr_start = n3 n4 := alloc(sz) - _dyncpy(g_ir_func_instr_count, g_ir_func_instr_count_cap*8, n4) + cell_copy(g_ir_func_instr_count, g_ir_func_instr_count_cap*8, n4) g_ir_func_instr_count = n4 n5 := alloc(sz) - _dyncpy(g_ir_func_var_start, g_ir_func_var_start_cap*8, n5) + cell_copy(g_ir_func_var_start, g_ir_func_var_start_cap*8, n5) g_ir_func_var_start = n5 n6 := alloc(sz) - _dyncpy(g_ir_func_var_count, g_ir_func_var_count_cap*8, n6) + cell_copy(g_ir_func_var_count, g_ir_func_var_count_cap*8, n6) g_ir_func_var_count = n6 n7 := alloc(sz) - _dyncpy(g_ir_func_param_count, g_ir_func_param_count_cap*8, n7) + cell_copy(g_ir_func_param_count, g_ir_func_param_count_cap*8, n7) g_ir_func_param_count = n7 g_ir_func_name_idx_cap=nc g_ir_func_ret_type_cap=nc @@ -557,55 +485,55 @@ fn grow_ir_func_meta(needed: int): // Accessor helpers for AST node fields // ============================================================ fn ast_kind(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_KIND) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_KIND) fn ast_a(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_A) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_A) fn ast_b(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_B) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_B) fn ast_c(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_C) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_C) fn ast_int_val(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_INTVAL) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_INTVAL) fn ast_type_val(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_TYPEVAL) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_TYPEVAL) fn ast_data(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_DATA) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_DATA) fn ast_line(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_LINE) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_LINE) fn ast_col(n: int) -> int: - return r64(g_ast, n * ESZ_ASTNODE + OFF_AS_COL) + return cell_load(g_ast, n * ESZ_ASTNODE + OFF_AS_COL) fn ast_set_kind(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_KIND, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_KIND, v) fn ast_set_a(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_A, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_A, v) fn ast_set_b(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_B, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_B, v) fn ast_set_c(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_C, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_C, v) fn ast_set_int_val(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_INTVAL, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_INTVAL, v) fn ast_set_type_val(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_TYPEVAL, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_TYPEVAL, v) fn ast_set_data(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_DATA, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_DATA, v) fn ast_set_line(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_LINE, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_LINE, v) fn ast_set_col(n: int, v: int): - w64(g_ast, n * ESZ_ASTNODE + OFF_AS_COL, v) + cell_store(g_ast, n * ESZ_ASTNODE + OFF_AS_COL, v) fn ast_alloc(kind: int, a: int, b: int, c: int, iv: int, tv: int, d: int, line: int, col: int) -> int: idx := g_ast_count grow_ast(idx + 1) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_KIND, kind) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_A, a) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_B, b) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_C, c) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_INTVAL, iv) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_TYPEVAL, tv) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_DATA, d) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_LINE, line) - w64(g_ast, idx * ESZ_ASTNODE + OFF_AS_COL, col) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_KIND, kind) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_A, a) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_B, b) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_C, c) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_INTVAL, iv) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_TYPEVAL, tv) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_DATA, d) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_LINE, line) + cell_store(g_ast, idx * ESZ_ASTNODE + OFF_AS_COL, col) g_ast_count = idx + 1 return idx @@ -617,67 +545,67 @@ fn ast_alloc(kind: int, a: int, b: int, c: int, iv: int, tv: int, d: int, line: fn sym_name(n: int) -> int: if n < 0 || n >= g_sym_count: return -1 - return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME) + return cell_load(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME) fn sym_kind(n: int) -> int: if n < 0 || n >= g_sym_count: return -1 - return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND) + return cell_load(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND) fn sym_type(n: int) -> int: if n < 0 || n >= g_sym_count: return -1 - return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE) + return cell_load(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE) fn sym_node(n: int) -> int: if n < 0 || n >= g_sym_count: return -1 - return r64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE) + return cell_load(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE) fn sym_set_name(n: int, v: int): - w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME, v) + cell_store(g_syms, n * ESZ_SYMENTRY + OFF_SY_NAME, v) fn sym_set_kind(n: int, v: int): - w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND, v) + cell_store(g_syms, n * ESZ_SYMENTRY + OFF_SY_KIND, v) fn sym_set_type(n: int, v: int): - w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE, v) + cell_store(g_syms, n * ESZ_SYMENTRY + OFF_SY_TYPE, v) fn sym_set_node(n: int, v: int): - w64(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE, v) + cell_store(g_syms, n * ESZ_SYMENTRY + OFF_SY_NODE, v) // IRVar helpers fn irv_name(n: int) -> int: - return r64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME) + return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME) fn irv_id(n: int) -> int: - return r64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_ID) + return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_ID) fn irv_type(n: int) -> int: - return r64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_TYPE) + return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_TYPE) fn irv_set_name(n: int, v: int): - w64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME, v) + cell_store(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME, v) fn irv_set_id(n: int, v: int): - w64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_ID, v) + cell_store(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_ID, v) fn irv_set_type(n: int, v: int): - w64(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_TYPE, v) + cell_store(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_TYPE, v) // IRInstr helpers fn iri_op(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_OP) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_OP) fn iri_dest(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_DEST) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_DEST) fn iri_s1(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S1) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S1) fn iri_s2(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S2) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S2) fn iri_s3(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S3) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S3) fn iri_tk(n: int) -> int: - return r64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_TK) + return cell_load(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_TK) fn iri_set_op(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_OP, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_OP, v) fn iri_set_dest(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_DEST, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_DEST, v) fn iri_set_s1(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S1, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S1, v) fn iri_set_s2(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S2, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S2, v) fn iri_set_s3(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S3, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_S3, v) fn iri_set_tk(n: int, v: int): - w64(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_TK, v) + cell_store(g_ir_instrs, n * ESZ_IRINSTR + OFF_IRI_TK, v) // Variable-name lookup. It lives here because regalloc.cr in corearch consumes it, // and the corearch build does not include ir_gen.cr. @@ -689,53 +617,53 @@ fn get_ir_var_name(var_idx: int) -> string: // FuncInfo helpers fn fi_name(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME) fn fi_param_count(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT) fn fi_return_type(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE) fn fi_ast_node(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE) fn fi_generic_count(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_COUNT) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_COUNT) fn fi_param_type(n: int, pi: int) -> int: if pi < 0 || pi >= MAX_FN_PARAMS: return 0 // Out of range: read guard only; writes have the same guard plus parser P020. - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi * 8) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi * 8) fn fi_generic_name(n: int, gi: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi * 8) fn fi_set_name(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_NAME, v) fn fi_set_param_count(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_COUNT, v) fn fi_set_param_type(n: int, pi: int, v: int): if pi < 0 || pi >= MAX_FN_PARAMS: return // Slot guard. An out-of-range write would overwrite return_type, ast_node, // generic_*, and ispure. - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi*8, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_PARAM_TYPES + pi*8, v) fn fi_set_return_type(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_RETURN_TYPE, v) fn fi_set_ast_node(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_AST_NODE, v) fn fi_set_generic_name(n: int, gi: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi*8, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_NAMES + gi*8, v) fn fi_set_generic_count(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_COUNT, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_GENERIC_COUNT, v) fn fi_ispure(n: int) -> int: - return r64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_ISPURE) + return cell_load(g_funcs, n * ESZ_FUNCINFO + OFF_FI_ISPURE) fn fi_set_ispure(n: int, v: int): - w64(g_funcs, n * ESZ_FUNCINFO + OFF_FI_ISPURE, v) + cell_store(g_funcs, n * ESZ_FUNCINFO + OFF_FI_ISPURE, v) // StructInfo helpers fn si_name(n: int) -> int: - return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_NAME) + return cell_load(g_structs, n * ESZ_STRUCTINFO + OFF_SI_NAME) fn si_field_count(n: int) -> int: - return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT) + return cell_load(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT) fn si_generic_count(n: int) -> int: - return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT) + return cell_load(g_structs, n * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT) // Struct field side tables. OFF_SI_FIELD_BASE is the starting row and // OFF_SI_FIELD_COUNT is the true count. Three flat tables are addressed by record // base + field index. si_commit_fields advances g_si_f_used, reserving the whole @@ -749,19 +677,19 @@ fn grow_si_fields(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_si_f_names, g_si_f_cap * 8, nb) + cell_copy(g_si_f_names, g_si_f_cap * 8, nb) g_si_f_names = nb tb := alloc(nc * 8) - _dyncpy(g_si_f_types, g_si_f_cap * 8, tb) + cell_copy(g_si_f_types, g_si_f_cap * 8, tb) g_si_f_types = tb ob := alloc(nc * 8) - _dyncpy(g_si_f_nodes, g_si_f_cap * 8, ob) + cell_copy(g_si_f_nodes, g_si_f_cap * 8, ob) g_si_f_nodes = ob g_si_f_cap = nc fn si_field_base(n: int) -> int: - return r64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE) + return cell_load(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE) fn si_set_field_base(n: int, v: int): - w64(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE, v) + cell_store(g_structs, n * ESZ_STRUCTINFO + OFF_SI_FIELD_BASE, v) // Commit the field range, advancing the high-water mark to base + count. parser and // the ccr_io loader share this. fn si_commit_fields(n: int, fc: int): @@ -787,29 +715,29 @@ fn si_field_name(n: int, fi: int) -> int: r := si_field_row(n, fi) if r < 0: return -1 - return r64(g_si_f_names, r * 8) + return cell_load(g_si_f_names, r * 8) fn si_field_type(n: int, fi: int) -> int: r := si_field_row(n, fi) if r < 0: return -1 - return r64(g_si_f_types, r * 8) + return cell_load(g_si_f_types, r * 8) fn si_field_type_node(n: int, fi: int) -> int: r := si_field_row(n, fi) if r < 0: return -1 - return r64(g_si_f_nodes, r * 8) + return cell_load(g_si_f_nodes, r * 8) fn si_generic_name(n: int, gi: int) -> int: - return r64(g_structs, n*ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + gi*8) + return cell_load(g_structs, n*ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + gi*8) // EnumInfo helpers fn ei_name(n: int) -> int: - return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_NAME) + return cell_load(g_enums, n * ESZ_ENUMINFO + OFF_EI_NAME) fn ei_variant_count(n: int) -> int: - return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT) + return cell_load(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT) fn ei_generic_count(n: int) -> int: - return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT) + return cell_load(g_enums, n * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT) fn ei_generic_name(n: int, gi: int) -> int: - return r64(g_enums, n*ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + gi*8) + return cell_load(g_enums, n*ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + gi*8) // Read guard: out-of-range reads return -1 for names, type codes, and type nodes, // and 0 for counts. They never read the neighboring record. // Enum variant side tables, the same shape as struct fields. @@ -822,19 +750,19 @@ fn grow_ei_variants(needed: int): if nc < needed: nc = needed + 64 a := alloc(nc * 8) - _dyncpy(g_ei_v_names, g_ei_v_cap * 8, a) + cell_copy(g_ei_v_names, g_ei_v_cap * 8, a) g_ei_v_names = a b := alloc(nc * 8) - _dyncpy(g_ei_v_tbase, g_ei_v_cap * 8, b) + cell_copy(g_ei_v_tbase, g_ei_v_cap * 8, b) g_ei_v_tbase = b c := alloc(nc * 8) - _dyncpy(g_ei_v_tcount, g_ei_v_cap * 8, c) + cell_copy(g_ei_v_tcount, g_ei_v_cap * 8, c) g_ei_v_tcount = c z : ., mut = g_ei_v_cap loop: if z >= nc: break - w64(b, z * 8, -1) + cell_store(b, z * 8, -1) z = z + 1 // tbase 新槽 = -1 哨兵 g_ei_v_cap = nc @@ -847,16 +775,16 @@ fn grow_ei_vtypes(needed: int): if nc < needed: nc = needed + 64 a := alloc(nc * 8) - _dyncpy(g_ei_vt_types, g_ei_vt_cap * 8, a) + cell_copy(g_ei_vt_types, g_ei_vt_cap * 8, a) g_ei_vt_types = a b := alloc(nc * 8) - _dyncpy(g_ei_vt_nodes, g_ei_vt_cap * 8, b) + cell_copy(g_ei_vt_nodes, g_ei_vt_cap * 8, b) g_ei_vt_nodes = b g_ei_vt_cap = nc fn ei_variant_base(n: int) -> int: - return r64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS) + return cell_load(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS) fn ei_set_variant_base(n: int, v: int): - w64(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS, v) + cell_store(g_enums, n * ESZ_ENUMINFO + OFF_EI_VARIANTS, v) fn ei_commit_variants(n: int, vc: int): b := ei_variant_base(n) if b < 0: @@ -878,22 +806,22 @@ fn ei_variant_name(n: int, vi: int) -> int: r := ei_variant_row(n, vi) if r < 0: return -1 - return r64(g_ei_v_names, r * 8) + return cell_load(g_ei_v_names, r * 8) fn ei_variant_type_count(n: int, vi: int) -> int: r := ei_variant_row(n, vi) if r < 0: return 0 - return r64(g_ei_v_tcount, r * 8) + return cell_load(g_ei_v_tcount, r * 8) fn ei_variant_type(n: int, vi: int, ti: int) -> int: r := ei_variant_row(n, vi) if r < 0: return -1 - if ti < 0 || ti >= r64(g_ei_v_tcount, r * 8): + if ti < 0 || ti >= cell_load(g_ei_v_tcount, r * 8): return -1 - tb := r64(g_ei_v_tbase, r * 8) + tb := cell_load(g_ei_v_tbase, r * 8) if tb < 0: return -1 - return r64(g_ei_vt_types, (tb + ti) * 8) + return cell_load(g_ei_vt_types, (tb + ti) * 8) // Type node of payload ti, or -1/0 when there is no information. The .ccr does not // store this column, so a loader read returns 0. Consumers must bound ti by // type_count and must not interpret 0 as node 0. @@ -901,12 +829,12 @@ fn ei_variant_type_node(n: int, vi: int, ti: int) -> int: r := ei_variant_row(n, vi) if r < 0: return -1 - if ti < 0 || ti >= r64(g_ei_v_tcount, r * 8): + if ti < 0 || ti >= cell_load(g_ei_v_tcount, r * 8): return -1 - tb := r64(g_ei_v_tbase, r * 8) + tb := cell_load(g_ei_v_tbase, r * 8) if tb < 0: return -1 - return r64(g_ei_vt_nodes, (tb + ti) * 8) + return cell_load(g_ei_vt_nodes, (tb + ti) * 8) // Guarded enum and struct accessors. // Field and variant storage now lives in side tables. The setters below are the @@ -925,7 +853,7 @@ fn ei_set_variant_name(n: int, vi: int, v: int): grow_ei_variants(b + vi + 1) if b + vi + 1 > g_ei_v_used: g_ei_v_used = b + vi + 1 - w64(g_ei_v_names, (b + vi) * 8, v) + cell_store(g_ei_v_names, (b + vi) * 8, v) // Payload bases are allocated lazily: the first payload write reserves from the high-water mark. fn ei_variant_tbase_ensure(n: int, vi: int) -> int: @@ -934,10 +862,10 @@ fn ei_variant_tbase_ensure(n: int, vi: int) -> int: return -1 grow_ei_variants(b + vi + 1) r := b + vi - tb := r64(g_ei_v_tbase, r * 8) + tb := cell_load(g_ei_v_tbase, r * 8) if tb < 0: tb = g_ei_vt_used - w64(g_ei_v_tbase, r * 8, tb) + cell_store(g_ei_v_tbase, r * 8, tb) return tb fn ei_set_variant_type(n: int, vi: int, ti: int, v: int): if vi < 0 || ti < 0: @@ -946,7 +874,7 @@ fn ei_set_variant_type(n: int, vi: int, ti: int, v: int): if tb < 0: return grow_ei_vtypes(tb + ti + 1) - w64(g_ei_vt_types, (tb + ti) * 8, v) + cell_store(g_ei_vt_types, (tb + ti) * 8, v) if tb + ti + 1 > g_ei_vt_used: g_ei_vt_used = tb + ti + 1 fn ei_set_variant_type_count(n: int, vi: int, v: int): @@ -956,7 +884,7 @@ fn ei_set_variant_type_count(n: int, vi: int, v: int): if b < 0: return grow_ei_variants(b + vi + 1) - w64(g_ei_v_tcount, (b + vi) * 8, v) + cell_store(g_ei_v_tcount, (b + vi) * 8, v) if v > 0: tb := ei_variant_tbase_ensure(n, vi) // 申报区间整体占位(未写槽亦不与他人重叠) if tb >= 0 && tb + v > g_ei_vt_used: @@ -968,7 +896,7 @@ fn ei_set_variant_type_node(n: int, vi: int, ti: int, v: int): if tb < 0: return grow_ei_vtypes(tb + ti + 1) - w64(g_ei_vt_nodes, (tb + ti) * 8, v) + cell_store(g_ei_vt_nodes, (tb + ti) * 8, v) if tb + ti + 1 > g_ei_vt_used: g_ei_vt_used = tb + ti + 1 fn si_set_field_name(n: int, fi: int, v: int): @@ -980,7 +908,7 @@ fn si_set_field_name(n: int, fi: int, v: int): grow_si_fields(b + fi + 1) if b + fi + 1 > g_si_f_used: g_si_f_used = b + fi + 1 - w64(g_si_f_names, (b + fi) * 8, v) + cell_store(g_si_f_names, (b + fi) * 8, v) fn si_set_field_type(n: int, fi: int, v: int): if fi < 0: return @@ -990,7 +918,7 @@ fn si_set_field_type(n: int, fi: int, v: int): grow_si_fields(b + fi + 1) if b + fi + 1 > g_si_f_used: g_si_f_used = b + fi + 1 - w64(g_si_f_types, (b + fi) * 8, v) + cell_store(g_si_f_types, (b + fi) * 8, v) fn si_set_field_type_node(n: int, fi: int, v: int): if fi < 0: return @@ -1000,7 +928,7 @@ fn si_set_field_type_node(n: int, fi: int, v: int): grow_si_fields(b + fi + 1) if b + fi + 1 > g_si_f_used: g_si_f_used = b + fi + 1 - w64(g_si_f_nodes, (b + fi) * 8, v) + cell_store(g_si_f_nodes, (b + fi) * 8, v) // ============================================================ // String table helpers (dynamic byte buffer) @@ -1014,7 +942,7 @@ fn grow_g_strs(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_strs, g_str_cap * 8, nb) + cell_copy(g_strs, g_str_cap * 8, nb) g_strs = nb g_str_cap = nc @@ -1022,7 +950,7 @@ fn str_hash(s: string) -> int: h : ., mut = 5381 i : ., mut = 0 loop: - c := load8(s, i) + c := cell_load_byte(s, i) if c == 0: break h = h * 33 + c @@ -1039,7 +967,7 @@ fn hash_bytes(data: string, len: int) -> int: if i >= len: break h = h * 16777619 // FNV prime - h = h + (load8(data, i) % 256) + h = h + (cell_load_byte(data, i) % 256) i = i + 1 return h @@ -1050,7 +978,7 @@ fn _grow_str_hash(ncap: int): loop: if i >= ncap: break - w64(nb, i * 8, -1) + cell_store(nb, i * 8, -1) i = i + 1 // Rehash all strings already in g_strs into new table @@ -1058,14 +986,14 @@ fn _grow_str_hash(ncap: int): loop: if si >= g_str_count: break - s := load_str_ptr(g_strs, si * 8) + s := cell_load_str(g_strs, si * 8) h := str_hash(s) pos := h % ncap if pos < 0: pos = -pos loop: - if r64(nb, pos * 8) < 0: - w64(nb, pos * 8, si) + if cell_load(nb, pos * 8) < 0: + cell_store(nb, pos * 8, si) break pos = (pos + 1) % ncap si = si + 1 @@ -1084,17 +1012,17 @@ fn str_intern(s: string) -> int: if pos < 0: pos = -pos loop: - si := r64(g_str_hash, pos * 8) + si := cell_load(g_str_hash, pos * 8) if si < 0: break // Empty slot means not found. - if str_eq(load_str_ptr(g_strs, si * 8), s) != 0: + if str_eq(cell_load_str(g_strs, si * 8), s) != 0: return si pos = (pos + 1) % cap // Not found: insert grow_g_strs(g_str_count + 1) - store_str_ptr(g_strs, g_str_count * 8, s) + cell_store_str(g_strs, g_str_count * 8, s) g_str_count = g_str_count + 1 // This is the only insertion path for interned strings, so every new string @@ -1103,7 +1031,7 @@ fn str_intern(s: string) -> int: cir_rec_push(g_str_count - 1) // Insert into hash table (pos is the empty slot we found) - w64(g_str_hash, pos * 8, g_str_count - 1) + cell_store(g_str_hash, pos * 8, g_str_count - 1) // Auto-rehash if load > 70% if g_str_count * 10 > g_str_hash_cap * 7: @@ -1119,16 +1047,16 @@ fn cir_rec_push(si: int): if nc < 64: nc = 64 nb := alloc(nc * 8) - _dyncpy(g_cir_rec_ids, g_cir_rec_cap * 8, nb) + cell_copy(g_cir_rec_ids, g_cir_rec_cap * 8, nb) g_cir_rec_ids = nb g_cir_rec_cap = nc - w64(g_cir_rec_ids, g_cir_rec_count * 8, si) + cell_store(g_cir_rec_ids, g_cir_rec_count * 8, si) g_cir_rec_count = g_cir_rec_count + 1 fn istr_get(idx: int) -> string: if idx < 0 || idx >= g_str_count: return "" - return load_str_ptr(g_strs, idx * 8) + return cell_load_str(g_strs, idx * 8) fn istr_len(idx: int) -> int: if idx < 0 || idx >= g_str_count: @@ -1138,7 +1066,7 @@ fn istr_len(idx: int) -> int: fn str_load8(idx: int, ci: int) -> int: if idx < 0 || idx >= g_str_count: return 0 - return load8(istr_get(idx), ci) + return cell_load_byte(istr_get(idx), ci) fn grow_line_file(needed: int): if needed < g_line_cap: @@ -1149,7 +1077,7 @@ fn grow_line_file(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * 8) - _dyncpy(g_line_fileid, g_line_cap * 8, nb) + cell_copy(g_line_fileid, g_line_cap * 8, nb) g_line_fileid = nb g_line_cap = nc fn grow_segs(needed: int): @@ -1162,10 +1090,10 @@ fn grow_segs(needed: int): nc = needed + 16 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_seg_starts, g_seg_cap * 8, n1) + cell_copy(g_seg_starts, g_seg_cap * 8, n1) g_seg_starts = n1 n2 := alloc(sz) - _dyncpy(g_seg_fileids, g_seg_cap * 8, n2) + cell_copy(g_seg_fileids, g_seg_cap * 8, n2) g_seg_fileids = n2 g_seg_cap = nc fn grow_gen_apply_data(needed: int): @@ -1177,7 +1105,7 @@ fn grow_gen_apply_data(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_gen_apply_data, g_gen_apply_data_cap * 8, nb) + cell_copy(g_gen_apply_data, g_gen_apply_data_cap * 8, nb) g_gen_apply_data = nb g_gen_apply_data_cap = nc fn grow_df_nodes(needed: int): @@ -1189,7 +1117,7 @@ fn grow_df_nodes(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_DFNODE) - _dyncpy(g_df_nodes, g_df_node_cap * ESZ_DFNODE, nb) + cell_copy(g_df_nodes, g_df_node_cap * ESZ_DFNODE, nb) g_df_nodes = nb g_df_node_cap = nc fn grow_df_node_region(needed: int): @@ -1201,7 +1129,7 @@ fn grow_df_node_region(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * 8) - _dyncpy(g_df_node_region, g_df_node_region_cap * 8, nb) + cell_copy(g_df_node_region, g_df_node_region_cap * 8, nb) g_df_node_region = nb g_df_node_region_cap = nc fn grow_df_edges(needed: int): @@ -1213,7 +1141,7 @@ fn grow_df_edges(needed: int): if nc < needed: nc = needed + 128 nb := alloc(nc * ESZ_DFEDGE) - _dyncpy(g_df_edges, g_df_edge_cap * ESZ_DFEDGE, nb) + cell_copy(g_df_edges, g_df_edge_cap * ESZ_DFEDGE, nb) g_df_edges = nb g_df_edge_cap = nc fn grow_df_arrays(needed: int): @@ -1226,7 +1154,7 @@ fn grow_df_arrays(needed: int): nc = needed + 128 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_df_var_producer, g_df_cap * 8, n1) + cell_copy(g_df_var_producer, g_df_cap * 8, n1) // Initialize producer slots to -1. Newly allocated memory is not guaranteed to // be zeroed, and a zero would mean node 0, creating ghost edges 0->X and a // self-loop. The new range is filled explicitly; the copied range keeps its @@ -1235,19 +1163,19 @@ fn grow_df_arrays(needed: int): loop: if zi >= nc: break - w64(n1, zi * 8, -1) + cell_store(n1, zi * 8, -1) zi = zi + 1 g_df_var_producer = n1 n2 := alloc(sz) - _dyncpy(g_df_func_node_start, g_df_cap * 8, n2) + cell_copy(g_df_func_node_start, g_df_cap * 8, n2) g_df_func_node_start = n2 n3 := alloc(sz) - _dyncpy(g_df_func_node_count, g_df_cap * 8, n3) + cell_copy(g_df_func_node_count, g_df_cap * 8, n3) g_df_func_node_count = n3 // Per-function edge starts grow with g_df_cap. df_begin_func overwrites the // slot for each function, and readers only access it afterwards. n4 := alloc(sz) - _dyncpy(g_df_func_edge_start, g_df_cap * 8, n4) + cell_copy(g_df_func_edge_start, g_df_cap * 8, n4) g_df_func_edge_start = n4 g_df_cap = nc @@ -1261,10 +1189,10 @@ fn grow_gen_map(needed: int): nc = needed + 8 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_gen_map_names, g_gen_map_cap*8, n1) + cell_copy(g_gen_map_names, g_gen_map_cap*8, n1) g_gen_map_names = n1 n2 := alloc(sz) - _dyncpy(g_gen_map_types, g_gen_map_cap*8, n2) + cell_copy(g_gen_map_types, g_gen_map_cap*8, n2) g_gen_map_types = n2 g_gen_map_cap = nc fn grow_borrow_vars(needed: int): @@ -1277,13 +1205,13 @@ fn grow_borrow_vars(needed: int): nc = needed + 16 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_borrow_vars, g_borrow_cap*8, n1) + cell_copy(g_borrow_vars, g_borrow_cap*8, n1) g_borrow_vars = n1 n2 := alloc(sz) - _dyncpy(g_borrow_refs, g_borrow_cap*8, n2) + cell_copy(g_borrow_refs, g_borrow_cap*8, n2) g_borrow_refs = n2 n3 := alloc(sz) - _dyncpy(g_borrow_muts, g_borrow_cap*8, n3) + cell_copy(g_borrow_muts, g_borrow_cap*8, n3) g_borrow_muts = n3 g_borrow_cap = nc fn grow_holder(needed: int): @@ -1296,13 +1224,13 @@ fn grow_holder(needed: int): nc = needed + 16 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_holder_borrowers, g_holder_cap*8, n1) + cell_copy(g_holder_borrowers, g_holder_cap*8, n1) g_holder_borrowers = n1 n2 := alloc(sz) - _dyncpy(g_holder_borrowed, g_holder_cap*8, n2) + cell_copy(g_holder_borrowed, g_holder_cap*8, n2) g_holder_borrowed = n2 n3 := alloc(sz) - _dyncpy(g_holder_is_mut, g_holder_cap*8, n3) + cell_copy(g_holder_is_mut, g_holder_cap*8, n3) g_holder_is_mut = n3 g_holder_cap = nc @@ -1315,7 +1243,7 @@ fn grow_global_lets(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_global_lets, g_global_lets_cap * 8, nb) + cell_copy(g_global_lets, g_global_lets_cap * 8, nb) g_global_lets = nb g_global_lets_cap = nc fn grow_loop_stack(needed: int): @@ -1327,7 +1255,7 @@ fn grow_loop_stack(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 24) - _dyncpy(g_loop_stack, g_loop_stack_cap * 24, nb) + cell_copy(g_loop_stack, g_loop_stack_cap * 24, nb) g_loop_stack = nb g_loop_stack_cap = nc fn grow_type_aliases(needed: int): @@ -1339,7 +1267,7 @@ fn grow_type_aliases(needed: int): if nc < needed: nc = needed + 32 nb := alloc(nc * 16) - _dyncpy(g_type_aliases, g_type_alias_cap * 16, nb) + cell_copy(g_type_aliases, g_type_alias_cap * 16, nb) g_type_aliases = nb g_type_alias_cap = nc fn grow_scope_bounds(needed: int): @@ -1351,7 +1279,7 @@ fn grow_scope_bounds(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_scope_bounds, g_scope_bounds_cap * 8, nb) + cell_copy(g_scope_bounds, g_scope_bounds_cap * 8, nb) g_scope_bounds = nb g_scope_bounds_cap = nc fn grow_methods(needed: int): @@ -1363,7 +1291,7 @@ fn grow_methods(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 24) - _dyncpy(g_methods, g_method_cap * 24, nb) + cell_copy(g_methods, g_method_cap * 24, nb) g_methods = nb g_method_cap = nc fn grow_borrow_markers(needed: int): @@ -1375,7 +1303,7 @@ fn grow_borrow_markers(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_borrow_scope_markers, g_borrow_scope_markers_cap * 8, nb) + cell_copy(g_borrow_scope_markers, g_borrow_scope_markers_cap * 8, nb) g_borrow_scope_markers = nb g_borrow_scope_markers_cap = nc @@ -1396,13 +1324,13 @@ fn grow_so_side(needed: int): nc = needed + 32 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_so_side_name, g_so_side_cap*8, n1) + cell_copy(g_so_side_name, g_so_side_cap*8, n1) g_so_side_name = n1 n2 := alloc(sz) - _dyncpy(g_so_side_tags, g_so_side_cap*8, n2) + cell_copy(g_so_side_tags, g_so_side_cap*8, n2) g_so_side_tags = n2 n3 := alloc(sz) - _dyncpy(g_so_side_type, g_so_side_cap*8, n3) + cell_copy(g_so_side_type, g_so_side_cap*8, n3) g_so_side_type = n3 g_so_side_cap = nc @@ -1417,25 +1345,25 @@ fn grow_spec_side(needed: int): nc = needed + 16 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_spec_fn, g_spec_cap*8, n1) + cell_copy(g_spec_fn, g_spec_cap*8, n1) g_spec_fn = n1 n6 := alloc(sz) - _dyncpy(g_spec_fnode, g_spec_cap*8, n6) + cell_copy(g_spec_fnode, g_spec_cap*8, n6) g_spec_fnode = n6 n7 := alloc(sz) - _dyncpy(g_spec_status, g_spec_cap*8, n7) + cell_copy(g_spec_status, g_spec_cap*8, n7) g_spec_status = n7 n2 := alloc(sz) - _dyncpy(g_spec_kind, g_spec_cap*8, n2) + cell_copy(g_spec_kind, g_spec_cap*8, n2) g_spec_kind = n2 n3 := alloc(sz) - _dyncpy(g_spec_expr, g_spec_cap*8, n3) + cell_copy(g_spec_expr, g_spec_cap*8, n3) g_spec_expr = n3 n4 := alloc(sz) - _dyncpy(g_spec_line, g_spec_cap*8, n4) + cell_copy(g_spec_line, g_spec_cap*8, n4) g_spec_line = n4 n5 := alloc(sz) - _dyncpy(g_spec_col, g_spec_cap*8, n5) + cell_copy(g_spec_col, g_spec_cap*8, n5) g_spec_col = n5 g_spec_cap = nc @@ -1443,56 +1371,56 @@ fn grow_spec_side(needed: int): fn spec_add(fn_ni: int, kind: int, expr: int, line: int, col: int): grow_spec_side(g_spec_count + 1) i := g_spec_count - w64(g_spec_fn, i * 8, fn_ni) - w64(g_spec_fnode, i * 8, -1) // 由 parser 在 EXPR_FN 分配后回填(spec_set_fnode) - w64(g_spec_status, i * 8, SPEC_ST_YELLOW) - w64(g_spec_kind, i * 8, kind) - w64(g_spec_expr, i * 8, expr) - w64(g_spec_line, i * 8, line) - w64(g_spec_col, i * 8, col) + cell_store(g_spec_fn, i * 8, fn_ni) + cell_store(g_spec_fnode, i * 8, -1) // 由 parser 在 EXPR_FN 分配后回填(spec_set_fnode) + cell_store(g_spec_status, i * 8, SPEC_ST_YELLOW) + cell_store(g_spec_kind, i * 8, kind) + cell_store(g_spec_expr, i * 8, expr) + cell_store(g_spec_line, i * 8, line) + cell_store(g_spec_col, i * 8, col) g_spec_count = i + 1 // Guarded read accessors: out-of-range indexes return the sentinel and never read a neighboring record. fn spec_fn(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_fn, i * 8) + return cell_load(g_spec_fn, i * 8) fn spec_fnode(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_fnode, i * 8) + return cell_load(g_spec_fnode, i * 8) fn spec_status(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_status, i * 8) + return cell_load(g_spec_status, i * 8) // Status write: red is sticky. A later green must not erase a hard error. fn spec_set_status(i: int, st: int): if i < 0 || i >= g_spec_count: return - cur := r64(g_spec_status, i * 8) + cur := cell_load(g_spec_status, i * 8) if cur == SPEC_ST_RED: return - w64(g_spec_status, i * 8, st) + cell_store(g_spec_status, i * 8, st) fn spec_set_fnode(i: int, v: int): if i < 0 || i >= g_spec_count: return - w64(g_spec_fnode, i * 8, v) + cell_store(g_spec_fnode, i * 8, v) fn spec_kind(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_kind, i * 8) + return cell_load(g_spec_kind, i * 8) fn spec_expr(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_expr, i * 8) + return cell_load(g_spec_expr, i * 8) fn spec_line(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_line, i * 8) + return cell_load(g_spec_line, i * 8) fn spec_col(i: int) -> int: if i < 0 || i >= g_spec_count: return -1 - return r64(g_spec_col, i * 8) + return cell_load(g_spec_col, i * 8) fn grow_pending(needed: int): if needed < g_pending_cap: @@ -1503,10 +1431,10 @@ fn grow_pending(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_pending_pos, g_pending_cap * 8, nb) + cell_copy(g_pending_pos, g_pending_cap * 8, nb) g_pending_pos = nb nb2 := alloc(nc * 8) - _dyncpy(g_pending_label, g_pending_cap * 8, nb2) + cell_copy(g_pending_label, g_pending_cap * 8, nb2) g_pending_label = nb2 g_pending_cap = nc fn grow_opt_meta(needed: int): @@ -1518,7 +1446,7 @@ fn grow_opt_meta(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * OPT_META_STRIDE) - _dyncpy(g_opt_meta, g_opt_meta_count * OPT_META_STRIDE, nb) + cell_copy(g_opt_meta, g_opt_meta_count * OPT_META_STRIDE, nb) g_opt_meta = nb g_opt_meta_cap = nc fn grow_ir_local_scopes(needed: int): @@ -1530,7 +1458,7 @@ fn grow_ir_local_scopes(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_ir_local_scopes, g_ir_local_scopes_cap * 8, nb) + cell_copy(g_ir_local_scopes, g_ir_local_scopes_cap * 8, nb) g_ir_local_scopes = nb g_ir_local_scopes_cap = nc @@ -1543,10 +1471,10 @@ fn grow_ir_loop_stacks(needed: int): if nc < needed: nc = needed + 64 n1 := alloc(nc * 8) - _dyncpy(g_ir_loop_header, g_ir_loop_stacks_cap * 8, n1) + cell_copy(g_ir_loop_header, g_ir_loop_stacks_cap * 8, n1) g_ir_loop_header = n1 n2 := alloc(nc * 8) - _dyncpy(g_ir_loop_exit, g_ir_loop_stacks_cap * 8, n2) + cell_copy(g_ir_loop_exit, g_ir_loop_stacks_cap * 8, n2) g_ir_loop_exit = n2 g_ir_loop_stacks_cap = nc @@ -1559,7 +1487,7 @@ fn grow_label_poses(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_label_poses, g_label_cap * 8, nb) + cell_copy(g_label_poses, g_label_cap * 8, nb) g_label_poses = nb g_label_cap = nc @@ -1577,7 +1505,7 @@ fn grow_diags(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * DIAG_REC_SIZE) - _dyncpy(g_diags, g_diag_cap * DIAG_REC_SIZE, nb) + cell_copy(g_diags, g_diag_cap * DIAG_REC_SIZE, nb) g_diags = nb g_diag_cap = nc @@ -1591,7 +1519,7 @@ fn grow_files(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 16) - _dyncpy(g_files, g_file_cap * 16, nb) + cell_copy(g_files, g_file_cap * 16, nb) g_files = nb g_file_cap = nc @@ -1605,7 +1533,7 @@ fn grow_mods(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 24) - _dyncpy(g_mods, g_mod_cap * 24, nb) + cell_copy(g_mods, g_mod_cap * 24, nb) g_mods = nb g_mod_cap = nc @@ -1620,13 +1548,13 @@ fn grow_mod_funcs(needed: int): nc = needed + 64 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_mod_func_fileids, g_mod_func_cap * 8, n1) + cell_copy(g_mod_func_fileids, g_mod_func_cap * 8, n1) g_mod_func_fileids = n1 n2 := alloc(sz) - _dyncpy(g_mod_func_names, g_mod_func_cap * 8, n2) + cell_copy(g_mod_func_names, g_mod_func_cap * 8, n2) g_mod_func_names = n2 n3 := alloc(sz) - _dyncpy(g_mod_func_tis, g_mod_func_cap * 8, n3) + cell_copy(g_mod_func_tis, g_mod_func_cap * 8, n3) g_mod_func_tis = n3 g_mod_func_cap = nc @@ -1640,7 +1568,7 @@ fn grow_mod_paths(needed: int): if nc < needed: nc = needed + 32 nb := alloc(nc * 8) - _dyncpy(g_mod_path_names, g_mod_path_cap * 8, nb) + cell_copy(g_mod_path_names, g_mod_path_cap * 8, nb) g_mod_path_names = nb g_mod_path_cap = nc @@ -1654,7 +1582,7 @@ fn grow_impl_for(needed: int): if nc < needed: nc = needed + 8 nb := alloc(nc * 16) - _dyncpy(g_impl_for, g_impl_for_cap * 16, nb) + cell_copy(g_impl_for, g_impl_for_cap * 16, nb) g_impl_for = nb g_impl_for_cap = nc @@ -1668,7 +1596,7 @@ fn grow_gen_params(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_gen_params, g_gen_param_cap * 8, nb) + cell_copy(g_gen_params, g_gen_param_cap * 8, nb) g_gen_params = nb g_gen_param_cap = nc @@ -1682,7 +1610,7 @@ fn grow_gen_constr(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_generic_constr, g_generic_constr_cap * 8, nb) + cell_copy(g_generic_constr, g_generic_constr_cap * 8, nb) g_generic_constr = nb g_generic_constr_cap = nc @@ -1697,7 +1625,7 @@ fn grow_sgen_constr(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_sgen_constr, g_sgen_constr_cap * 8, nb) + cell_copy(g_sgen_constr, g_sgen_constr_cap * 8, nb) g_sgen_constr = nb g_sgen_constr_cap = nc @@ -1710,7 +1638,7 @@ fn grow_egen_constr(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_egen_constr, g_egen_constr_cap * 8, nb) + cell_copy(g_egen_constr, g_egen_constr_cap * 8, nb) g_egen_constr = nb g_egen_constr_cap = nc @@ -1724,7 +1652,7 @@ fn grow_gen_binds(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_gen_binds, g_gen_binds_cap * 8, nb) + cell_copy(g_gen_binds, g_gen_binds_cap * 8, nb) g_gen_binds = nb g_gen_binds_cap = nc @@ -1737,7 +1665,7 @@ fn si_gen_constr(n: int, gi: int) -> int: idx := n * MAX_GENERICS + gi if idx >= g_sgen_constr_count: return -1 - return r64(g_sgen_constr, idx * 8) + return cell_load(g_sgen_constr, idx * 8) fn ei_gen_constr(n: int, gi: int) -> int: if n < 0 || gi < 0 || gi >= MAX_GENERICS: @@ -1745,7 +1673,7 @@ fn ei_gen_constr(n: int, gi: int) -> int: idx := n * MAX_GENERICS + gi if idx >= g_egen_constr_count: return -1 - return r64(g_egen_constr, idx * 8) + return cell_load(g_egen_constr, idx * 8) fn grow_sg(n: int): if n < g_sg_cap: @@ -1759,7 +1687,7 @@ fn grow_sg(n: int): nc = nc * 2 nb := alloc(nc * ESZ_SG) if g_sg_cap > 0: - _dyncpy(g_sgs, g_sg_cap * ESZ_SG, nb) + cell_copy(g_sgs, g_sg_cap * ESZ_SG, nb) g_sgs = nb g_sg_cap = nc @@ -1775,7 +1703,7 @@ fn grow_pts(n: int): nc = nc * 2 nb := alloc(nc * 8) if g_pts_cap > 0: - _dyncpy(g_pts, g_pts_cap * 8, nb) + cell_copy(g_pts, g_pts_cap * 8, nb) g_pts = nb g_pts_cap = nc @@ -1791,6 +1719,6 @@ fn grow_offsets(n: int): nc = nc * 2 nb := alloc(nc * 8) if g_offsets_cap > 0: - _dyncpy(g_offsets, g_offsets_cap * 8, nb) + cell_copy(g_offsets, g_offsets_cap * 8, nb) g_offsets = nb g_offsets_cap = nc diff --git a/src/compiler/ext_mgr.cr b/src/compiler/ext_mgr.cr index 612271b4..f0adc48b 100644 --- a/src/compiler/ext_mgr.cr +++ b/src/compiler/ext_mgr.cr @@ -28,7 +28,7 @@ fn grow_ext_reg(needed: int): // Each record is 16 bytes: hook_type at offset 0 and plugin_id at offset 8. // An 8-byte stride would overlap the two fields and break dispatch. nb := alloc(ncap * 16) - _dyncpy(g_ext_reg, g_ext_reg_cap * 16, nb) + cell_copy(g_ext_reg, g_ext_reg_cap * 16, nb) g_ext_reg = nb g_ext_reg_cap = ncap @@ -56,8 +56,8 @@ fn ext_has(flag: int) -> int: // Register a plugin for a hook. fn ext_reg(hook_type: int, plugin_id: int): grow_ext_reg(g_ext_reg_count + 1) - w64(g_ext_reg, g_ext_reg_count * 16, hook_type) - w64(g_ext_reg, g_ext_reg_count * 16 + 8, plugin_id) + cell_store(g_ext_reg, g_ext_reg_count * 16, hook_type) + cell_store(g_ext_reg, g_ext_reg_count * 16 + 8, plugin_id) g_ext_reg_count = g_ext_reg_count + 1 // Dispatch an array access hook. arr_var, idx_var, idx_lit, and arr_len_lit are @@ -69,8 +69,8 @@ fn ext_dispatch_array_access(arr_var: int, idx_var: int, idx_lit: int, arr_len_l loop: if ri >= g_ext_reg_count: break - ht := r64(g_ext_reg, ri * 16) - pid := r64(g_ext_reg, ri * 16 + 8) + ht := cell_load(g_ext_reg, ri * 16) + pid := cell_load(g_ext_reg, ri * 16 + 8) if ht == EXT_HOOK_ARRAY_ACCESS: if pid == 1: // EXT_PLUGIN_SAFETY @@ -88,8 +88,8 @@ fn ext_dispatch_binary_op(op: int, lv: int, rv: int, result_var: int) -> int: loop: if ri >= g_ext_reg_count: break - ht := r64(g_ext_reg, ri * 16) - pid := r64(g_ext_reg, ri * 16 + 8) + ht := cell_load(g_ext_reg, ri * 16) + pid := cell_load(g_ext_reg, ri * 16 + 8) if ht == EXT_HOOK_BINARY_OP: if pid == 1: return ext_safety_on_binary_op(op, lv, rv, result_var) diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 06dfc124..ef0cbe07 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -633,7 +633,7 @@ fn grow_plugin_tags(needed: int): if ncap < needed: ncap = needed + 8 nb := alloc(ncap * 24) - _dyncpy(g_plugin_tags, g_plugin_tag_cap * 24, nb) + cell_copy(g_plugin_tags, g_plugin_tag_cap * 24, nb) g_plugin_tags = nb g_plugin_tag_cap = ncap @@ -646,7 +646,7 @@ fn grow_plugin_rtypes(needed: int): if ncap < needed: ncap = needed + 8 nb := alloc(ncap * 24) - _dyncpy(g_plugin_rtypes, g_plugin_rtype_cap * 24, nb) + cell_copy(g_plugin_rtypes, g_plugin_rtype_cap * 24, nb) g_plugin_rtypes = nb g_plugin_rtype_cap = ncap @@ -658,9 +658,9 @@ fn find_plugin_entry(table: string, count: int, name_ni: int, ns_ni: int) -> int loop: if i >= count: break - if r64(table, i*24+8) == name_ni && r64(table, i*24) == ns_ni: + if cell_load(table, i*24+8) == name_ni && cell_load(table, i*24) == ns_ni: return i - if r64(table, i*24+8) == name_ni && ns_ni < 0 && r64(table, i*24) < 0: + if cell_load(table, i*24+8) == name_ni && ns_ni < 0 && cell_load(table, i*24) < 0: return i i = i + 1 return -1 @@ -673,9 +673,9 @@ fn register_plugin_tag(ns_ni: int, name_ni: int, data: int) -> int: if ns_ni >= 0 && find_plugin_entry(g_plugin_tags, g_plugin_tag_count, name_ni, -1) >= 0: return -3 grow_plugin_tags(g_plugin_tag_count + 1) - w64(g_plugin_tags, g_plugin_tag_count*24, ns_ni) - w64(g_plugin_tags, g_plugin_tag_count*24+8, name_ni) - w64(g_plugin_tags, g_plugin_tag_count*24+16, data) + cell_store(g_plugin_tags, g_plugin_tag_count*24, ns_ni) + cell_store(g_plugin_tags, g_plugin_tag_count*24+8, name_ni) + cell_store(g_plugin_tags, g_plugin_tag_count*24+16, data) g_plugin_tag_count = g_plugin_tag_count + 1 return 0 @@ -685,9 +685,9 @@ fn register_plugin_rtype(ns_ni: int, name_ni: int, data: int) -> int: if ns_ni >= 0 && find_plugin_entry(g_plugin_rtypes, g_plugin_rtype_count, name_ni, -1) >= 0: return -3 grow_plugin_rtypes(g_plugin_rtype_count + 1) - w64(g_plugin_rtypes, g_plugin_rtype_count*24, ns_ni) - w64(g_plugin_rtypes, g_plugin_rtype_count*24+8, name_ni) - w64(g_plugin_rtypes, g_plugin_rtype_count*24+16, data) + cell_store(g_plugin_rtypes, g_plugin_rtype_count*24, ns_ni) + cell_store(g_plugin_rtypes, g_plugin_rtype_count*24+8, name_ni) + cell_store(g_plugin_rtypes, g_plugin_rtype_count*24+16, data) g_plugin_rtype_count = g_plugin_rtype_count + 1 return 0 diff --git a/src/compiler/iface_axis.cr b/src/compiler/iface_axis.cr index e7b4526f..85a296f3 100644 --- a/src/compiler/iface_axis.cr +++ b/src/compiler/iface_axis.cr @@ -107,9 +107,9 @@ fn iface_find_method(type_ni: int, method_ni: int) -> int: loop: if i >= g_method_count: return -1 - if r64(g_methods, i * 24) == type_ni: - if r64(g_methods, i * 24 + 8) == method_ni: - return r64(g_methods, i * 24 + 16) + if cell_load(g_methods, i * 24) == type_ni: + if cell_load(g_methods, i * 24 + 8) == method_ni: + return cell_load(g_methods, i * 24 + 16) i = i + 1 return -1 @@ -145,10 +145,10 @@ fn iface_user_satisfies(t_ti: int, iface_ni: int) -> int: fn iface_user_satisfies_ii(tname_ni: int, ii: int) -> int: if tname_ni < 0 || ii < 0: return -1 - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) if mc < 0 || mc > MAX_IFACE_METHODS: return -1 - shp := sh_iface_shape_term(r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_NAME)) + shp := sh_iface_shape_term(cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_NAME)) if shp < 0: return -1 mi : ., mut = 0 @@ -156,7 +156,7 @@ fn iface_user_satisfies_ii(tname_ni: int, ii: int) -> int: if mi >= mc: return 1 mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - m_ni := r64(g_ifaces, mbase + OFF_IFM_NAME) + m_ni := cell_load(g_ifaces, mbase + OFF_IFM_NAME) f_ni := iface_find_method(tname_ni, m_ni) if f_ni < 0: return 0 diff --git a/src/compiler/iface_registry.cr b/src/compiler/iface_registry.cr index 5ddcc2b3..f7f72ce1 100644 --- a/src/compiler/iface_registry.cr +++ b/src/compiler/iface_registry.cr @@ -102,11 +102,11 @@ fn iface_registry_init(): // Write one entry row. All table writes go through this function. fn iface_put(e: int, ak: int, ti: int, name_ni: int, lit_code: int, ops: int): - w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK, ak) - w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI, ti) - w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME, name_ni) - w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_LIT, lit_code) - w64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS, ops) + cell_store(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK, ak) + cell_store(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI, ti) + cell_store(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME, name_ni) + cell_store(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_LIT, lit_code) + cell_store(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS, ops) // Ensure the table exists. init_types builds it, but a query may run earlier in a // self-test or future call site; reading an empty table would walk out of bounds. @@ -125,7 +125,7 @@ fn iface_entry(ak: int) -> int: loop: if i >= g_iface_entry_count: break - if r64(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_AK) == ak: + if cell_load(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_AK) == ak: return i i = i + 1 return -1 @@ -135,7 +135,7 @@ fn iface_ops(ak: int) -> int: e := iface_entry(ak) if e < 0: return 0 - return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS) + return cell_load(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_OPS) // Bit test. op already includes the UOP or IP bias. Out-of-range indices return 0; // they must not wrap, because the multiply-by-two construction would make a @@ -170,7 +170,7 @@ fn iface_lit_entry(lit_kind: int) -> int: loop: if i >= g_iface_entry_count: break - if r64(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_LIT) == lit_kind: + if cell_load(g_iface_entries, i * ESZ_IFACE_ENTRY + OFF_IE_LIT) == lit_kind: return i i = i + 1 return -1 @@ -181,14 +181,14 @@ fn iface_lit_ti(lit_kind: int) -> int: e := iface_lit_entry(lit_kind) if e < 0: return -1 - return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI) + return cell_load(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI) // 字面量 AST kind -> AK_*(同上;交叉断言/引擎侧用) fn iface_lit_ak(lit_kind: int) -> int: e := iface_lit_entry(lit_kind) if e < 0: return -1 - return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK) + return cell_load(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_AK) // TY_* base type code to AK_* atom class. This is the single implementation. // TY_DEX_S maps to AK_DEX: the representation difference is not part of type @@ -247,7 +247,7 @@ fn iface_ti_of(ak: int) -> int: e := iface_entry(ak) if e < 0: return -1 - return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI) + return cell_load(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_TI) // Atom class to registered name intern index (-1 if absent). This is a read-only // query; it never calls str_intern, so the judgment and query paths add no new @@ -256,7 +256,7 @@ fn iface_name_of(ak: int) -> int: e := iface_entry(ak) if e < 0: return -1 - return r64(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME) + return cell_load(g_iface_entries, e * ESZ_IFACE_ENTRY + OFF_IE_NAME) // Cross-cutting shape entries for iface_satisfies axis A. // Each shape entry maps an interned name to a type term. Satisfaction is the @@ -279,13 +279,13 @@ fn iface_shape_register(name_ni: int, term: int) -> int: loop: if i >= g_iface_shape_count: break - if r64(g_iface_shape_names, i * 8) == name_ni: - w64(g_iface_shape_terms, i * 8, term) // 覆盖(幂等重注册) + if cell_load(g_iface_shape_names, i * 8) == name_ni: + cell_store(g_iface_shape_terms, i * 8, term) // 覆盖(幂等重注册) return i i = i + 1 iface_shape_grow(g_iface_shape_count + 1) - w64(g_iface_shape_names, g_iface_shape_count * 8, name_ni) - w64(g_iface_shape_terms, g_iface_shape_count * 8, term) + cell_store(g_iface_shape_names, g_iface_shape_count * 8, name_ni) + cell_store(g_iface_shape_terms, g_iface_shape_count * 8, term) g_iface_shape_count = g_iface_shape_count + 1 return g_iface_shape_count - 1 @@ -297,8 +297,8 @@ fn iface_shape_lookup(name_ni: int) -> int: loop: if i >= g_iface_shape_count: return -1 - if r64(g_iface_shape_names, i * 8) == name_ni: - return r64(g_iface_shape_terms, i * 8) + if cell_load(g_iface_shape_names, i * 8) == name_ni: + return cell_load(g_iface_shape_terms, i * 8) i = i + 1 return -1 diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index dc7a20ce..3055b13c 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -54,40 +54,40 @@ g_interp_stack : string, mut fn ir_interp_binary_raw(d: int, s1: int, s2: int, s3: int): if d < 0: return - lv := r64(g_ir_vals, s1 * 8) - rv := r64(g_ir_vals, s2 * 8) + lv := cell_load(g_ir_vals, s1 * 8) + rv := cell_load(g_ir_vals, s2 * 8) if s3 == 1: - w64(g_ir_vals, d * 8, lv + rv) + cell_store(g_ir_vals, d * 8, lv + rv) else if s3 == 2: - w64(g_ir_vals, d * 8, lv - rv) + cell_store(g_ir_vals, d * 8, lv - rv) else if s3 == 3: - w64(g_ir_vals, d * 8, lv * rv) + cell_store(g_ir_vals, d * 8, lv * rv) else if s3 == 4: - w64(g_ir_vals, d * 8, lv / rv) + cell_store(g_ir_vals, d * 8, lv / rv) else if s3 == 5: - w64(g_ir_vals, d * 8, lv % rv) + cell_store(g_ir_vals, d * 8, lv % rv) else if s3 == 6: - w64(g_ir_vals, d * 8, lv == rv) + cell_store(g_ir_vals, d * 8, lv == rv) else if s3 == 7: - w64(g_ir_vals, d * 8, lv != rv) + cell_store(g_ir_vals, d * 8, lv != rv) else if s3 == 8: - w64(g_ir_vals, d * 8, lv < rv) + cell_store(g_ir_vals, d * 8, lv < rv) else if s3 == 9: - w64(g_ir_vals, d * 8, lv > rv) + cell_store(g_ir_vals, d * 8, lv > rv) else if s3 == 10: - w64(g_ir_vals, d * 8, lv <= rv) + cell_store(g_ir_vals, d * 8, lv <= rv) else if s3 == 11: - w64(g_ir_vals, d * 8, lv >= rv) + cell_store(g_ir_vals, d * 8, lv >= rv) else if s3 == 12: - w64(g_ir_vals, d * 8, lv != 0 && rv != 0) + cell_store(g_ir_vals, d * 8, lv != 0 && rv != 0) else if s3 == 13: - w64(g_ir_vals, d * 8, lv != 0 || rv != 0) + cell_store(g_ir_vals, d * 8, lv != 0 || rv != 0) else if s3 == 17: - w64(g_ir_vals, d * 8, lv + rv * 8) + cell_store(g_ir_vals, d * 8, lv + mp_offset(rv, TI_INT)) else if s3 == 18: - w64(g_ir_vals, d * 8, lv - rv * 8) + cell_store(g_ir_vals, d * 8, lv - mp_offset(rv, TI_INT)) else if s3 == 19: - w64(g_ir_vals, d * 8, (lv - rv) / 8) + cell_store(g_ir_vals, d * 8, (lv - rv) / mp_stride(TI_INT)) // Shared IR_BINARY dispatch for the main loop and callee loops. // g_ir_vals int slots hold tagged mathematical integers. Char/bool/dex and @@ -100,24 +100,24 @@ fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): return if d < 0: return - lv := r64(g_ir_vals, s1 * 8) - rv := r64(g_ir_vals, s2 * 8) + lv := cell_load(g_ir_vals, s1 * 8) + rv := cell_load(g_ir_vals, s2 * 8) if s3 == 17 || s3 == 18 || s3 == 19: // Pointer add/sub/diff: the pointer is raw; the offset is mathematical. off := ir_interp_word(rv) if s3 == 17: - w64(g_ir_vals, d * 8, lv + off * 8) + cell_store(g_ir_vals, d * 8, lv + mp_offset(off, TI_INT)) else if s3 == 18: - w64(g_ir_vals, d * 8, lv - off * 8) + cell_store(g_ir_vals, d * 8, lv - mp_offset(off, TI_INT)) else: - w64(g_ir_vals, d * 8, ir_interp_tag((lv - rv) / 8)) + cell_store(g_ir_vals, d * 8, ir_interp_tag((lv - rv) / mp_stride(TI_INT))) return if s3 == 12 || s3 == 13: // Boolean and/or store raw 0/1. if s3 == 12: - w64(g_ir_vals, d * 8, lv != 0 && rv != 0) + cell_store(g_ir_vals, d * 8, lv != 0 && rv != 0) else: - w64(g_ir_vals, d * 8, lv != 0 || rv != 0) + cell_store(g_ir_vals, d * 8, lv != 0 || rv != 0) return lhs_char := irv_type(s1) == TI_CHAR @@ -131,58 +131,58 @@ fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): if !rhs_char && s2 >= 0: r = ir_interp_word(rv) if s3 == 6: - w64(g_ir_vals, d * 8, l == r) + cell_store(g_ir_vals, d * 8, l == r) else if s3 == 7: - w64(g_ir_vals, d * 8, l != r) + cell_store(g_ir_vals, d * 8, l != r) else if s3 == 8: - w64(g_ir_vals, d * 8, l < r) + cell_store(g_ir_vals, d * 8, l < r) else if s3 == 9: - w64(g_ir_vals, d * 8, l > r) + cell_store(g_ir_vals, d * 8, l > r) else if s3 == 10: - w64(g_ir_vals, d * 8, l <= r) + cell_store(g_ir_vals, d * 8, l <= r) else if s3 == 11: - w64(g_ir_vals, d * 8, l >= r) + cell_store(g_ir_vals, d * 8, l >= r) return if ti == TI_DEX || ti == TI_DEX_S: // Dex slots are scaled machine integers (or binary64 bits, rejected at // dispatch); keep their existing raw arithmetic. if s3 == 1: - w64(g_ir_vals, d * 8, lv + rv) + cell_store(g_ir_vals, d * 8, lv + rv) else if s3 == 2: - w64(g_ir_vals, d * 8, lv - rv) + cell_store(g_ir_vals, d * 8, lv - rv) else if s3 == 3: - w64(g_ir_vals, d * 8, lv * rv) + cell_store(g_ir_vals, d * 8, lv * rv) else if s3 == 4: - w64(g_ir_vals, d * 8, lv / rv) + cell_store(g_ir_vals, d * 8, lv / rv) else if s3 == 5: - w64(g_ir_vals, d * 8, lv % rv) + cell_store(g_ir_vals, d * 8, lv % rv) return if s3 == 1: - w64(g_ir_vals, d * 8, bi_add(lv, rv)) + cell_store(g_ir_vals, d * 8, bi_add(lv, rv)) else if s3 == 2: - w64(g_ir_vals, d * 8, bi_sub(lv, rv)) + cell_store(g_ir_vals, d * 8, bi_sub(lv, rv)) else if s3 == 3: - w64(g_ir_vals, d * 8, bi_mul(lv, rv)) + cell_store(g_ir_vals, d * 8, bi_mul(lv, rv)) else if s3 == 4: - w64(g_ir_vals, d * 8, bi_div(lv, rv)) + cell_store(g_ir_vals, d * 8, bi_div(lv, rv)) else if s3 == 5: - w64(g_ir_vals, d * 8, bi_mod(lv, rv)) + cell_store(g_ir_vals, d * 8, bi_mod(lv, rv)) else if s3 >= 6 && s3 <= 11: c := bi_cmp(lv, rv) if s3 == 6: - w64(g_ir_vals, d * 8, c == 0) + cell_store(g_ir_vals, d * 8, c == 0) else if s3 == 7: - w64(g_ir_vals, d * 8, c != 0) + cell_store(g_ir_vals, d * 8, c != 0) else if s3 == 8: - w64(g_ir_vals, d * 8, c < 0) + cell_store(g_ir_vals, d * 8, c < 0) else if s3 == 9: - w64(g_ir_vals, d * 8, c > 0) + cell_store(g_ir_vals, d * 8, c > 0) else if s3 == 10: - w64(g_ir_vals, d * 8, c <= 0) + cell_store(g_ir_vals, d * 8, c <= 0) else: - w64(g_ir_vals, d * 8, c >= 0) + cell_store(g_ir_vals, d * 8, c >= 0) // Pointer read and write. // A pointer in [0, g_ir_var_count) is a stack slot address produced by REF; read @@ -190,15 +190,15 @@ fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): // ALLOC_ARRAY; read or write memory. fn ir_interp_deref_read(ptr: int) -> int: if ptr >= 0 && ptr < g_ir_var_count: - return r64(g_ir_vals, ptr * 8) + return cell_load(g_ir_vals, ptr * 8) unsafe: - return r64(@str_of(ptr), 0) + return cell_load(@str_of(ptr), 0) fn ir_interp_deref_write(ptr: int, val: int): if ptr >= 0 && ptr < g_ir_var_count: - w64(g_ir_vals, ptr * 8, val) + cell_store(g_ir_vals, ptr * 8, val) else: unsafe: - w64(@str_of(ptr), 0, val) + cell_store(@str_of(ptr), 0, val) // Interpreter string values are intern-table indices (IR_CONST stores the // index, while native ELF stores a pointer). Keep string builtins in that same @@ -231,68 +231,68 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): // syscall3 and syscall4 return 0 in the interpreter. if str_eq(fn_name, "syscall3") != 0 || str_eq(fn_name, "syscall4") != 0: if d >= 0: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) if str_eq(fn_name, "str_len") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, ir_interp_tag(istr_len(r64(g_ir_vals, s1 * 8)))) + cell_store(g_ir_vals, d * 8, ir_interp_tag(istr_len(cell_load(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "str_eq") != 0: if d >= 0 && s2 >= 2: - left_s := istr_get(r64(g_ir_vals, s1 * 8)) - right_s := istr_get(r64(g_ir_vals, (s1 + 1) * 8)) - w64(g_ir_vals, d * 8, str_eq(left_s, right_s)) + left_s := istr_get(cell_load(g_ir_vals, s1 * 8)) + right_s := istr_get(cell_load(g_ir_vals, (s1 + 1) * 8)) + cell_store(g_ir_vals, d * 8, str_eq(left_s, right_s)) if str_eq(fn_name, "concat") != 0: if d >= 0 && s2 >= 2: - w64(g_ir_vals, d * 8, ir_interp_str_concat( - r64(g_ir_vals, s1 * 8), r64(g_ir_vals, (s1 + 1) * 8))) + cell_store(g_ir_vals, d * 8, ir_interp_str_concat( + cell_load(g_ir_vals, s1 * 8), cell_load(g_ir_vals, (s1 + 1) * 8))) if str_eq(fn_name, "int_str") != 0: if d >= 0 && s2 >= 1: if g_interp_big != 0: - w64(g_ir_vals, d * 8, str_intern(bi_to_dec(r64(g_ir_vals, s1 * 8)))) + cell_store(g_ir_vals, d * 8, str_intern(bi_to_dec(cell_load(g_ir_vals, s1 * 8)))) else: - w64(g_ir_vals, d * 8, str_intern(int_str(r64(g_ir_vals, s1 * 8)))) + cell_store(g_ir_vals, d * 8, str_intern(int_str(cell_load(g_ir_vals, s1 * 8)))) if str_eq(fn_name, "chr") != 0: if d >= 0 && s2 >= 1: - w64(g_ir_vals, d * 8, str_intern(chr(ir_interp_word(r64(g_ir_vals, s1 * 8))))) + cell_store(g_ir_vals, d * 8, str_intern(chr(ir_interp_word(cell_load(g_ir_vals, s1 * 8))))) if str_eq(fn_name, "get_char") != 0: if d >= 0 && s2 >= 2: - w64(g_ir_vals, d * 8, ir_interp_str_char( - r64(g_ir_vals, s1 * 8), - ir_interp_word(r64(g_ir_vals, (s1 + 1) * 8)))) + cell_store(g_ir_vals, d * 8, ir_interp_str_char( + cell_load(g_ir_vals, s1 * 8), + ir_interp_word(cell_load(g_ir_vals, (s1 + 1) * 8)))) if str_eq(fn_name, "str_sub") != 0: if d >= 0 && s2 >= 3: - w64(g_ir_vals, d * 8, ir_interp_str_sub( - r64(g_ir_vals, s1 * 8), - ir_interp_word(r64(g_ir_vals, (s1 + 1) * 8)), - ir_interp_word(r64(g_ir_vals, (s1 + 2) * 8)))) - if str_eq(fn_name, "load_str_ptr") != 0: + cell_store(g_ir_vals, d * 8, ir_interp_str_sub( + cell_load(g_ir_vals, s1 * 8), + ir_interp_word(cell_load(g_ir_vals, (s1 + 1) * 8)), + ir_interp_word(cell_load(g_ir_vals, (s1 + 2) * 8)))) + if x86_frontend_builtin_is_str_ptr_load(fn_name) != 0: if d >= 0 && s2 >= 2: - b := r64(g_ir_vals, s1 * 8) - p := ir_interp_word(r64(g_ir_vals, s1 + 1 * 8)) - lo := load8(b, p) + load8(b, p+1)*256 + load8(b, p+2)*65536 + load8(b, p+3)*16777216 - hi := load8(b, p+4) + load8(b, p+5)*256 + load8(b, p+6)*65536 + load8(b, p+7)*16777216 + b := cell_load(g_ir_vals, s1 * 8) + p := ir_interp_word(cell_load(g_ir_vals, s1 + 1 * 8)) + lo := cell_load_byte(b, p) + cell_load_byte(b, p+1)*256 + cell_load_byte(b, p+2)*65536 + cell_load_byte(b, p+3)*16777216 + hi := cell_load_byte(b, p+4) + cell_load_byte(b, p+5)*256 + cell_load_byte(b, p+6)*65536 + cell_load_byte(b, p+7)*16777216 if hi < 0: hi = hi + 4294967296 - w64(g_ir_vals, d * 8, ir_interp_tag(lo + hi * 4294967296)) - if str_eq(fn_name, "store_str_ptr") != 0: + cell_store(g_ir_vals, d * 8, ir_interp_tag(lo + hi * 4294967296)) + if x86_frontend_builtin_is_str_ptr_store(fn_name) != 0: if s2 >= 3: - b := r64(g_ir_vals, s1 * 8) - p := ir_interp_word(r64(g_ir_vals, s1 + 1 * 8)) - v := ir_interp_word(r64(g_ir_vals, s1 + 2 * 8)) + b := cell_load(g_ir_vals, s1 * 8) + p := ir_interp_word(cell_load(g_ir_vals, s1 + 1 * 8)) + v := ir_interp_word(cell_load(g_ir_vals, s1 + 2 * 8)) lo : ., mut = v % 4294967296 hi : ., mut = v / 4294967296 if v < 0: lo = v hi = -1 - store8(b, p, lo%256) - store8(b, p+1, (lo/256)%256) - store8(b, p+2, (lo/65536)%256) - store8(b, p+3, (lo/16777216)%256) - store8(b, p+4, hi%256) - store8(b, p+5, (hi/256)%256) - store8(b, p+6, (hi/65536)%256) - store8(b, p+7, (hi/16777216)%256) + cell_store_byte(b, p, lo%256) + cell_store_byte(b, p+1, (lo/256)%256) + cell_store_byte(b, p+2, (lo/65536)%256) + cell_store_byte(b, p+3, (lo/16777216)%256) + cell_store_byte(b, p+4, hi%256) + cell_store_byte(b, p+5, (hi/256)%256) + cell_store_byte(b, p+6, (hi/65536)%256) + cell_store_byte(b, p+7, (hi/16777216)%256) if d >= 0: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) // String builtins are handled; do not fall through to the user-function lookup. if str_eq(fn_name, "str_len") != 0 || str_eq(fn_name, "str_eq") != 0 || str_eq(fn_name, "concat") != 0 || str_eq(fn_name, "int_str") != 0 || str_eq(fn_name, "chr") != 0 || str_eq(fn_name, "get_char") != 0 || str_eq(fn_name, "str_sub") != 0: return @@ -303,13 +303,13 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): loop: if cfi >= g_ir_func_count: break - if r64(g_ir_func_name_idx, cfi * 8) == fn_ni: - f_start := r64(g_df_func_node_start, cfi * 8) - f_count := r64(g_df_func_node_count, cfi * 8) + if cell_load(g_ir_func_name_idx, cfi * 8) == fn_ni: + f_start := cell_load(g_df_func_node_start, cfi * 8) + f_count := cell_load(g_df_func_node_count, cfi * 8) if f_start >= 0 && f_count > 0: rval := ir_interp_run_fn(cfi, s1, s2) if g_interp_abort == 0: - w64(g_ir_vals, d * 8, rval) + cell_store(g_ir_vals, d * 8, rval) break cfi = cfi + 1 @@ -319,8 +319,8 @@ fn ir_interp_call(d: int, s1: int, s2: int, s3: int): // with no return value returns 0. // Depth check: exceeding IR_INTERP_MAX_DEPTH reports an error and aborts. fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: - f_start := r64(g_df_func_node_start, cfi * 8) - f_count := r64(g_df_func_node_count, cfi * 8) + f_start := cell_load(g_df_func_node_start, cfi * 8) + f_count := cell_load(g_df_func_node_count, cfi * 8) if f_start < 0 || f_count <= 0: return 0 if g_interp_call_depth >= IR_INTERP_MAX_DEPTH: @@ -335,12 +335,12 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if si3 >= g_interp_call_depth: break - if r64(g_interp_stack, si3 * 8) == cfi: + if cell_load(g_interp_stack, si3 * 8) == cfi: reentrant = 1 break si3 = si3 + 1 - vs := r64(g_ir_func_var_start, cfi * 8) - vc := r64(g_ir_func_var_count, cfi * 8) + vs := cell_load(g_ir_func_var_start, cfi * 8) + vc := cell_load(g_ir_func_var_count, cfi * 8) frame : string, mut = "" has_frame : ., mut = 0 if reentrant != 0 && vc > 0 && vs >= 0: @@ -350,7 +350,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if fi4 >= vc: break - w64(frame, fi4 * 8, r64(g_ir_vals, (vs + fi4) * 8)) + cell_store(frame, fi4 * 8, cell_load(g_ir_vals, (vs + fi4) * 8)) fi4 = fi4 + 1 // Copy arguments to a buffer before writing parameter slots. In a recursive // call the argument and parameter slots belong to the same function range, so @@ -362,10 +362,10 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if pai >= argc: break - w64(args_buf, pai * 8, r64(g_ir_vals, (arg_base + pai) * 8)) + cell_store(args_buf, pai * 8, cell_load(g_ir_vals, (arg_base + pai) * 8)) pai = pai + 1 // Push the frame. Re-entry detection and the depth check already ran. - w64(g_interp_stack, g_interp_call_depth * 8, cfi) + cell_store(g_interp_stack, g_interp_call_depth * 8, cfi) g_interp_call_depth = g_interp_call_depth + 1 rval : ., mut = 0 // a callee with no value (IR_RETURN s1 < 0) returns 0 // Save label state in locals so each recursion level restores its own state. @@ -379,12 +379,12 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if li2 >= f_count: break - n_op := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_OPCODE) - n_s1 := r64(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_S1) + n_op := cell_load(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_OPCODE) + n_s1 := cell_load(g_df_nodes, (f_start + li2) * ESZ_DFNODE + OFF_DF_S1) if n_op == 21: if n_s1 >= 0: grow_label_poses(n_s1 + 1) - w64(g_label_poses, n_s1 * 8, li2) + cell_store(g_label_poses, n_s1 * 8, li2) if n_s1 + 1 > g_label_count: g_label_count = n_s1 + 1 li2 = li2 + 1 @@ -394,7 +394,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if pai2 >= argc: break - w64(g_ir_vals, (pstart + pai2) * 8, r64(args_buf, pai2 * 8)) + cell_store(g_ir_vals, (pstart + pai2) * 8, cell_load(args_buf, pai2 * 8)) pai2 = pai2 + 1 // Execute callee graph (inline) ip2 : ., mut = 0 @@ -403,20 +403,20 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: break if g_interp_abort != 0: break - op2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_OPCODE) - d2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_DEST) - t1 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S1) - t2 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S2) - t3 := r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S3) + op2 := cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_OPCODE) + d2 := cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_DEST) + t1 := cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S1) + t2 := cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S2) + t3 := cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_S3) // Derived code, not a slot. After the single-slot change, slot 40 holds a // type-term reference. ir_interp_binary does not consume this argument // today; the operation code in s3 selects the operation. - t4 := sh_dfn_code_of_slots(r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_AUX)) + t4 := sh_dfn_code_of_slots(cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_TK), + cell_load(g_df_nodes, (f_start + ip2) * ESZ_DFNODE + OFF_DF_AUX)) if op2 == 1 && d2 >= 0: - w64(g_ir_vals, d2 * 8, ir_interp_const(t4, t1)) + cell_store(g_ir_vals, d2 * 8, ir_interp_const(t4, t1)) if op2 == 40 && d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, bi_from_dec(istr_get(t1))) + cell_store(g_ir_vals, d2 * 8, bi_from_dec(istr_get(t1))) if op2 == 2 && t1 >= 0 && t2 >= 0: ir_interp_binary(d2, t1, t2, t3, t4) if op2 == 49 || op2 == 50: @@ -424,30 +424,30 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: g_interp_abort = -1 break if op2 == 3 && t1 >= 0: - ov2 := r64(g_ir_vals, t1 * 8) + ov2 := cell_load(g_ir_vals, t1 * 8) if t3 == 1: if g_interp_big != 0 && irv_type(t1) == TI_INT: - w64(g_ir_vals, d2 * 8, bi_neg(ov2)) + cell_store(g_ir_vals, d2 * 8, bi_neg(ov2)) else: - w64(g_ir_vals, d2 * 8, -ov2) + cell_store(g_ir_vals, d2 * 8, -ov2) else if t3 == 2: if ov2 == 0: - w64(g_ir_vals, d2 * 8, 1) + cell_store(g_ir_vals, d2 * 8, 1) else: - w64(g_ir_vals, d2 * 8, 0) + cell_store(g_ir_vals, d2 * 8, 0) // Keep callee locals in the shared value store. Skipping these opcodes // would leave loop-carried assignments unchanged. if op2 == 6 && d2 >= 0: - w64(g_ir_vals, d2 * 8, 0) + cell_store(g_ir_vals, d2 * 8, 0) // IR_STORE if op2 == 9 && t1 >= 0 && t2 >= 0: - v2 := r64(g_ir_vals, t2 * 8) + v2 := cell_load(g_ir_vals, t2 * 8) if g_interp_big != 0 && irv_type(t1) == TI_INT && irv_type(t2) == TI_DEX_S: v2 = ir_interp_tag(v2) - w64(g_ir_vals, t1 * 8, v2) + cell_store(g_ir_vals, t1 * 8, v2) // IR_LOAD if op2 == 10 && d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // Aggregate operations: ALLOC_ARRAY, ALLOC_STRUCT, LOAD_INDEX, // STORE_INDEX, LOAD_FIELD, STORE_FIELD, and ADDR_INDEX. The callee path // must implement them too, or an aggregate global read inside a callee @@ -464,63 +464,63 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if vi3 >= need3: break - store8(bp2, vi3, 0) + cell_store_byte(bp2, vi3, 0) vi3 = vi3 + 1 unsafe: - w64(g_ir_vals, d2 * 8, @ptr_of(bp2)) + cell_store(g_ir_vals, d2 * 8, @ptr_of(bp2)) if op2 == 13 && d2 >= 0 && t1 >= 0: // IR_LOAD_INDEX: t1=arr_var, t3=literal_idx - av2 := r64(g_ir_vals, t1 * 8) + av2 := cell_load(g_ir_vals, t1 * 8) if irv_type(t1) == TI_STR: - w64(g_ir_vals, d2 * 8, str_load8(av2, t3)) + cell_store(g_ir_vals, d2 * 8, str_load8(av2, t3)) else: unsafe: - w64(g_ir_vals, d2 * 8, r64(@str_of(av2), t3 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), t3 * 8)) if op2 == 14 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX: t1=arr_var, t2=val_var - av2 := r64(g_ir_vals, t1 * 8) + av2 := cell_load(g_ir_vals, t1 * 8) if irv_type(t1) == TI_STR: - store8(istr_get(av2), t3, r64(g_ir_vals, t2 * 8)) + cell_store_byte(istr_get(av2), t3, cell_load(g_ir_vals, t2 * 8)) else: unsafe: - w64(@str_of(av2), t3 * 8, r64(g_ir_vals, t2 * 8)) + cell_store(@str_of(av2), t3 * 8, cell_load(g_ir_vals, t2 * 8)) if op2 == 15 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_LOAD_INDEX_VAR: t1=arr_var, t2=idx_var - av2 := r64(g_ir_vals, t1 * 8) - ix2 := ir_interp_word(r64(g_ir_vals, t2 * 8)) + av2 := cell_load(g_ir_vals, t1 * 8) + ix2 := ir_interp_word(cell_load(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: - w64(g_ir_vals, d2 * 8, str_load8(av2, ix2)) + cell_store(g_ir_vals, d2 * 8, str_load8(av2, ix2)) else: unsafe: - w64(g_ir_vals, d2 * 8, r64(@str_of(av2), ix2 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), ix2 * 8)) if op2 == 16 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX_VAR: d2=val_var, t1=arr_var, t2=idx_var - av2 := r64(g_ir_vals, t1 * 8) - ix2 := ir_interp_word(r64(g_ir_vals, t2 * 8)) + av2 := cell_load(g_ir_vals, t1 * 8) + ix2 := ir_interp_word(cell_load(g_ir_vals, t2 * 8)) if irv_type(t1) == TI_STR: - store8(istr_get(av2), ix2, r64(g_ir_vals, d2 * 8)) + cell_store_byte(istr_get(av2), ix2, cell_load(g_ir_vals, d2 * 8)) else: unsafe: - w64(@str_of(av2), ix2 * 8, r64(g_ir_vals, d2 * 8)) + cell_store(@str_of(av2), ix2 * 8, cell_load(g_ir_vals, d2 * 8)) if op2 == 31 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_ADDR_INDEX: &arr[i] - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + ir_interp_word(r64(g_ir_vals, t2 * 8)) * 8) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * 8) if op2 == 11 && d2 >= 0 && t1 >= 0: // IR_LOAD_FIELD: t1=struct_var, t3=field_idx - pf2 := r64(g_ir_vals, t1 * 8) + pf2 := cell_load(g_ir_vals, t1 * 8) if pf2 != 0: unsafe: - w64(g_ir_vals, d2 * 8, r64(@str_of(pf2), t3 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(pf2), t3 * 8)) else: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) if op2 == 12 && t1 >= 0 && t2 >= 0: // IR_STORE_FIELD: t1=struct_var, t2=val_var, t3=field_idx - pf2 := r64(g_ir_vals, t1 * 8) + pf2 := cell_load(g_ir_vals, t1 * 8) if pf2 != 0: unsafe: - w64(@str_of(pf2), t3 * 8, r64(g_ir_vals, t2 * 8)) + cell_store(@str_of(pf2), t3 * 8, cell_load(g_ir_vals, t2 * 8)) else: - w64(g_ir_vals, t1 * 8, r64(g_ir_vals, t2 * 8)) + cell_store(g_ir_vals, t1 * 8, cell_load(g_ir_vals, t2 * 8)) if op2 == 7 && d2 >= 0: // IR_ALLOC_STRUCT si2 := find_struct(t3) @@ -533,17 +533,17 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if vi3 >= need3: break - store8(bp2, vi3, 0) + cell_store_byte(bp2, vi3, 0) vi3 = vi3 + 1 unsafe: - w64(g_ir_vals, d2 * 8, @ptr_of(bp2)) + cell_store(g_ir_vals, d2 * 8, @ptr_of(bp2)) if op2 == 32 && d2 >= 0: - w64(g_ir_vals, d2 * 8, 0) + cell_store(g_ir_vals, d2 * 8, 0) // IR_ARENA_NEW // IR_ARENA_RESET if op2 == 46 || op2 == 47: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // The following opcode families mirror the main loop. // IR_MAKE_ENUM: allocate 8 + 8*s2 bytes and store s1 (the variant-name // index) in the first word. This matches the ELF layout [tag][payload]; @@ -556,84 +556,84 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if vi3 >= need3: break - store8(bp2, vi3, 0) + cell_store_byte(bp2, vi3, 0) vi3 = vi3 + 1 - w64(bp2, 0, t1) + cell_store(bp2, 0, t1) unsafe: - w64(g_ir_vals, d2 * 8, @ptr_of(bp2)) + cell_store(g_ir_vals, d2 * 8, @ptr_of(bp2)) // IR_LOAD_ENUM_TAG: d = tag word at the pointer in slot s1. if op2 == 23: if d2 >= 0 && t1 >= 0: - p23 := r64(g_ir_vals, t1 * 8) + p23 := cell_load(g_ir_vals, t1 * 8) if p23 != 0: unsafe: - w64(g_ir_vals, d2 * 8, ir_interp_tag(r64(@str_of(p23), 0))) + cell_store(g_ir_vals, d2 * 8, ir_interp_tag(cell_load(@str_of(p23), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op2 == 18: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, t1) + cell_store(g_ir_vals, d2 * 8, t1) // IR_DEREF: read through the pointer in slot s1. if op2 == 25: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, ir_interp_deref_read(r64(g_ir_vals, t1 * 8))) + cell_store(g_ir_vals, d2 * 8, ir_interp_deref_read(cell_load(g_ir_vals, t1 * 8))) // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op2 == 24: if d2 >= 0 && t1 >= 0 && t2 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8) + ir_interp_word(r64(g_ir_vals, t2 * 8)) * 8) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. A dynamic // limit uses ti = 1. An index below 0 or at least the limit aborts with -1. if op2 == 30 && t2 >= 0: - iv30 := ir_interp_word(r64(g_ir_vals, t1 * 8)) + iv30 := ir_interp_word(cell_load(g_ir_vals, t1 * 8)) lim30 : ., mut = t2 if t4 == 1: - lim30 = ir_interp_word(r64(g_ir_vals, t2 * 8)) + lim30 = ir_interp_word(cell_load(g_ir_vals, t2 * 8)) if iv30 < 0 || iv30 >= lim30: g_interp_abort = -1 break // IR_STORE_PTR: write the value in slot s2 through the pointer in slot s1. if op2 == 26: if t1 >= 0 && t2 >= 0: - ir_interp_deref_write(r64(g_ir_vals, t1 * 8), r64(g_ir_vals, t2 * 8)) + ir_interp_deref_write(cell_load(g_ir_vals, t1 * 8), cell_load(g_ir_vals, t2 * 8)) // IR_YIELD: pass the value in slot s1 eagerly, like IR_AWAIT. if op2 == 28: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // IR_AWAIT. (29) if op2 == 29: if d2 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. if op2 == 52: if d2 >= 0 && t1 >= 0: if g_interp_big != 0: - w64(g_ir_vals, d2 * 8, bi_from_uword(r64(g_ir_vals, t1 * 8))) + cell_store(g_ir_vals, d2 * 8, bi_from_uword(cell_load(g_ir_vals, t1 * 8))) else: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) if op2 == 53: if d2 >= 0 && t1 >= 0: if g_interp_big != 0: - w64(g_ir_vals, d2 * 8, bi_to_uword_checked(r64(g_ir_vals, t1 * 8))) + cell_store(g_ir_vals, d2 * 8, bi_to_uword_checked(cell_load(g_ir_vals, t1 * 8))) else: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // IR_FNADDR: no real addresses in the interpreter; store 0. if op2 == 48: if d2 >= 0: - w64(g_ir_vals, d2 * 8, 0) + cell_store(g_ir_vals, d2 * 8, 0) // IR_APPROX is a pure annotation; skip it. // IR_DYN_PACK: a dyn value uses two slots, value at d and tag at d+1. if op2 == 43: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) - w64(g_ir_vals, (d2 + 1) * 8, t2) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, (d2 + 1) * 8, t2) // IR_DYN_TAG: d = tag at slot s1 + 1. if op2 == 41: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, (t1 + 1) * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, (t1 + 1) * 8)) // IR_DYN_VAL: d = value at slot s1. if op2 == 42: if d2 >= 0 && t1 >= 0: - w64(g_ir_vals, d2 * 8, r64(g_ir_vals, t1 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // IR_DYN_DISPATCH has no interpreter implementation; report an error. if op2 == 44: println("error: interp 不支持动态分发(IR_DYN_DISPATCH)") @@ -650,20 +650,20 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: ir_interp_call(d2, t1, t2, t3) if op2 == 5: if t1 >= 0: - rval = r64(g_ir_vals, t1 * 8) + rval = cell_load(g_ir_vals, t1 * 8) ip2 = f_count // return exits the callee immediately // IR_RETURN if op2 == 19 && t1 >= 0: - cv2 := r64(g_ir_vals, t1 * 8) + cv2 := cell_load(g_ir_vals, t1 * 8) if cv2 != 0: if t2 >= 0 && t2 < g_label_count: - ip2 = r64(g_label_poses, t2 * 8) + ip2 = cell_load(g_label_poses, t2 * 8) else: if t3 >= 0 && t3 < g_label_count: - ip2 = r64(g_label_poses, t3 * 8) + ip2 = cell_load(g_label_poses, t3 * 8) if op2 == 20: if t1 >= 0 && t1 < g_label_count: - ip2 = r64(g_label_poses, t1 * 8) + ip2 = cell_load(g_label_poses, t1 * 8) ip2 = ip2 + 1 // Restore label state g_label_count = old_lc @@ -676,7 +676,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: loop: if fi5 >= vc: break - w64(g_ir_vals, (vs + fi5) * 8, r64(frame, fi5 * 8)) + cell_store(g_ir_vals, (vs + fi5) * 8, cell_load(frame, fi5 * 8)) fi5 = fi5 + 1 g_interp_call_depth = g_interp_call_depth - 1 if g_interp_abort != 0: @@ -690,15 +690,15 @@ fn ir_interpret() -> int: loop: if fi >= g_ir_func_count: break - ni := r64(g_ir_func_name_idx, fi * 8) + ni := cell_load(g_ir_func_name_idx, fi * 8) if str_eq(istr_get(ni), "main") != 0: main_idx = fi break fi = fi + 1 if main_idx < 0: return -1 - node_start := r64(g_df_func_node_start, main_idx * 8) - node_count := r64(g_df_func_node_count, main_idx * 8) + node_start := cell_load(g_df_func_node_start, main_idx * 8) + node_count := cell_load(g_df_func_node_count, main_idx * 8) if node_start < 0 || node_count <= 0: return -1 @@ -721,7 +721,7 @@ fn ir_interpret() -> int: loop: if vi >= need: break - w64(g_ir_vals, vi * 8, 0) + cell_store(g_ir_vals, vi * 8, 0) vi = vi + 1 // Initialize compile-time scalar globals. This mirrors the constant loop in @@ -732,10 +732,10 @@ fn ir_interpret() -> int: loop: if gi2 >= g_ir_global_count: break - iv2 := r64(g_ir_globals, gi2 * 24 + 16) - gv2 := r64(g_ir_globals, gi2 * 24 + 8) + iv2 := cell_load(g_ir_globals, gi2 * 24 + 16) + gv2 := cell_load(g_ir_globals, gi2 * 24 + 8) if iv2 != 0 && gv2 >= 0 && gv2 < need: - w64(g_ir_vals, gv2 * 8, iv2) + cell_store(g_ir_vals, gv2 * 8, iv2) gi2 = gi2 + 1 // Pre-scan: build the label-to-node mapping for branches. @@ -744,14 +744,14 @@ fn ir_interpret() -> int: loop: if li >= node_count: break - n_op := r64(g_df_nodes, (node_start + li) * ESZ_DFNODE + OFF_DF_OPCODE) - n_s1 := r64(g_df_nodes, (node_start + li) * ESZ_DFNODE + OFF_DF_S1) + n_op := cell_load(g_df_nodes, (node_start + li) * ESZ_DFNODE + OFF_DF_OPCODE) + n_s1 := cell_load(g_df_nodes, (node_start + li) * ESZ_DFNODE + OFF_DF_S1) if n_op == 21: // IR_LABEL ln := n_s1 // label number if ln >= 0: grow_label_poses(ln + 1) - w64(g_label_poses, ln * 8, li) + cell_store(g_label_poses, ln * 8, li) if ln + 1 > g_label_count: g_label_count = ln + 1 li = li + 1 @@ -763,10 +763,10 @@ fn ir_interpret() -> int: loop: if li >= g_sg_count: break - lk := r64(g_sgs, li * ESZ_SG + OFF_SG_KIND) + lk := cell_load(g_sgs, li * ESZ_SG + OFF_SG_KIND) if lk == SG_LOOP || lk == SG_FOR: - l_enter := r64(g_sgs, li * ESZ_SG + OFF_SG_ENTER) - l_exit := r64(g_sgs, li * ESZ_SG + OFF_SG_EXIT) + l_enter := cell_load(g_sgs, li * ESZ_SG + OFF_SG_ENTER) + l_exit := cell_load(g_sgs, li * ESZ_SG + OFF_SG_EXIT) if l_enter >= node_start && l_enter < node_start + node_count: if g_loop_region_count + 1 > g_loop_region_cap: // Grow both arrays using the old capacity. Copying with the new @@ -775,14 +775,14 @@ fn ir_interpret() -> int: if nc < 16: nc = 16 nb := alloc(nc * 8) - _dyncpy(g_loop_region_enter, g_loop_region_cap * 8, nb) + cell_copy(g_loop_region_enter, g_loop_region_cap * 8, nb) nb2 := alloc(nc * 8) - _dyncpy(g_loop_region_exit, g_loop_region_cap * 8, nb2) + cell_copy(g_loop_region_exit, g_loop_region_cap * 8, nb2) g_loop_region_enter = nb g_loop_region_exit = nb2 g_loop_region_cap = nc - w64(g_loop_region_enter, g_loop_region_count * 8, l_enter - node_start) - w64(g_loop_region_exit, g_loop_region_count * 8, l_exit - node_start) + cell_store(g_loop_region_enter, g_loop_region_count * 8, l_enter - node_start) + cell_store(g_loop_region_exit, g_loop_region_count * 8, l_exit - node_start) g_loop_region_count = g_loop_region_count + 1 li = li + 1 @@ -791,26 +791,26 @@ fn ir_interpret() -> int: loop: if ip >= node_count: break - op := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_OPCODE) - d := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_DEST) - s1 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S1) - s2 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S2) - s3 := r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S3) + op := cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_OPCODE) + d := cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_DEST) + s1 := cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S1) + s2 := cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S2) + s3 := cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_S3) // Derived code as above; passed to IR_BINARY and currently unused. - ti := sh_dfn_code_of_slots(r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_AUX)) + ti := sh_dfn_code_of_slots(cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_TK), + cell_load(g_df_nodes, (node_start + ip) * ESZ_DFNODE + OFF_DF_AUX)) if op == 1: if d >= 0: - w64(g_ir_vals, d * 8, ir_interp_const(ti, s1)) + cell_store(g_ir_vals, d * 8, ir_interp_const(ti, s1)) // IR_CONST; dex uses a scaled integer in s1. if op == 40: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, bi_from_dec(istr_get(s1))) + cell_store(g_ir_vals, d * 8, bi_from_dec(istr_get(s1))) // IR_CONST_BIG if op == 5: if s1 >= 0: - return ir_interp_word(r64(g_ir_vals, s1 * 8)) + return ir_interp_word(cell_load(g_ir_vals, s1 * 8)) return 0 // IR_RETURN @@ -820,22 +820,22 @@ fn ir_interpret() -> int: if op == 3: // IR_UNARY - ov := r64(g_ir_vals, s1 * 8) + ov := cell_load(g_ir_vals, s1 * 8) if s3 == 1: if g_interp_big != 0 && irv_type(s1) == TI_INT: - w64(g_ir_vals, d * 8, bi_neg(ov)) + cell_store(g_ir_vals, d * 8, bi_neg(ov)) else: - w64(g_ir_vals, d * 8, -ov) + cell_store(g_ir_vals, d * 8, -ov) if s3 == 2: if ov == 0: - w64(g_ir_vals, d * 8, 1) + cell_store(g_ir_vals, d * 8, 1) else: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) // Alloc / Store / Load / Field / Index if op == 6: if d >= 0: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) // IR_ALLOC if op == 7: // IR_ALLOC_STRUCT @@ -850,10 +850,10 @@ fn ir_interpret() -> int: loop: if vi2 >= need2: break - store8(bp, vi2, 0) + cell_store_byte(bp, vi2, 0) vi2 = vi2 + 1 unsafe: - w64(g_ir_vals, d * 8, @ptr_of(bp)) + cell_store(g_ir_vals, d * 8, @ptr_of(bp)) if op == 8: // IR_ALLOC_ARRAY if d >= 0: @@ -867,77 +867,77 @@ fn ir_interpret() -> int: loop: if vi2 >= need2: break - store8(bp, vi2, 0) + cell_store_byte(bp, vi2, 0) vi2 = vi2 + 1 unsafe: - w64(g_ir_vals, d * 8, @ptr_of(bp)) + cell_store(g_ir_vals, d * 8, @ptr_of(bp)) if op == 9: if s1 >= 0 && s2 >= 0: - v := r64(g_ir_vals, s2 * 8) + v := cell_load(g_ir_vals, s2 * 8) if g_interp_big != 0 && irv_type(s1) == TI_INT && irv_type(s2) == TI_DEX_S: v = ir_interp_tag(v) - w64(g_ir_vals, s1 * 8, v) + cell_store(g_ir_vals, s1 * 8, v) // IR_LOAD if op == 10: if d >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_LOAD if op == 11: // IR_LOAD_FIELD: s1=struct_var, s3=field_idx if d >= 0 && s1 >= 0: - ptr := r64(g_ir_vals, s1 * 8) + ptr := cell_load(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - w64(g_ir_vals, d * 8, r64(@str_of(ptr), s3 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(ptr), s3 * 8)) else: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) if op == 12: // IR_STORE_FIELD: s1=struct_var, s2=val_var, s3=field_idx if s1 >= 0 && s2 >= 0: - ptr := r64(g_ir_vals, s1 * 8) + ptr := cell_load(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - w64(@str_of(ptr), s3 * 8, r64(g_ir_vals, s2 * 8)) + cell_store(@str_of(ptr), s3 * 8, cell_load(g_ir_vals, s2 * 8)) else: - w64(g_ir_vals, s1 * 8, r64(g_ir_vals, s2 * 8)) + cell_store(g_ir_vals, s1 * 8, cell_load(g_ir_vals, s2 * 8)) if op == 13: // IR_LOAD_INDEX: s1=arr_var, s3=literal_idx if d >= 0 && s1 >= 0: - arr_value := r64(g_ir_vals, s1 * 8) + arr_value := cell_load(g_ir_vals, s1 * 8) if irv_type(s1) == TI_STR: - w64(g_ir_vals, d * 8, str_load8(arr_value, s3)) + cell_store(g_ir_vals, d * 8, str_load8(arr_value, s3)) else: unsafe: - w64(g_ir_vals, d * 8, r64(@str_of(arr_value), s3 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), s3 * 8)) if op == 14: // IR_STORE_INDEX: s1=arr_var, s2=val_var, s3=literal_idx if s1 >= 0 && s2 >= 0: - arr_value := r64(g_ir_vals, s1 * 8) + arr_value := cell_load(g_ir_vals, s1 * 8) if irv_type(s1) == TI_STR: - store8(istr_get(arr_value), s3, r64(g_ir_vals, s2 * 8)) + cell_store_byte(istr_get(arr_value), s3, cell_load(g_ir_vals, s2 * 8)) else: unsafe: - w64(@str_of(arr_value), s3 * 8, r64(g_ir_vals, s2 * 8)) + cell_store(@str_of(arr_value), s3 * 8, cell_load(g_ir_vals, s2 * 8)) if op == 15: // IR_LOAD_INDEX_VAR: s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: - arr_value := r64(g_ir_vals, s1 * 8) - idx := ir_interp_word(r64(g_ir_vals, s2 * 8)) + arr_value := cell_load(g_ir_vals, s1 * 8) + idx := ir_interp_word(cell_load(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: - w64(g_ir_vals, d * 8, str_load8(arr_value, idx)) + cell_store(g_ir_vals, d * 8, str_load8(arr_value, idx)) else: unsafe: - w64(g_ir_vals, d * 8, r64(@str_of(arr_value), idx * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), idx * 8)) if op == 16: // IR_STORE_INDEX_VAR: d=val_var, s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: - arr_value := r64(g_ir_vals, s1 * 8) - idx := ir_interp_word(r64(g_ir_vals, s2 * 8)) + arr_value := cell_load(g_ir_vals, s1 * 8) + idx := ir_interp_word(cell_load(g_ir_vals, s2 * 8)) if irv_type(s1) == TI_STR: - store8(istr_get(arr_value), idx, r64(g_ir_vals, d * 8)) + cell_store_byte(istr_get(arr_value), idx, cell_load(g_ir_vals, d * 8)) else: unsafe: - w64(@str_of(arr_value), idx * 8, r64(g_ir_vals, d * 8)) + cell_store(@str_of(arr_value), idx * 8, cell_load(g_ir_vals, d * 8)) // IR_MAKE_ENUM: allocate 8 + 8*s2 bytes and store s1 (the variant-name // index) in the first word. This matches the ELF layout [tag][payload]; // IR_STORE_FIELD writes the payload. @@ -949,39 +949,39 @@ fn ir_interpret() -> int: loop: if vi2 >= need2: break - store8(bp, vi2, 0) + cell_store_byte(bp, vi2, 0) vi2 = vi2 + 1 - w64(bp, 0, s1) + cell_store(bp, 0, s1) unsafe: - w64(g_ir_vals, d * 8, @ptr_of(bp)) + cell_store(g_ir_vals, d * 8, @ptr_of(bp)) // IR_LOAD_ENUM_TAG: d = tag word at the pointer in slot s1. if op == 23: if d >= 0 && s1 >= 0: - ptr := r64(g_ir_vals, s1 * 8) + ptr := cell_load(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - w64(g_ir_vals, d * 8, ir_interp_tag(r64(@str_of(ptr), 0))) + cell_store(g_ir_vals, d * 8, ir_interp_tag(cell_load(@str_of(ptr), 0))) // IR_REF: d = s1. The slot number is the stack address used by the deref helpers. if op == 18: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, s1) + cell_store(g_ir_vals, d * 8, s1) // IR_DEREF: read through the pointer in slot s1. if op == 25: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, ir_interp_deref_read(r64(g_ir_vals, s1 * 8))) + cell_store(g_ir_vals, d * 8, ir_interp_deref_read(cell_load(g_ir_vals, s1 * 8))) // IR_SLICE: d = arr + low*8, matching the ELF encoding. if op == 24: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + ir_interp_word(r64(g_ir_vals, s2 * 8)) * 8) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * 8) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. An index // below 0 or at least the limit aborts with -1. if op == 30 && s2 >= 0: - iv := ir_interp_word(r64(g_ir_vals, s1 * 8)) + iv := ir_interp_word(cell_load(g_ir_vals, s1 * 8)) limit : ., mut = s2 if ti == 1: - limit = ir_interp_word(r64(g_ir_vals, s2 * 8)) + limit = ir_interp_word(cell_load(g_ir_vals, s2 * 8)) if iv < 0 || iv >= limit: return -1 @@ -989,33 +989,33 @@ fn ir_interpret() -> int: // preserve the computed value. if op == 46 || op == 47: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_YIELD: pass the value in slot s1 eagerly, like IR_AWAIT. Emission // uses dest = -1, so the d >= 0 check prevents a write before the value // buffer. if op == 28: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_PTR_OF / IR_STR_OF: raw address <-> mathematical int view. if op == 52: if d >= 0 && s1 >= 0: if g_interp_big != 0: - w64(g_ir_vals, d * 8, bi_from_uword(r64(g_ir_vals, s1 * 8))) + cell_store(g_ir_vals, d * 8, bi_from_uword(cell_load(g_ir_vals, s1 * 8))) else: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) if op == 53: if d >= 0 && s1 >= 0: if g_interp_big != 0: - w64(g_ir_vals, d * 8, bi_to_uword_checked(r64(g_ir_vals, s1 * 8))) + cell_store(g_ir_vals, d * 8, bi_to_uword_checked(cell_load(g_ir_vals, s1 * 8))) else: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_FNADDR: the interpreter has no real addresses; store 0. if op == 48: if d >= 0: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) // IR_APPROX is a pure annotation; skip it. if op == 51: @@ -1033,31 +1033,31 @@ fn ir_interpret() -> int: // The pointer may be a stack slot number or a heap address. if op == 26: if s1 >= 0 && s2 >= 0: - ir_interp_deref_write(r64(g_ir_vals, s1 * 8), r64(g_ir_vals, s2 * 8)) + ir_interp_deref_write(cell_load(g_ir_vals, s1 * 8), cell_load(g_ir_vals, s2 * 8)) // IR_ADDR_INDEX: d = slot s1 + 8 * slot s2 for &arr[i]. if op == 31: if d >= 0 && s1 >= 0 && s2 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8) + ir_interp_word(r64(g_ir_vals, s2 * 8)) * 8) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * 8) // IR_ARENA_NEW and IR_ARENA_RESET: the slot model has no scoped arena, so // these are approximations. if op == 32: if d >= 0: - w64(g_ir_vals, d * 8, 0) + cell_store(g_ir_vals, d * 8, 0) // IR_DYN_PACK: a dyn value uses two slots, value at d and tag at d+1. if op == 43: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) - w64(g_ir_vals, (d + 1) * 8, s2) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, (d + 1) * 8, s2) // IR_DYN_TAG: d = tag at slot s1 + 1. if op == 41: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, (s1 + 1) * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, (s1 + 1) * 8)) // IR_DYN_VAL: d = value at slot s1. if op == 42: if d >= 0 && s1 >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_DYN_DISPATCH has no interpreter implementation; report an error. if op == 44: @@ -1073,13 +1073,13 @@ fn ir_interpret() -> int: // Branch by node index. if op == 19: - cv := r64(g_ir_vals, s1 * 8) + cv := cell_load(g_ir_vals, s1 * 8) if cv != 0: if s2 >= 0 && s2 < g_label_count: - ip = r64(g_label_poses, s2 * 8) + ip = cell_load(g_label_poses, s2 * 8) else: if s3 >= 0 && s3 < g_label_count: - ip = r64(g_label_poses, s3 * 8) + ip = cell_load(g_label_poses, s3 * 8) if ip < node_count: continue else: @@ -1096,19 +1096,19 @@ fn ir_interpret() -> int: loop: if ri2 >= g_loop_region_count: break - e2 := r64(g_loop_region_enter, ri2 * 8) - x2 := r64(g_loop_region_exit, ri2 * 8) + e2 := cell_load(g_loop_region_enter, ri2 * 8) + x2 := cell_load(g_loop_region_exit, ri2 * 8) if ip >= e2 && ip < x2 && e2 > cur_enter: cur_enter = e2 cur_ri = ri2 ri2 = ri2 + 1 if s1 >= 0 && s1 < g_label_count: - target := r64(g_label_poses, s1 * 8) + target := cell_load(g_label_poses, s1 * 8) if target >= 0: if cur_ri >= 0 && target == cur_enter: // Back-edge to the innermost loop region's enter: // ip comes from the region table, not label poses. - ip = r64(g_loop_region_enter, cur_ri * 8) // == cur_enter + ip = cell_load(g_loop_region_enter, cur_ri * 8) // == cur_enter else: ip = target // plain jump, resolved via label poses else: @@ -1124,7 +1124,7 @@ fn ir_interpret() -> int: // IR_AWAIT if op == 29: if d >= 0: - w64(g_ir_vals, d * 8, r64(g_ir_vals, s1 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) // IR_CALL and IR_SPAWN use the shared dispatch in ir_interp_call. Builtins, // inline user functions, nested recursion, and abort propagation all live diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index d128f767..04b41c09 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -28,7 +28,7 @@ fn grow_ir_slice_lens(needed: int): if nc < needed: nc = needed + 8 nb := alloc(nc * 16) - _dyncpy(g_ir_slice_lens, g_ir_slice_len_cap * 16, nb) + cell_copy(g_ir_slice_lens, g_ir_slice_len_cap * 16, nb) g_ir_slice_lens = nb g_ir_slice_len_cap = nc @@ -37,8 +37,8 @@ fn slice_len_get(var_idx: int) -> int: loop: if si >= g_ir_slice_len_count: break - if r64(g_ir_slice_lens, si * 16) == var_idx: - return r64(g_ir_slice_lens, si * 16 + 8) + if cell_load(g_ir_slice_lens, si * 16) == var_idx: + return cell_load(g_ir_slice_lens, si * 16 + 8) si = si + 1 return -1 @@ -47,15 +47,15 @@ fn slice_len_set(var_idx: int, len: int): loop: if si >= g_ir_slice_len_count: break - if r64(g_ir_slice_lens, si * 16) == var_idx: - w64(g_ir_slice_lens, si * 16 + 8, len) + if cell_load(g_ir_slice_lens, si * 16) == var_idx: + cell_store(g_ir_slice_lens, si * 16 + 8, len) return si = si + 1 if len < 0: return grow_ir_slice_lens(g_ir_slice_len_count + 1) - w64(g_ir_slice_lens, g_ir_slice_len_count * 16, var_idx) - w64(g_ir_slice_lens, g_ir_slice_len_count * 16 + 8, len) + cell_store(g_ir_slice_lens, g_ir_slice_len_count * 16, var_idx) + cell_store(g_ir_slice_lens, g_ir_slice_len_count * 16 + 8, len) g_ir_slice_len_count = g_ir_slice_len_count + 1 // F11 运行时界切片:len 字段编码, @@ -65,16 +65,16 @@ fn slice_len_raw_set(var_idx: int, len_code: int): loop: if si >= g_ir_slice_len_count: break - if r64(g_ir_slice_lens, si * 16) == var_idx: - w64(g_ir_slice_lens, si * 16 + 8, len_code) + if cell_load(g_ir_slice_lens, si * 16) == var_idx: + cell_store(g_ir_slice_lens, si * 16 + 8, len_code) return si = si + 1 if len_code == -1: return // 清除无条目 = no-op;负编码(长度变量)允许新增 grow_ir_slice_lens(g_ir_slice_len_count + 1) - w64(g_ir_slice_lens, g_ir_slice_len_count * 16, var_idx) - w64(g_ir_slice_lens, g_ir_slice_len_count * 16 + 8, len_code) + cell_store(g_ir_slice_lens, g_ir_slice_len_count * 16, var_idx) + cell_store(g_ir_slice_lens, g_ir_slice_len_count * 16 + 8, len_code) g_ir_slice_len_count = g_ir_slice_len_count + 1 // 登记运行时界切片的长度变量;查询返回 len_var(无 -> -1)。 @@ -88,8 +88,8 @@ fn slice_len_var_get(var_idx: int) -> int: loop: if si >= g_ir_slice_len_count: break - if r64(g_ir_slice_lens, si * 16) == var_idx: - v := r64(g_ir_slice_lens, si * 16 + 8) + if cell_load(g_ir_slice_lens, si * 16) == var_idx: + v := cell_load(g_ir_slice_lens, si * 16 + 8) if v <= -2: return -2 - v return -1 @@ -103,8 +103,8 @@ fn slice_len_copy_to(dst_var: int, src_var: int): loop: if si >= g_ir_slice_len_count: break - if r64(g_ir_slice_lens, si * 16) == src_var: - slice_len_raw_set(dst_var, r64(g_ir_slice_lens, si * 16 + 8)) + if cell_load(g_ir_slice_lens, si * 16) == src_var: + slice_len_raw_set(dst_var, cell_load(g_ir_slice_lens, si * 16 + 8)) return si = si + 1 slice_len_raw_set(dst_var, -1) @@ -136,12 +136,12 @@ fn arr_len_lit_of(arr_var: int) -> int: if ti >= 0 && ti < g_type_count && get_type_kind(ti) == TYP_ARRAY: return get_type_extra(ti) if ti == TI_STR: - prod := r64(g_df_var_producer, arr_var * 8) + prod := cell_load(g_df_var_producer, arr_var * 8) // 读 派生码(不是 40 槽,单槽化后 40 槽 = 类型项引用); // 常量节点的行是原子基行 -> 派生码逐字节 ≡ 单槽化前该槽的混用码。 - if prod >= 0 && r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CONST && sh_dfn_code_of_slots(r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_TK), - r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_AUX)) == TI_STR: - return istr_len(r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1)) + if prod >= 0 && cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CONST && sh_dfn_code_of_slots(cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_TK), + cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_AUX)) == TI_STR: + return istr_len(cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1)) return slice_len_get(arr_var) fn emit_string_bounds(arr_var: int, idx_var: int): @@ -169,7 +169,7 @@ fn grow_sg_alloc(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_sg_alloc_total, g_sg_alloc_cap * 8, nb) + cell_copy(g_sg_alloc_total, g_sg_alloc_cap * 8, nb) g_sg_alloc_total = nb g_sg_alloc_cap = nc @@ -182,26 +182,26 @@ fn grow_sg_arena_var(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_sg_arena_var, g_sg_arena_var_cap * 8, nb) + cell_copy(g_sg_arena_var, g_sg_arena_var_cap * 8, nb) g_sg_arena_var = nb g_sg_arena_var_cap = nc fn sg_alloc_push(kind: int): grow_sg_alloc(g_sg_count + 1) grow_sg_arena_var(g_sg_count + 1) - w64(g_sg_alloc_total, g_sg_count * 8, 0) + cell_store(g_sg_alloc_total, g_sg_count * 8, 0) sg_push(kind) fn sg_alloc_pop(): - total := r64(g_sg_alloc_total, (g_sg_count - 1) * 8) - arena_var := r64(g_sg_arena_var, (g_sg_count - 1) * 8) + total := cell_load(g_sg_alloc_total, (g_sg_count - 1) * 8) + arena_var := cell_load(g_sg_arena_var, (g_sg_count - 1) * 8) sg_pop() emit(IR_ARENA_RESET, -1, arena_var, 0, 0, 0) fn track_alloc_size(size: int): if g_sg_count > 0 && str_len(g_sg_alloc_total) > 0: - prev := r64(g_sg_alloc_total, (g_sg_count - 1) * 8) - w64(g_sg_alloc_total, (g_sg_count - 1) * 8, prev + size) + prev := cell_load(g_sg_alloc_total, (g_sg_count - 1) * 8) + cell_store(g_sg_alloc_total, (g_sg_count - 1) * 8, prev + size) fn new_ir_var(name: string, type_idx: int) -> int: idx := g_ir_var_count @@ -239,7 +239,7 @@ fn grow_ir_var_rep(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_ir_var_rep, g_ir_var_rep_cap * 8, nb) + cell_copy(g_ir_var_rep, g_ir_var_rep_cap * 8, nb) // 新槽填 -1(哨兵 = 无表示位),alloc 零初始化,不填则未写槽被读成 // 表示位 var 0(每个值都被判成有表示位,产生错误值且不报错;for example, the `None` initial value // 的表示位被读成 var0 的当前值 0 = 裸值)。 @@ -247,7 +247,7 @@ fn grow_ir_var_rep(needed: int): loop: if z >= nc: break - w64(nb, z * 8, -1) + cell_store(nb, z * 8, -1) z = z + 1 g_ir_var_rep = nb g_ir_var_rep_cap = nc @@ -263,13 +263,13 @@ fn irv_rep(var_idx: int) -> int: return -1 if var_idx >= g_ir_var_rep_cap: return -1 - return r64(g_ir_var_rep, var_idx * 8) + return cell_load(g_ir_var_rep, var_idx * 8) fn irv_set_rep(var_idx: int, rep_var: int): if var_idx < 0: return grow_ir_var_rep(var_idx + 1) - w64(g_ir_var_rep, var_idx * 8, rep_var) + cell_store(g_ir_var_rep, var_idx * 8, rep_var) // 置表示位:enc >= 0 -> 从该表示位槽拷贝;OE_BARE/OE_BOXED -> 直接常量写。 fn emit_rep_set(rep_var: int, enc: int): @@ -302,7 +302,7 @@ fn global_decl_optional(name_ni: int) -> int: loop: if gi >= g_global_let_count: break - lnode := r64(g_global_lets, gi * 8) + lnode := cell_load(g_global_lets, gi * 8) if ast_a(lnode) == name_ni: tn := ast_b(lnode) if tn >= 0 && res_type_node(tn) >= 0: @@ -354,12 +354,12 @@ fn grow_ir_var_decl(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_ir_var_decl_ti, g_ir_var_decl_cap * 8, nb) + cell_copy(g_ir_var_decl_ti, g_ir_var_decl_cap * 8, nb) z : ., mut = g_ir_var_decl_cap loop: if z >= nc: break - w64(nb, z * 8, -1) + cell_store(nb, z * 8, -1) z = z + 1 g_ir_var_decl_ti = nb g_ir_var_decl_cap = nc @@ -370,12 +370,12 @@ fn irv_decl_ti(v: int) -> int: return -1 if v >= g_ir_var_decl_cap: return -1 - return r64(g_ir_var_decl_ti, v * 8) + return cell_load(g_ir_var_decl_ti, v * 8) fn irv_set_decl_ti(v: int, ti: int): if v < 0 || ti < 0: return grow_ir_var_decl(v + 1) - w64(g_ir_var_decl_ti, v * 8, ti) + cell_store(g_ir_var_decl_ti, v * 8, ti) // 目标槽类型是否可选(未知/越界 -> 0 = 不装箱) fn ti_is_optional(ti: int) -> int: @@ -433,7 +433,7 @@ fn global_decl_ti(name_ni: int) -> int: loop: if gi >= g_global_let_count: break - lnode := r64(g_global_lets, gi * 8) + lnode := cell_load(g_global_lets, gi * 8) if ast_a(lnode) == name_ni: tn := ast_b(lnode) if tn >= 0: @@ -785,8 +785,8 @@ fn new_label() -> int: fn bind_local(name_idx: int, var_idx: int): grow_ir_locals(g_ir_local_count + 1) - w64(g_ir_locals, g_ir_local_count * 16, name_idx) - w64(g_ir_locals, g_ir_local_count * 16 + 8, var_idx) + cell_store(g_ir_locals, g_ir_local_count * 16, name_idx) + cell_store(g_ir_locals, g_ir_local_count * 16 + 8, var_idx) g_ir_local_count = g_ir_local_count + 1 fn find_local(name_idx: int) -> int: @@ -794,8 +794,8 @@ fn find_local(name_idx: int) -> int: loop: if i < 0: return -1 - if r64(g_ir_locals, i * 16) == name_idx: - return r64(g_ir_locals, i * 16 + 8) + if cell_load(g_ir_locals, i * 16) == name_idx: + return cell_load(g_ir_locals, i * 16 + 8) i = i - 1 return -1 @@ -804,8 +804,8 @@ fn find_global(name_idx: int) -> int: loop: if i < 0: return -1 - if r64(g_ir_globals, i * 24) == name_idx: - return r64(g_ir_globals, i * 24 + 8) + if cell_load(g_ir_globals, i * 24) == name_idx: + return cell_load(g_ir_globals, i * 24 + 8) i = i - 1 return -1 @@ -817,7 +817,7 @@ fn find_global_const_node(name_idx: int) -> int: loop: if i < 0: break - node := r64(g_global_lets, i * 8) + node := cell_load(g_global_lets, i * 8) if ast_a(node) == name_idx && ast_data(node) == 0: value_node := ast_c(node) if value_node >= 0: @@ -830,12 +830,12 @@ fn find_global_const_node(name_idx: int) -> int: fn push_ir_scope(): grow_ir_local_scopes(g_ir_local_depth + 1) - w64(g_ir_local_scopes, g_ir_local_depth * 8, g_ir_local_count) + cell_store(g_ir_local_scopes, g_ir_local_depth * 8, g_ir_local_count) g_ir_local_depth = g_ir_local_depth + 1 fn pop_ir_scope(): g_ir_local_depth = g_ir_local_depth - 1 - g_ir_local_count = r64(g_ir_local_scopes, g_ir_local_depth * 8) + g_ir_local_count = cell_load(g_ir_local_scopes, g_ir_local_depth * 8) fn is_ptr_var(var_idx: int) -> int: if var_idx < 0: @@ -849,10 +849,10 @@ fn is_ptr_var(var_idx: int) -> int: tk := get_type_kind(ti) if tk == TYP_PTR || tk == TYP_REF: return 1 - prod := r64(g_df_var_producer, var_idx * 8) + prod := cell_load(g_df_var_producer, var_idx * 8) if prod < 0: return 0 - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_ALLOC: ti := irv_type(var_idx) if ti == TI_INT || ti == TI_DEX || ti == TI_BOOL || ti == TI_CHAR: @@ -870,15 +870,15 @@ fn is_byte_buf_var(var_idx: int) -> int: return 0 if irv_type(var_idx) != TI_UNIT: return 0 - prod := r64(g_df_var_producer, var_idx * 8) + prod := cell_load(g_df_var_producer, var_idx * 8) if prod < 0: return 0 - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_ALLOC: return 1 if prod_op == IR_CALL: - func_ni := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S3) - if func_ni >= 0 && istr_get(func_ni) == "alloc": + func_ni := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S3) + if runtime_frontend_builtin_is_alloc(func_ni) != 0: return 1 return 0 @@ -887,14 +887,14 @@ fn ir_call_return_type(func_ni: int) -> int: return TI_UNIT // alloc() produces a raw byte buffer. Keep the existing internal pointer // sentinel until Core has a distinct raw-buffer type. - if istr_get(func_ni) == "alloc": + if runtime_frontend_builtin_is_alloc(func_ni) != 0: return TI_UNIT bi : ., mut = 0 loop: if bi >= g_rt_builtin_count: break - if r64(g_rt_builtin_names, bi * 8) == func_ni: - return r64(g_rt_builtin_ret_types, bi * 8) + if cell_load(g_rt_builtin_names, bi * 8) == func_ni: + return cell_load(g_rt_builtin_ret_types, bi * 8) bi = bi + 1 fi := find_func(func_ni) if fi >= 0: @@ -941,18 +941,18 @@ fn track_str(str_idx: int): loop: if i >= g_ir_str_const_count: break - if r64(g_ir_str_consts, i * 8) == str_idx: + if cell_load(g_ir_str_consts, i * 8) == str_idx: return i = i + 1 grow_ir_str_consts(g_ir_str_const_count + 1) - w64(g_ir_str_consts, g_ir_str_const_count * 8, str_idx) + cell_store(g_ir_str_consts, g_ir_str_const_count * 8, str_idx) g_ir_str_const_count = g_ir_str_const_count + 1 // --- Loop label stack --- fn push_loop_labels(header: int, exit: int): grow_ir_loop_stacks(g_ir_loop_depth + 1) - w64(g_ir_loop_header, g_ir_loop_depth * 8, header) - w64(g_ir_loop_exit, g_ir_loop_depth * 8, exit) + cell_store(g_ir_loop_header, g_ir_loop_depth * 8, header) + cell_store(g_ir_loop_exit, g_ir_loop_depth * 8, exit) g_ir_loop_depth = g_ir_loop_depth + 1 fn pop_loop_labels(): @@ -1057,7 +1057,7 @@ fn ptr_pointee_type(var_idx: int) -> int: fn ptr_access_width(var_idx: int) -> int: width := type_size(ptr_pointee_type(var_idx)) if width <= 0: - return 8 + return mp_stride(TI_INT) return width // Type alignment in bytes. The classic mapping lives in mapping.cr. @@ -1144,9 +1144,9 @@ fn is_hotpatch_func(name_idx: int) -> int: fn force_if_thunk(var_idx: int) -> int: if var_idx < 0: return var_idx - prod := r64(g_df_var_producer, var_idx * 8) + prod := cell_load(g_df_var_producer, var_idx * 8) if prod >= 0: - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_LAZY_THUNK: forced := new_ir_var("_forced", irv_type(var_idx)) emit(IR_LAZY_FORCE, forced, var_idx, 0, 0, 0) @@ -1317,20 +1317,20 @@ fn dex_align_call_args(cfi: int, ac: int, arg_vars: string, arg_nodes: string, a // 泛型声明的 `T?`(inner = 泛型参数)判据为假 -> 调用点在重定向后 // 用实例参数列表(inner = 具体 dex)再跑一遍本环。 if ast_type_val(cpn) == TI_DEX || (cfn_ext == 0 && dex_opt_type_node(ast_data(cpn)) != 0): - av := r64(arg_vars, cpi * 8) + av := cell_load(arg_vars, cpi * 8) if av >= 0: if cfn_ext != 0: // extern:scaled -> bits(字面量直接重发射位模式常量) if irv_type(av) == TI_DEX_S: arg_node : ., mut = -1 if cpi < arg_nodes_cap: - arg_node = r64(arg_nodes, cpi * 8) + arg_node = cell_load(arg_nodes, cpi * 8) av = dex_scaled_to_bits(av, arg_node) - w64(arg_vars, cpi * 8, av) + cell_store(arg_vars, cpi * 8, av) else if irv_type(av) == TI_DEX: // Core 函数:apx bits -> scaled av = dex_bits_to_scaled(av) - w64(arg_vars, cpi * 8, av) + cell_store(arg_vars, cpi * 8, av) cpi = cpi + 1 cpn = cpn + 1 loop: @@ -1373,7 +1373,7 @@ fn dex_decl_form_of_expr(node: int) -> int: loop: if gi >= g_global_let_count: break - lnode := r64(g_global_lets, gi * 8) + lnode := cell_load(g_global_lets, gi * 8) if ast_a(lnode) == ni: tn := ast_b(lnode) if tn >= 0 && ast_kind(tn) == EXPR_ARRAY: @@ -2195,17 +2195,17 @@ fn gen_expr(node: int) -> int: if ac >= arg_vars_cap: nc := arg_vars_cap * 2 nb := alloc(nc * 8) - _dyncpy(arg_vars, arg_vars_cap * 8, nb) + cell_copy(arg_vars, arg_vars_cap * 8, nb) arg_vars = nb arg_vars_cap = nc - w64(arg_vars, ac * 8, self_var) + cell_store(arg_vars, ac * 8, self_var) if ac >= arg_nodes_cap: nc := arg_nodes_cap * 2 nb := alloc(nc * 8) - _dyncpy(arg_nodes, arg_nodes_cap * 8, nb) + cell_copy(arg_nodes, arg_nodes_cap * 8, nb) arg_nodes = nb arg_nodes_cap = nc - w64(arg_nodes, ac * 8, obj_node) + cell_store(arg_nodes, ac * 8, obj_node) ac = ac + 1 else if ast_kind(func_node) == EXPR_IDENT: func_ni = ast_int_val(func_node) @@ -2220,17 +2220,17 @@ fn gen_expr(node: int) -> int: if ac >= arg_vars_cap: nc := arg_vars_cap * 2 nb := alloc(nc * 8) - _dyncpy(arg_vars, arg_vars_cap * 8, nb) + cell_copy(arg_vars, arg_vars_cap * 8, nb) arg_vars = nb arg_vars_cap = nc - w64(arg_vars, ac * 8, arg_var) + cell_store(arg_vars, ac * 8, arg_var) if ac >= arg_nodes_cap: nc := arg_nodes_cap * 2 nb := alloc(nc * 8) - _dyncpy(arg_nodes, arg_nodes_cap * 8, nb) + cell_copy(arg_nodes, arg_nodes_cap * 8, nb) arg_nodes = nb arg_nodes_cap = nc - w64(arg_nodes, ac * 8, ast_a(an)) + cell_store(arg_nodes, ac * 8, ast_a(an)) ac = ac + 1 an = ast_b(an) // dex 边界规则:dex 参数在调用点按被调方形式对齐, @@ -2273,14 +2273,14 @@ fn gen_expr(node: int) -> int: if biv > 0: bs := biv - 1 if bs < g_gen_binds_count: - bcnt := r64(g_gen_binds, bs * 8) + bcnt := cell_load(g_gen_binds, bs * 8) if bcnt == gc_g && bs + 1 + bcnt <= g_gen_binds_count: ok_b : ., mut = 1 gi_b : ., mut = 0 loop: if gi_b >= bcnt: break - bt := r64(g_gen_binds, (bs + 1 + gi_b) * 8) + bt := cell_load(g_gen_binds, (bs + 1 + gi_b) * 8) if bt < 0: ok_b = 0 break @@ -2304,7 +2304,7 @@ fn gen_expr(node: int) -> int: loop: if ai >= ac: break - av := r64(arg_vars, ai * 8) + av := cell_load(arg_vars, ai * 8) ti := TI_INT if av >= 0: ti = irv_type(av) @@ -2354,13 +2354,13 @@ fn gen_expr(node: int) -> int: first_arg_var := -1 need_pack : ., mut = 0 if ac > 0: - prev := r64(arg_vars, 0 * 8) + prev := cell_load(arg_vars, 0 * 8) first_arg_var = prev ai : ., mut = 1 loop: if ai >= ac: break - cur := r64(arg_vars, ai * 8) + cur := cell_load(arg_vars, ai * 8) if cur != prev + 1: need_pack = 1 break @@ -2371,7 +2371,7 @@ fn gen_expr(node: int) -> int: loop: if ai >= ac: break - av := r64(arg_vars, ai * 8) + av := cell_load(arg_vars, ai * 8) at := TI_INT if av >= 0: at = irv_type(av) @@ -2415,10 +2415,10 @@ fn gen_expr(node: int) -> int: break if ai3 >= g_optrep_arg_count: break - av3 := r64(arg_vars, ai3 * 8) + av3 := cell_load(arg_vars, ai3 * 8) an3 : ., mut = -1 if ai3 < arg_nodes_cap: - an3 = r64(arg_nodes, ai3 * 8) + an3 = cell_load(arg_nodes, ai3 * 8) emit_rep_set(g_optrep_arg_cell0 + ai3, rep_enc_of_expr(an3, av3)) ai3 = ai3 + 1 emit(IR_CALL, dest, first_arg_var, ac, func_ni, call_ti) @@ -2434,7 +2434,7 @@ fn gen_expr(node: int) -> int: call_fi := find_func(func_ni) if call_fi >= 0 && fi_ispure(call_fi) != 0: grow_var_use_count(dest + 1) - use_count := r64(g_var_use_count, dest * 8) + use_count := cell_load(g_var_use_count, dest * 8) if use_count <= 1: thunk_var := new_ir_var("_lazy", irv_type(dest)) emit(IR_LAZY_THUNK, thunk_var, dest, 0, 0, 0) @@ -2455,7 +2455,7 @@ fn gen_expr(node: int) -> int: loop: if i >= stmt_count: break - sn := r64(g_block_stmts, (stmt_start + i) * 8) + sn := cell_load(g_block_stmts, (stmt_start + i) * 8) last = gen_expr(sn) i = i + 1 pop_ir_scope() @@ -2497,7 +2497,7 @@ fn gen_expr(node: int) -> int: // header lands on the region enter (region iteration in interp.cr). sg_alloc_push(SG_LOOP) arena_var := new_ir_var("_arena", TI_INT) - w64(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) + cell_store(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) emit(IR_LABEL, -1, header_lbl, 0, 0, 0) emit(IR_JUMP, -1, body_lbl, 0, 0, 0) emit(IR_LABEL, -1, body_lbl, 0, 0, 0) @@ -2511,7 +2511,7 @@ fn gen_expr(node: int) -> int: gen_expr(ast_a(node)) pop_loop_labels() pop_ir_scope() - total := r64(g_sg_alloc_total, g_sg_count - 1) + total := cell_load(g_sg_alloc_total, g_sg_count - 1) if total > 0: iri_set_s1(arena_instr, total) emit(IR_LABEL, -1, post_lbl, 0, 0, 0) @@ -2533,7 +2533,7 @@ fn gen_expr(node: int) -> int: // can add the termination dependency and advance the state chain. sg_push(SG_LOOP) arena_var := new_ir_var("_arena", TI_INT) - w64(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) + cell_store(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) emit(IR_LABEL, -1, header_lbl, 0, 0, 0) cond_var := gen_expr(cond) cond_var = force_if_thunk(cond_var) @@ -2548,7 +2548,7 @@ fn gen_expr(node: int) -> int: gen_expr(body) pop_loop_labels() pop_ir_scope() - total := r64(g_sg_alloc_total, g_sg_count - 1) + total := cell_load(g_sg_alloc_total, g_sg_count - 1) if total > 0: iri_set_s1(arena_instr, total) emit(IR_LABEL, -1, post_lbl, 0, 0, 0) @@ -2590,7 +2590,7 @@ fn gen_expr(node: int) -> int: // drive loop iteration from region boundaries (region iteration). sg_alloc_push(SG_FOR) arena_var := new_ir_var("_arena", TI_INT) - w64(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) + cell_store(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) // Header: check ivar < end, branch to exit if false emit(IR_LABEL, -1, header_lbl, 0, 0, 0) cond_var := new_ir_var("for_cond", TI_INT) @@ -2608,7 +2608,7 @@ fn gen_expr(node: int) -> int: gen_expr(body) pop_loop_labels() pop_ir_scope() - total := r64(g_sg_alloc_total, g_sg_count - 1) + total := cell_load(g_sg_alloc_total, g_sg_count - 1) if total > 0: iri_set_s1(arena_instr, total) // Increment ivar and jump to header (arena reset runs on every path, @@ -2839,7 +2839,7 @@ fn gen_expr(node: int) -> int: if ast_kind(type_node) == 19: sz := ast_int_val(type_node) if sz > 0: - emit(IR_ALLOC_ARRAY, var, sz, 8, 0, 0) + emit(IR_ALLOC_ARRAY, var, sz, mp_stride(TI_INT), 0, 0) is_arr = 1 // F1:记录数组类型(TYP_ARRAY extra=size),直接索引越界 // 检查(arr_len_lit_of)依赖该长度信息。 @@ -3182,11 +3182,11 @@ fn gen_expr(node: int) -> int: // Break / Continue if ast_kind(node) == EXPR_BREAK: if g_ir_loop_depth > 0: - emit(IR_JUMP, -1, r64(g_ir_loop_exit, (g_ir_loop_depth - 1) * 8), 0, 0, 0) + emit(IR_JUMP, -1, cell_load(g_ir_loop_exit, (g_ir_loop_depth - 1) * 8), 0, 0, 0) return -1 if ast_kind(node) == EXPR_CONTINUE: if g_ir_loop_depth > 0: - emit(IR_JUMP, -1, r64(g_ir_loop_header, (g_ir_loop_depth - 1) * 8), 0, 0, 0) + emit(IR_JUMP, -1, cell_load(g_ir_loop_header, (g_ir_loop_depth - 1) * 8), 0, 0, 0) return -1 if ast_kind(node) == EXPR_WILDCARD: @@ -3198,11 +3198,11 @@ fn gen_expr(node: int) -> int: if ast_kind(node) == EXPR_UNSAFE: sg_alloc_push(SG_UNSAFE) arena_var := new_ir_var("_arena", TI_INT) - w64(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) + cell_store(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) emit(IR_ARENA_NEW, arena_var, 0, 0, 0, 0) arena_instr := g_ir_instr_count - 1 ret := gen_expr(ast_a(node)) - total := r64(g_sg_alloc_total, g_sg_count - 1) + total := cell_load(g_sg_alloc_total, g_sg_count - 1) if total > 0: iri_set_s1(arena_instr, total) sg_alloc_pop() @@ -3260,7 +3260,7 @@ fn gen_expr(node: int) -> int: elem_idx := ast_a(node) ec : ., mut = ast_b(node) tv := new_ir_var("tuple", TI_INT) - emit(IR_ALLOC_ARRAY, tv, ec, 0, 8, 0) // alloc N * 8 bytes + emit(IR_ALLOC_ARRAY, tv, ec, 0, mp_stride(TI_INT), 0) // target slot stride // Store each element at its offset e : ., mut = 0 loop: @@ -3301,11 +3301,11 @@ fn ir_gen_func(fi: int): // Record function metadata func_idx := g_ir_func_count grow_ir_func_meta(func_idx + 1) - w64(g_ir_func_name_idx, func_idx * 8, name_idx) - w64(g_ir_func_ret_type, func_idx * 8, ret_ti) - w64(g_ir_func_instr_start, func_idx * 8, g_ir_instr_count) - w64(g_ir_func_var_start, func_idx * 8, g_ir_var_count) - w64(g_ir_func_param_count, func_idx * 8, param_count) + cell_store(g_ir_func_name_idx, func_idx * 8, name_idx) + cell_store(g_ir_func_ret_type, func_idx * 8, ret_ti) + cell_store(g_ir_func_instr_start, func_idx * 8, g_ir_instr_count) + cell_store(g_ir_func_var_start, func_idx * 8, g_ir_var_count) + cell_store(g_ir_func_param_count, func_idx * 8, param_count) // Create IR vars for params pi : ., mut = 0 @@ -3411,9 +3411,9 @@ fn ir_gen_func(fi: int): // Function-level arena (df_begin_func already pushed SG_FUNC) grow_sg_alloc(g_sg_count + 1) grow_sg_arena_var(g_sg_count + 1) - w64(g_sg_alloc_total, (g_sg_count - 1) * 8, 0) + cell_store(g_sg_alloc_total, (g_sg_count - 1) * 8, 0) arena_var := new_ir_var("_arena", TI_INT) - w64(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) + cell_store(g_sg_arena_var, (g_sg_count - 1) * 8, arena_var) emit(IR_ARENA_NEW, arena_var, 0, 0, 0, 0) arena_instr := g_ir_instr_count - 1 @@ -3450,7 +3450,7 @@ fn ir_gen_func(fi: int): g_cur_ret_dex_opt = 0 // Patch arena size and reset before return - total := r64(g_sg_alloc_total, (g_sg_count - 1) * 8) + total := cell_load(g_sg_alloc_total, (g_sg_count - 1) * 8) if total > 0: iri_set_s1(arena_instr, total) emit(IR_ARENA_RESET, -1, arena_var, 0, 0, 0) @@ -3461,8 +3461,8 @@ fn ir_gen_func(fi: int): emit_rep_set(g_optrep_ret_cell, oe_boxed()) emit(IR_RETURN, -1, -1, 0, 0, 0) - w64(g_ir_func_instr_count, func_idx * 8, g_ir_instr_count - r64(g_ir_func_instr_start, func_idx * 8)) - w64(g_ir_func_var_count, func_idx * 8, g_ir_var_count - r64(g_ir_func_var_start, func_idx * 8)) + cell_store(g_ir_func_instr_count, func_idx * 8, g_ir_instr_count - cell_load(g_ir_func_instr_start, func_idx * 8)) + cell_store(g_ir_func_var_count, func_idx * 8, g_ir_var_count - cell_load(g_ir_func_var_start, func_idx * 8)) g_ir_func_count = func_idx + 1 // --- Initialize global IR vars from global lets --- @@ -3477,7 +3477,7 @@ fn global_init_val(name_idx: int) -> int: loop: if i < 0: break - node := r64(g_global_lets, i * 8) + node := cell_load(g_global_lets, i * 8) if ast_a(node) == name_idx: value_node := ast_c(node) if value_node >= 0: @@ -3512,8 +3512,8 @@ fn agg_elem_count_of(tn: int) -> int: loop: if ai >= g_type_alias_count: break - if r64(g_type_aliases, ai * 16) == ni: - rt := r64(g_type_aliases, ai * 16 + 8) + if cell_load(g_type_aliases, ai * 16) == ni: + rt := cell_load(g_type_aliases, ai * 16 + 8) if rt >= 0 && ast_kind(rt) == EXPR_ARRAY: return ast_int_val(rt) break @@ -3529,7 +3529,7 @@ fn global_needs_runtime_init(name_idx: int) -> int: loop: if i < 0: break - node := r64(g_global_lets, i * 8) + node := cell_load(g_global_lets, i * 8) if ast_a(node) == name_idx: // 容量批 T2:可选全局槽必须走运行期初始化,静态度量(.quad 裸值)写进槽后 // 读侧按装箱解引用 -> 双路径 139(run-time failure)。装箱在 inject_global_inits @@ -3569,8 +3569,8 @@ fn global_var_of(name_idx: int) -> int: loop: if gi >= g_ir_global_count: break - if r64(g_ir_globals, gi * 24) == name_idx: - return r64(g_ir_globals, gi * 24 + 8) + if cell_load(g_ir_globals, gi * 24) == name_idx: + return cell_load(g_ir_globals, gi * 24 + 8) gi = gi + 1 return -1 @@ -3581,7 +3581,7 @@ fn inject_global_inits(): loop: if i >= g_global_let_count: break - lnode := r64(g_global_lets, i * 8) + lnode := cell_load(g_global_lets, i * 8) name_idx := ast_a(lnode) if global_needs_runtime_init(name_idx) != 0: gv := global_var_of(name_idx) @@ -3618,7 +3618,7 @@ fn reg_one_global(name_idx: int): loop: if gi >= g_ir_global_count: break - if r64(g_ir_globals, gi * 24) == name_idx: + if cell_load(g_ir_globals, gi * 24) == name_idx: found = 1 break gi = gi + 1 @@ -3632,7 +3632,7 @@ fn reg_one_global(name_idx: int): loop: if gli >= g_global_let_count: break - lnode := r64(g_global_lets, gli * 8) + lnode := cell_load(g_global_lets, gli * 8) if ast_a(lnode) == name_idx: ltn := ast_b(lnode) if ltn >= 0 && ast_kind(ltn) == 0 && ast_type_val(ltn) == TY_DEX: @@ -3649,8 +3649,8 @@ fn reg_one_global(name_idx: int): gli = gli + 1 gvar := new_ir_var(name, gtype) grow_ir_globals(g_ir_global_count + 1) - w64(g_ir_globals, g_ir_global_count * 24, name_idx) - w64(g_ir_globals, g_ir_global_count * 24 + 8, gvar) + cell_store(g_ir_globals, g_ir_global_count * 24, name_idx) + cell_store(g_ir_globals, g_ir_global_count * 24 + 8, gvar) // Const initializer — emitted by the ELF backend's _init_globals. iv : int = global_init_val(name_idx) if is_dex_global != 0 && gtype == TI_DEX: @@ -3659,14 +3659,14 @@ fn reg_one_global(name_idx: int): loop: if i2 >= g_global_let_count: break - lnode2 := r64(g_global_lets, i2 * 8) + lnode2 := cell_load(g_global_lets, i2 * 8) if ast_a(lnode2) == name_idx: vn := ast_c(lnode2) if vn >= 0 && ast_kind(vn) == EXPR_DEX: iv = ast_a(vn) break i2 = i2 + 1 - w64(g_ir_globals, g_ir_global_count * 24 + 16, iv) + cell_store(g_ir_globals, g_ir_global_count * 24 + 16, iv) g_ir_global_count = g_ir_global_count + 1 fn ir_gen_globals(): @@ -3677,7 +3677,7 @@ fn ir_gen_globals(): loop: if i >= g_global_let_count: break - node := r64(g_global_lets, i * 8) + node := cell_load(g_global_lets, i * 8) reg_one_global(ast_a(node)) i = i + 1 @@ -3745,7 +3745,7 @@ fn grow_addr_taken(needed: int): if nc < needed: nc = needed + 32 nb := alloc(nc * 8) - _dyncpy(g_addr_taken_names, g_addr_taken_cap * 8, nb) + cell_copy(g_addr_taken_names, g_addr_taken_cap * 8, nb) g_addr_taken_names = nb g_addr_taken_cap = nc fn addr_taken_add(ni: int): @@ -3755,7 +3755,7 @@ fn addr_taken_add(ni: int): return // 去重(线性扫;量级 = 源码里 &ident 的条数) grow_addr_taken(g_addr_taken_count + 1) - w64(g_addr_taken_names, g_addr_taken_count * 8, ni) + cell_store(g_addr_taken_names, g_addr_taken_count * 8, ni) g_addr_taken_count = g_addr_taken_count + 1 fn addr_taken_of(ni: int) -> int: if ni < 0: @@ -3764,7 +3764,7 @@ fn addr_taken_of(ni: int) -> int: loop: if i >= g_addr_taken_count: return 0 - if r64(g_addr_taken_names, i * 8) == ni: + if cell_load(g_addr_taken_names, i * 8) == ni: return 1 i = i + 1 return 0 @@ -3806,9 +3806,9 @@ fn optrep_prescan(): fn reg_hidden_global(name_ni: int) -> int: gvar := new_ir_var(istr_get(name_ni), TI_INT) grow_ir_globals(g_ir_global_count + 1) - w64(g_ir_globals, g_ir_global_count * 24, name_ni) - w64(g_ir_globals, g_ir_global_count * 24 + 8, gvar) - w64(g_ir_globals, g_ir_global_count * 24 + 16, 0) + cell_store(g_ir_globals, g_ir_global_count * 24, name_ni) + cell_store(g_ir_globals, g_ir_global_count * 24 + 8, gvar) + cell_store(g_ir_globals, g_ir_global_count * 24 + 16, 0) g_ir_global_count = g_ir_global_count + 1 return gvar @@ -3862,11 +3862,11 @@ fn ast_patch_node(node: int, subst_from: string, subst_to: string): loop: if dci >= flen: break - if load8(data_str, dci) != load8(subst_from, dci): + if cell_load_byte(data_str, dci) != cell_load_byte(subst_from, dci): matches = 0 break dci = dci + 1 - if matches != 0 && (dlen == flen || load8(data_str, flen) == 46): + if matches != 0 && (dlen == flen || cell_load_byte(data_str, flen) == 46): rest := str_sub(data_str, flen, dlen - flen) new_name := subst_to + rest ast_set_data(node, str_intern(new_name)) @@ -3878,7 +3878,7 @@ fn ast_patch_node(node: int, subst_from: string, subst_to: string): loop: if i2 >= sc: break - sn2 := r64(g_block_stmts, (ss + i2) * 8) + sn2 := cell_load(g_block_stmts, (ss + i2) * 8) ast_patch_node(sn2, subst_from, subst_to) i2 = i2 + 1 else if k == EXPR_IF || k == EXPR_LOOP || k == EXPR_WHILE || k == EXPR_UNSAFE: @@ -4077,7 +4077,7 @@ fn ccr_source_has_marker(marker: string) -> int: loop: if j >= ml: break - if load8(g_source, i + j) != load8(marker, j): + if cell_load_byte(g_source, i + j) != cell_load_byte(marker, j): break j = j + 1 if j >= ml: @@ -4092,7 +4092,7 @@ fn ccr_source_dir_is_compiler() -> int: return 1 if n >= 12 && str_eq(str_sub(d, n - 12, 12), "src/compiler") != 0: return 1 - if n >= 13 && load8(d, n - 1) == 47: + if n >= 13 && cell_load_byte(d, n - 1) == 47: if str_eq(str_sub(d, n - 13, 12), "src/compiler") != 0: return 1 return 0 @@ -4170,7 +4170,7 @@ fn func_fingerprint(func_node: int) -> int: loop: if source_pos >= source_len: break - source_hash = source_hash * 16777619 + load8(g_source, source_pos) + source_hash = source_hash * 16777619 + cell_load_byte(g_source, source_pos) source_pos = source_pos + 1 g_ir_source_hash = source_hash g_ir_source_hash_ready = 1 @@ -4199,7 +4199,7 @@ fn sig_fingerprint(func_node: int) -> int: loop: if ni >= str_len(name): break - h = h * 16777619 + (load8(name, ni) % 256) + h = h * 16777619 + (cell_load_byte(name, ni) % 256) ni = ni + 1 // Param types param := ast_b(func_node) diff --git a/src/compiler/lexer.cr b/src/compiler/lexer.cr index 881537d2..04c98d33 100644 --- a/src/compiler/lexer.cr +++ b/src/compiler/lexer.cr @@ -79,7 +79,7 @@ fn is_ident_char(c: int) -> int: fn add_error(msg: string): mi := str_intern(msg) grow_errors(g_error_count + 1) - w64(g_errors, g_error_count * 8, mi) + cell_store(g_errors, g_error_count * 8, mi) g_error_count = g_error_count + 1 fn cur_char() -> int: @@ -90,22 +90,22 @@ fn cur_char() -> int: src_len := str_len(g_source) if g_pos >= src_len: return 0 - return load8(g_source, g_pos) + return cell_load_byte(g_source, g_pos) fn peek() -> int: src_len := str_len(g_source) if g_pos + 1 >= src_len: return 0 - return load8(g_source, g_pos + 1) + return cell_load_byte(g_source, g_pos + 1) fn cur_char_at(src: string, pos: int, max_len: int) -> int: if pos >= max_len: return 0 - return load8(src, pos) + return cell_load_byte(src, pos) fn peek_at(src: string, pos: int, max_len: int) -> int: if pos + 1 >= max_len: return 0 - return load8(src, pos + 1) + return cell_load_byte(src, pos + 1) fn lookup_keyword(s: string) -> int: if s == "fn": @@ -202,8 +202,8 @@ fn str_int_literal(s: string) -> int: sl := str_len(s) base : ., mut = 10 start : ., mut = 0 - if sl >= 2 && load8(s, 0) == 48: - pfx := load8(s, 1) + if sl >= 2 && cell_load_byte(s, 0) == 48: + pfx := cell_load_byte(s, 1) if pfx == 120 || pfx == 88: base = 16 start = 2 @@ -219,7 +219,7 @@ fn str_int_literal(s: string) -> int: loop: if i >= sl: break - c := load8(s, i) + c := cell_load_byte(s, i) if c == 95: i = i + 1 continue @@ -269,7 +269,7 @@ fn str_to_scaled(s: string) -> int: sl := str_len(s) i : ., mut = 0 neg : int = 0 - if i < sl && load8(s, i) == 45: + if i < sl && cell_load_byte(s, i) == 45: neg = 1 i = i + 1 ip : int = 0 @@ -280,7 +280,7 @@ fn str_to_scaled(s: string) -> int: loop: if i >= sl: break - c := load8(s, i) + c := cell_load_byte(s, i) if c == 46: sd = 1 i = i + 1 @@ -318,43 +318,43 @@ fn str_to_scaled(s: string) -> int: fn add_tok(kind: int, lex: int, start_line: int, start_col: int): grow_tokens(g_token_count + 1) tp := g_token_count * ESZ_TOKEN - w64(g_tokens, tp + OFF_TK_KIND, kind) - w64(g_tokens, tp + OFF_TK_LEXEME, lex) - w64(g_tokens, tp + OFF_TK_INTVAL, 0) - w64(g_tokens, tp + OFF_TK_LINE, start_line) - w64(g_tokens, tp + OFF_TK_COL, start_col) + cell_store(g_tokens, tp + OFF_TK_KIND, kind) + cell_store(g_tokens, tp + OFF_TK_LEXEME, lex) + cell_store(g_tokens, tp + OFF_TK_INTVAL, 0) + cell_store(g_tokens, tp + OFF_TK_LINE, start_line) + cell_store(g_tokens, tp + OFF_TK_COL, start_col) g_token_count = g_token_count + 1 fn add_tok_int(kind: int, ival: int, start_line: int, start_col: int): grow_tokens(g_token_count + 1) tp := g_token_count * ESZ_TOKEN - w64(g_tokens, tp + OFF_TK_KIND, kind) - w64(g_tokens, tp + OFF_TK_LEXEME, -1) - w64(g_tokens, tp + OFF_TK_INTVAL, ival) - w64(g_tokens, tp + OFF_TK_LINE, start_line) - w64(g_tokens, tp + OFF_TK_COL, start_col) + cell_store(g_tokens, tp + OFF_TK_KIND, kind) + cell_store(g_tokens, tp + OFF_TK_LEXEME, -1) + cell_store(g_tokens, tp + OFF_TK_INTVAL, ival) + cell_store(g_tokens, tp + OFF_TK_LINE, start_line) + cell_store(g_tokens, tp + OFF_TK_COL, start_col) g_token_count = g_token_count + 1 fn add_tok_str(kind: int, s: string, start_line: int, start_col: int): grow_tokens(g_token_count + 1) tp := g_token_count * ESZ_TOKEN - w64(g_tokens, tp + OFF_TK_KIND, kind) + cell_store(g_tokens, tp + OFF_TK_KIND, kind) si := str_intern(s) - w64(g_tokens, tp + OFF_TK_LEXEME, si) - w64(g_tokens, tp + OFF_TK_INTVAL, 0) - w64(g_tokens, tp + OFF_TK_LINE, start_line) - w64(g_tokens, tp + OFF_TK_COL, start_col) + cell_store(g_tokens, tp + OFF_TK_LEXEME, si) + cell_store(g_tokens, tp + OFF_TK_INTVAL, 0) + cell_store(g_tokens, tp + OFF_TK_LINE, start_line) + cell_store(g_tokens, tp + OFF_TK_COL, start_col) g_token_count = g_token_count + 1 // Token with both int_val and lexeme, used for dex literals: int_val is the scaled integer and lexeme is the source digits. fn add_tok_int_lex(kind: int, ival: int, lexeme_ni: int, start_line: int, start_col: int): grow_tokens(g_token_count + 1) tp := g_token_count * ESZ_TOKEN - w64(g_tokens, tp + OFF_TK_KIND, kind) - w64(g_tokens, tp + OFF_TK_LEXEME, lexeme_ni) - w64(g_tokens, tp + OFF_TK_INTVAL, ival) - w64(g_tokens, tp + OFF_TK_LINE, start_line) - w64(g_tokens, tp + OFF_TK_COL, start_col) + cell_store(g_tokens, tp + OFF_TK_KIND, kind) + cell_store(g_tokens, tp + OFF_TK_LEXEME, lexeme_ni) + cell_store(g_tokens, tp + OFF_TK_INTVAL, ival) + cell_store(g_tokens, tp + OFF_TK_LINE, start_line) + cell_store(g_tokens, tp + OFF_TK_COL, start_col) g_token_count = g_token_count + 1 // Layout helpers. Anchor computation and the colon end-of-line check must share @@ -418,7 +418,7 @@ fn colon_at_line_end(src: string, pos: int, max_len: int) -> int: return 0 fn ind_top() -> int: - return r64(g_indent_stack, (g_indent_len - 1) * 8) + return cell_load(g_indent_stack, (g_indent_len - 1) * 8) fn ind_push(col: int): if g_indent_len >= g_indent_cap: @@ -427,10 +427,10 @@ fn ind_push(col: int): ncap = 16 nv := alloc(ncap * 8) if g_indent_cap > 0: - _dyncpy(g_indent_stack, g_indent_cap * 8, nv) + cell_copy(g_indent_stack, g_indent_cap * 8, nv) g_indent_stack = nv g_indent_cap = ncap - w64(g_indent_stack, g_indent_len * 8, col) + cell_store(g_indent_stack, g_indent_len * 8, col) g_indent_len = g_indent_len + 1 // Emit NEWLINE and any INDENT/DEDENT when crossing a physical line outside @@ -805,7 +805,7 @@ fn tokenize(_src: string, layout_bypass: int): cflag = 0 ti := g_token_count add_tok_str(T_INTERP, str_sub(_src, hs, hp - hs), start_line, start_col + 1 + (hs - sp)) - w64(g_tokens, ti * ESZ_TOKEN + OFF_TK_INTVAL, cflag) + cell_store(g_tokens, ti * ESZ_TOKEN + OFF_TK_INTVAL, cflag) _pos = hp if cflag != 0: _pos = _pos + 1 @@ -829,11 +829,11 @@ fn tokenize(_src: string, layout_bypass: int): loop: if ri >= g_token_count: break - iv : ., mut = r64(g_tokens, ri * ESZ_TOKEN + OFF_TK_INTVAL) + iv : ., mut = cell_load(g_tokens, ri * ESZ_TOKEN + OFF_TK_INTVAL) iv = iv + 1 if ri + 1 < g_token_count: iv = iv + 2 - w64(g_tokens, ri * ESZ_TOKEN + OFF_TK_INTVAL, iv) + cell_store(g_tokens, ri * ESZ_TOKEN + OFF_TK_INTVAL, iv) ri = ri + 1 _pos = skip_ws(_src, _pos, _slen) continue diff --git a/src/compiler/literal.cr b/src/compiler/literal.cr index 01c4af57..8d647ee9 100644 --- a/src/compiler/literal.cr +++ b/src/compiler/literal.cr @@ -21,7 +21,7 @@ fn str_to_f64_bits(s: string) -> int: sl := str_len(s) i : ., mut = 0 neg : int = 0 - if i < sl && load8(s, i) == 45: + if i < sl && cell_load_byte(s, i) == 45: neg = 1 i = i + 1 ip : int = 0 @@ -34,7 +34,7 @@ fn str_to_f64_bits(s: string) -> int: loop: if i >= sl: break - c := load8(s, i) + c := cell_load_byte(s, i) if c == 46: sd = 1 i = i + 1 diff --git a/src/compiler/main.cr b/src/compiler/main.cr index 41fd1e1b..127850bb 100644 --- a/src/compiler/main.cr +++ b/src/compiler/main.cr @@ -38,19 +38,19 @@ fn is_decl_stmt(s: string) -> int: loop: if i >= slen: return 0 - cb := load8(s, i) + cb := cell_load_byte(s, i) if cb == 32 || cb == 9 || cb == 10 || cb == 13: i = i + 1 else: break if i < slen: - cb := load8(s, i) + cb := cell_load_byte(s, i) if (cb >= 97 && cb <= 122) || (cb >= 65 && cb <= 90) || cb == 95: j : ., mut = i + 1 loop: if j >= slen: return 0 - c2b := load8(s, j) + c2b := cell_load_byte(s, j) if c2b == 32 || c2b == 9 || c2b == 10 || c2b == 13: j = j + 1 else if c2b == 58: @@ -69,7 +69,7 @@ fn detect_imports(src: string) -> int: loop: if i + 6 >= sl: break - if load8(src,i) == 105 && load8(src,i+1) == 109 && load8(src,i+2) == 112 && load8(src,i+3) == 111 && load8(src,i+4) == 114 && load8(src,i+5) == 116: + if cell_load_byte(src,i) == 105 && cell_load_byte(src,i+1) == 109 && cell_load_byte(src,i+2) == 112 && cell_load_byte(src,i+3) == 111 && cell_load_byte(src,i+4) == 114 && cell_load_byte(src,i+5) == 116: cnt = cnt + 1 i = i + 6 continue @@ -109,14 +109,15 @@ fn read_source_or_project(src_path: string) -> int: return 0 fn read_static_runtime_source() -> string: - source := read_file("src/runtime/rt.cr") + rel := target_static_runtime_source() + source := read_file(rel) if str_len(source) > 0: return source exe_path := get_exe_path() exe_dir := dirname(exe_path) if str_len(exe_dir) > 0: - source = read_file(exe_dir + "../src/runtime/rt.cr") + source = read_file(exe_dir + "../" + rel) return source // Run the shared frontend pipeline: tokenize, resolve, parse, and check. @@ -153,7 +154,7 @@ fn run_frontend() -> int: loop: if di >= g_diag_count: break - ec := r64(g_diags, di * DIAG_REC_SIZE) + ec := cell_load(g_diags, di * DIAG_REC_SIZE) // A diagnostic outside the exemption table is blocking. if diag_gate_exempt(ec, GATE_SCOPE_BUILD) == 0: hard = 1 @@ -287,10 +288,10 @@ fn corec_main() -> int: loop: if si2 >= sl2: break - c0 := load8(g_source, si2) + c0 := cell_load_byte(g_source, si2) if c0 == 102: if si2 + 6 < sl2: - if load8(g_source, si2) == 102 && load8(g_source, si2 + 1) == 110 && load8(g_source, si2 + 2) == 32 && load8(g_source, si2 + 3) == 109 && load8(g_source, si2 + 4) == 97 && load8(g_source, si2 + 5) == 105 && load8(g_source, si2 + 6) == 110: + if cell_load_byte(g_source, si2) == 102 && cell_load_byte(g_source, si2 + 1) == 110 && cell_load_byte(g_source, si2 + 2) == 32 && cell_load_byte(g_source, si2 + 3) == 109 && cell_load_byte(g_source, si2 + 4) == 97 && cell_load_byte(g_source, si2 + 5) == 105 && cell_load_byte(g_source, si2 + 6) == 110: has_main = 1 break si2 = si2 + 1 @@ -304,16 +305,16 @@ fn corec_main() -> int: if ii >= ilen: break if ii + 6 < ilen: - c := load8(g_source, ii) + c := cell_load_byte(g_source, ii) c_prev : ., mut = 59 if ii > 0: - c_prev = load8(g_source, ii - 1) - if (ii == 0 || c_prev == 59 || c_prev == 10) && load8(g_source, ii) == 105 && load8(g_source, ii + 1) == 109 && load8(g_source, ii + 2) == 112 && load8(g_source, ii + 3) == 111 && load8(g_source, ii + 4) == 114 && load8(g_source, ii + 5) == 116: + c_prev = cell_load_byte(g_source, ii - 1) + if (ii == 0 || c_prev == 59 || c_prev == 10) && cell_load_byte(g_source, ii) == 105 && cell_load_byte(g_source, ii + 1) == 109 && cell_load_byte(g_source, ii + 2) == 112 && cell_load_byte(g_source, ii + 3) == 111 && cell_load_byte(g_source, ii + 4) == 114 && cell_load_byte(g_source, ii + 5) == 116: ij : ., mut = ii loop: if ij >= ilen: break - if load8(g_source, ij) == 59: + if cell_load_byte(g_source, ij) == 59: ij = ij + 1 break ij = ij + 1 @@ -355,7 +356,7 @@ fn corec_main() -> int: loop: if lci >= lclen: break - if load8(last_expr, lci) == 40: + if cell_load_byte(last_expr, lci) == 40: has_lcall = 1 break lci = lci + 1 @@ -375,7 +376,7 @@ fn corec_main() -> int: loop: if ci3 >= clen: break - if load8(g_source, ci3) == 40: + if cell_load_byte(g_source, ci3) == 40: has_call = 1 break ci3 = ci3 + 1 @@ -509,7 +510,7 @@ fn corec_main() -> int: loop: if ci >= str_len(func_id): break - c := load8(func_id, ci) + c := cell_load_byte(func_id, ci) if c == 47: cache_path = cache_path + "_" else: @@ -529,13 +530,13 @@ fn corec_main() -> int: // Cache hit: setup function metadata for restored data func_idx := g_ir_func_count grow_ir_func_meta(func_idx + 1) - w64(g_ir_func_name_idx, func_idx * 8, name_ni) - w64(g_ir_func_ret_type, func_idx * 8, fi_return_type(fi)) - w64(g_ir_func_instr_start, func_idx * 8, instr_start) - w64(g_ir_func_var_start, func_idx * 8, var_start) - w64(g_ir_func_param_count, func_idx * 8, ast_c(fn_node)) - w64(g_ir_func_instr_count, func_idx * 8, g_ir_instr_count - instr_start) - w64(g_ir_func_var_count, func_idx * 8, g_ir_var_count - var_start) + cell_store(g_ir_func_name_idx, func_idx * 8, name_ni) + cell_store(g_ir_func_ret_type, func_idx * 8, fi_return_type(fi)) + cell_store(g_ir_func_instr_start, func_idx * 8, instr_start) + cell_store(g_ir_func_var_start, func_idx * 8, var_start) + cell_store(g_ir_func_param_count, func_idx * 8, ast_c(fn_node)) + cell_store(g_ir_func_instr_count, func_idx * 8, g_ir_instr_count - instr_start) + cell_store(g_ir_func_var_count, func_idx * 8, g_ir_var_count - var_start) g_ir_func_count = func_idx + 1 df_end_func(ir_func_idx) @@ -676,16 +677,8 @@ fn corec_main() -> int: println("error: could not write .ccr") return 1 println("generate ELF...") - // Call corearch to produce ELF - cmd2 : ., mut = "corearch " - cmd2 = cmd2 + ccr_path + " --elf" - if g_opt_level > 0: - cmd2 = cmd2 + " --opt-level " + int_str(g_opt_level) - if cli_has("static") != 0: - cmd2 = cmd2 + " --static" - else: - cmd2 = cmd2 + " --link auto" - cmd2 = cmd2 + " -o " + out_path + // The target owns the backend name, ELF flag, link mode, and output flag. + cmd2 : ., mut = target_backend_command(ccr_path, out_path, cli_has("static"), g_opt_level) self_path := get_arg(0) sl2 := str_len(self_path) if sl2 > 0: @@ -694,7 +687,7 @@ fn corec_main() -> int: loop: if si >= sl2: break - if load8(self_path, si) == 47: + if cell_load_byte(self_path, si) == 47: last_slash = si si = si + 1 if last_slash >= 0: @@ -720,8 +713,8 @@ fn compile_source(source: string) -> string: loop: if ei >= g_diag_count: break - diag_code := r64(g_diags, ei * DIAG_REC_SIZE) - diag_msg := load_str_ptr(g_diags, ei * DIAG_REC_SIZE + 8) + diag_code := cell_load(g_diags, ei * DIAG_REC_SIZE) + diag_msg := cell_load_str(g_diags, ei * DIAG_REC_SIZE + 8) err_msg = err_msg + " [" + int_str(diag_code) + "] " + diag_msg ei = ei + 1 return err_msg diff --git a/src/compiler/mapping.cr b/src/compiler/mapping.cr index 52dedf32..1d1352ac 100644 --- a/src/compiler/mapping.cr +++ b/src/compiler/mapping.cr @@ -1,104 +1,23 @@ // === mapping.cr === -// Classic x86-64 mapping for value extents and slot strides. -// The semantic front end asks for sizes and offsets through these functions. -// A non-classic mapping can replace this file without changing the IR passes. +// Target-neutral mapping interface for value extents and slot strides. +// The classic x86-64 implementation lives in +// src/arch/x86_64/x86_mapping.cr. The front end must not read target +// layout constants directly; it asks through these functions. -MP_WORD_BYTES : int = 8 -MP_SLICE_BYTES : int = 16 - -// Value size in bytes. fn mp_size(ti: int) -> int: - if ti == TI_DEX_S: - return MP_WORD_BYTES - // TI_DEX_S uses an 8-byte slot and is not in the type table. Row 8 is a - // placeholder; user types start at 9. - if ti == TI_CHAR: - return 4 - // not in type table - if ti < 0 || ti >= g_type_count: - return MP_WORD_BYTES - k := get_type_kind(ti) - if k == TYP_BASE: - d := get_type_data(ti) - if d == TY_INT: - return MP_WORD_BYTES - if d == TY_DEX: - return MP_WORD_BYTES - if d == TY_BOOL: - return 1 - if d == TY_STRING: - return MP_WORD_BYTES - if d == TY_UNIT: - return 0 - if d == TY_CHAR: - return 4 - return MP_WORD_BYTES - if k == TYP_ARRAY: - elem := get_type_data(ti) - cnt := get_type_extra(ti) - return mp_size(elem) * cnt - if k == TYP_PTR || k == TYP_REF: - return MP_WORD_BYTES - if k == TYP_SLICE: - return MP_SLICE_BYTES - if k == TYP_NAMED: - name_ni := get_type_data(ti) - si : ., mut = 0 - loop: - if si >= g_struct_count: - break - if si_name(si) == name_ni: - return mp_extent(si_field_count(si), TI_INT) - si = si + 1 - return MP_WORD_BYTES - return MP_WORD_BYTES + return x86_mp_size(ti) -// Value alignment in bytes. fn mp_align(ti: int) -> int: - if ti == TI_DEX_S: - return MP_WORD_BYTES - // TI_DEX_S has 8-byte alignment and is not in the type table. Row 8 is a - // placeholder. - if ti == TI_CHAR: - return 4 - if ti < 0 || ti >= g_type_count: - return MP_WORD_BYTES - k := get_type_kind(ti) - if k == TYP_BASE: - d := get_type_data(ti) - if d == TY_INT: - return MP_WORD_BYTES - if d == TY_DEX: - return MP_WORD_BYTES - if d == TY_BOOL: - return 1 - if d == TY_STRING: - return MP_WORD_BYTES - if d == TY_CHAR: - return 4 - return MP_WORD_BYTES - if k == TYP_ARRAY: - return mp_align(get_type_data(ti)) - if k == TYP_PTR || k == TYP_REF: - return MP_WORD_BYTES - if k == TYP_SLICE: - return MP_WORD_BYTES - if k == TYP_NAMED: - return MP_WORD_BYTES - return MP_WORD_BYTES + return x86_mp_align(ti) -// Slot stride for an array element or an indexed pointer. fn mp_stride(ti: int) -> int: - return MP_WORD_BYTES + return x86_mp_stride(ti) -// Size of a block containing count slots. fn mp_extent(count: int, ti: int) -> int: - return count * mp_stride(ti) + return x86_mp_extent(count, ti) -// Byte offset of an indexed slot. fn mp_offset(index: int, ti: int) -> int: - return index * mp_stride(ti) + return x86_mp_offset(index, ti) -// Byte offset of a struct field. fn mp_field_offset(field_index: int) -> int: - return field_index * MP_WORD_BYTES + return x86_mp_field_offset(field_index) diff --git a/src/compiler/module.cr b/src/compiler/module.cr index 9e40c496..afc34e69 100644 --- a/src/compiler/module.cr +++ b/src/compiler/module.cr @@ -16,7 +16,7 @@ fn grow_open_docs(needed: int): if ncap < needed: ncap = needed + 8 nb := alloc(ncap * 24) - _dyncpy(g_open_docs, g_open_doc_cap * 24, nb) + cell_copy(g_open_docs, g_open_doc_cap * 24, nb) g_open_docs = nb g_open_doc_cap = ncap @@ -25,15 +25,15 @@ fn module_open_doc_set(path: string, src: string): loop: if i >= g_open_doc_count: break - if str_eq(load_str_ptr(g_open_docs, i * 24), path) != 0: - store_str_ptr(g_open_docs, i * 24 + 8, src) - w64(g_open_docs, i * 24 + 16, str_len(src)) + if str_eq(cell_load_str(g_open_docs, i * 24), path) != 0: + cell_store_str(g_open_docs, i * 24 + 8, src) + cell_store(g_open_docs, i * 24 + 16, str_len(src)) return i = i + 1 grow_open_docs(g_open_doc_count + 1) - store_str_ptr(g_open_docs, g_open_doc_count * 24, path) - store_str_ptr(g_open_docs, g_open_doc_count * 24 + 8, src) - w64(g_open_docs, g_open_doc_count * 24 + 16, str_len(src)) + cell_store_str(g_open_docs, g_open_doc_count * 24, path) + cell_store_str(g_open_docs, g_open_doc_count * 24 + 8, src) + cell_store(g_open_docs, g_open_doc_count * 24 + 16, str_len(src)) g_open_doc_count = g_open_doc_count + 1 fn module_open_doc_remove(path: string): @@ -41,14 +41,14 @@ fn module_open_doc_remove(path: string): loop: if i >= g_open_doc_count: break - if str_eq(load_str_ptr(g_open_docs, i * 24), path) != 0: + if str_eq(cell_load_str(g_open_docs, i * 24), path) != 0: j : ., mut = i + 1 loop: if j >= g_open_doc_count: break - store_str_ptr(g_open_docs, (j - 1) * 24, load_str_ptr(g_open_docs, j * 24)) - store_str_ptr(g_open_docs, (j - 1) * 24 + 8, load_str_ptr(g_open_docs, j * 24 + 8)) - w64(g_open_docs, (j - 1) * 24 + 16, r64(g_open_docs, j * 24 + 16)) + cell_store_str(g_open_docs, (j - 1) * 24, cell_load_str(g_open_docs, j * 24)) + cell_store_str(g_open_docs, (j - 1) * 24 + 8, cell_load_str(g_open_docs, j * 24 + 8)) + cell_store(g_open_docs, (j - 1) * 24 + 16, cell_load(g_open_docs, j * 24 + 16)) j = j + 1 g_open_doc_count = g_open_doc_count - 1 return @@ -60,8 +60,8 @@ fn module_get_source(path: string) -> string: loop: if i >= g_open_doc_count: break - if str_eq(load_str_ptr(g_open_docs, i * 24), path) != 0: - return load_str_ptr(g_open_docs, i * 24 + 8) + if str_eq(cell_load_str(g_open_docs, i * 24), path) != 0: + return cell_load_str(g_open_docs, i * 24 + 8) i = i + 1 return read_file(path) @@ -84,19 +84,19 @@ fn get_fileid(s: string) -> string: if i + 6 >= slen: return "" // Inline "fileid" check — int-based, no str_sub/str_eq - if load8(s, i) == 102 && load8(s, i+1) == 105 && load8(s, i+2) == 108 && load8(s, i+3) == 101 && load8(s, i+4) == 105 && load8(s, i+5) == 100: + if cell_load_byte(s, i) == 102 && cell_load_byte(s, i+1) == 105 && cell_load_byte(s, i+2) == 108 && cell_load_byte(s, i+3) == 101 && cell_load_byte(s, i+4) == 105 && cell_load_byte(s, i+5) == 100: j : ., mut = i + 6 loop: if j >= slen: return "" - if load8(s, j) == 34: + if cell_load_byte(s, j) == 34: // '"' start := j + 1 k : ., mut = start loop: if k >= slen: return "" - if load8(s, k) == 34: + if cell_load_byte(s, k) == 34: // '"' return str_sub(s, start, k - start) k = k + 1 @@ -179,12 +179,12 @@ fn reg_fileid(fileid_str: string, path: string) -> int: loop: if fi >= g_file_count: break - if r64(g_files, fi * 16) == fni: + if cell_load(g_files, fi * 16) == fni: return fni fi = fi + 1 grow_files(g_file_count + 1) - w64(g_files, g_file_count * 16, fni) - store_str_ptr(g_files, g_file_count * 16 + 8, path) + cell_store(g_files, g_file_count * 16, fni) + cell_store_str(g_files, g_file_count * 16 + 8, path) g_file_count = g_file_count + 1 return fni @@ -194,7 +194,7 @@ fn build_line_fileid(): slen := str_len(g_source) seg_idx : ., mut = 0 grow_line_file(g_line_count + 1) - w64(g_line_fileid, g_line_count * 8, r64(g_seg_fileids, 0)) + cell_store(g_line_fileid, g_line_count * 8, cell_load(g_seg_fileids, 0)) g_line_count = g_line_count + 1 pos : ., mut = 0 loop: @@ -204,12 +204,12 @@ fn build_line_fileid(): next_start := 0 next_fileid := -1 if seg_idx + 1 < g_seg_count: - next_start = r64(g_seg_starts, (seg_idx + 1) * 8) - next_fileid = r64(g_seg_fileids, (seg_idx + 1) * 8) + next_start = cell_load(g_seg_starts, (seg_idx + 1) * 8) + next_fileid = cell_load(g_seg_fileids, (seg_idx + 1) * 8) if next_fileid >= 0 && pos + 1 >= next_start: seg_idx = seg_idx + 1 grow_line_file(g_line_count + 1) - w64(g_line_fileid, g_line_count * 8, r64(g_seg_fileids, seg_idx * 8)) + cell_store(g_line_fileid, g_line_count * 8, cell_load(g_seg_fileids, seg_idx * 8)) g_line_count = g_line_count + 1 pos = pos + 1 @@ -230,7 +230,7 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if line_end >= sl: break - c := load8(index_content, line_end) + c := cell_load_byte(index_content, line_end) if c == 10: break // \n @@ -241,7 +241,7 @@ fn reg_so_funcs(index_content: string, so_name: string): // Skip empty lines and comments if str_len(line) == 0: continue - c0 := load8(line, 0) + c0 := cell_load_byte(line, 0) if c0 == 35 || c0 == 10 || c0 == 13: continue // #, \n, \r @@ -252,7 +252,7 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if ci >= str_len(line): break - if load8(line, ci) == 58: + if cell_load_byte(line, ci) == 58: colon_pos = ci break // : @@ -276,7 +276,7 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if ai + 2 > rl: break - if load8(rest, ai) == 45 && ai + 1 < rl && load8(rest, ai+1) == 62: + if cell_load_byte(rest, ai) == 45 && ai + 1 < rl && cell_load_byte(rest, ai+1) == 62: // -> arrow_pos = ai break @@ -293,7 +293,7 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if rci >= str_len(ret_str): break - if load8(ret_str, rci) == 44: + if cell_load_byte(ret_str, rci) == 44: ret_comma = rci break rci = rci + 1 @@ -308,7 +308,7 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if tri >= str_len(ret_type): break - tc := load8(ret_type, tri) + tc := cell_load_byte(ret_type, tri) if tc != 32: trim_rt = trim_rt + get_char(ret_type, tri) tri = tri + 1 @@ -324,19 +324,19 @@ fn reg_so_funcs(index_content: string, so_name: string): if pi >= pl: break // Skip whitespace - while pi < pl && load8(params_str, pi) == 32: + while pi < pl && cell_load_byte(params_str, pi) == 32: pi = pi + 1 if pi >= pl: break // Find comma or end item_end : ., mut = pi - while item_end < pl && load8(params_str, item_end) != 44: + while item_end < pl && cell_load_byte(params_str, item_end) != 44: item_end = item_end + 1 item := str_sub(params_str, pi, item_end - pi) // Trim whitespace - while str_len(item) > 0 && load8(item, 0) == 32: + while str_len(item) > 0 && cell_load_byte(item, 0) == 32: item = str_sub(item, 1, str_len(item)-1) - while str_len(item) > 0 && load8(item, str_len(item)-1) == 32: + while str_len(item) > 0 && cell_load_byte(item, str_len(item)-1) == 32: item = str_sub(item, 0, str_len(item)-1) if str_len(item) > 0: @@ -357,17 +357,17 @@ fn reg_so_funcs(index_content: string, so_name: string): loop: if ti >= tl: break - while ti < tl && load8(tags_str, ti) == 32: + while ti < tl && cell_load_byte(tags_str, ti) == 32: ti = ti + 1 if ti >= tl: break item_end2 : ., mut = ti - while item_end2 < tl && load8(tags_str, item_end2) != 44: + while item_end2 < tl && cell_load_byte(tags_str, item_end2) != 44: item_end2 = item_end2 + 1 item2 := str_sub(tags_str, ti, item_end2 - ti) - while str_len(item2) > 0 && load8(item2, 0) == 32: + while str_len(item2) > 0 && cell_load_byte(item2, 0) == 32: item2 = str_sub(item2, 1, str_len(item2)-1) - while str_len(item2) > 0 && load8(item2, str_len(item2)-1) == 32: + while str_len(item2) > 0 && cell_load_byte(item2, str_len(item2)-1) == 32: item2 = str_sub(item2, 0, str_len(item2)-1) if item2 == "variadic": tag_flags = tag_flags + TAG_VARIADIC @@ -384,12 +384,12 @@ fn reg_so_funcs(index_content: string, so_name: string): break // Skip to next param (comma-separated) pn_start : ., mut = pti - while pti < str_len(param_types) && load8(param_types, pti) != 44: + while pti < str_len(param_types) && cell_load_byte(param_types, pti) != 44: pti = pti + 1 pname := str_sub(param_types, pn_start, pti - pn_start) - while str_len(pname) > 0 && load8(pname, 0) == 32: + while str_len(pname) > 0 && cell_load_byte(pname, 0) == 32: pname = str_sub(pname, 1, str_len(pname)-1) - while str_len(pname) > 0 && load8(pname, str_len(pname)-1) == 32: + while str_len(pname) > 0 && cell_load_byte(pname, str_len(pname)-1) == 32: pname = str_sub(pname, 0, str_len(pname)-1) if str_len(pname) > 0 && param_count < 8: // Encode type: int=0, string=1, unit=2, dex=3(binary64 跨 C 边界 = apx 授权), bool=4, other=5 @@ -407,7 +407,7 @@ fn reg_so_funcs(index_content: string, so_name: string): // Decimal encoding: packed positionally param_type_bits = param_type_bits * 100 + ptype_code param_count = param_count + 1 - if pti < str_len(param_types) && load8(param_types, pti) == 44: + if pti < str_len(param_types) && cell_load_byte(param_types, pti) == 44: pti = pti + 1 // Encode return type @@ -434,9 +434,9 @@ fn reg_so_funcs(index_content: string, so_name: string): // dynamically, so no fixed 128-entry buffer is needed. ssi : ., mut = g_so_side_count grow_so_side(ssi + 1) - store_str_ptr(g_so_side_name, ssi * 8, func_name) - w64(g_so_side_tags, ssi * 8, tag_flags) - w64(g_so_side_type, ssi * 8, type_encoding) + cell_store_str(g_so_side_name, ssi * 8, func_name) + cell_store(g_so_side_tags, ssi * 8, tag_flags) + cell_store(g_so_side_type, ssi * 8, type_encoding) g_so_side_count = ssi + 1 // Look up a side-table entry by name and return its index, or -1. Names are @@ -447,7 +447,7 @@ fn find_so_side(name_idx: int) -> int: loop: if i >= g_so_side_count: return -1 - if str_eq(istr_get(name_idx), load_str_ptr(g_so_side_name, i * 8)) != 0: + if str_eq(istr_get(name_idx), cell_load_str(g_so_side_name, i * 8)) != 0: return i i = i + 1 return -1 @@ -457,13 +457,13 @@ fn find_so_side(name_idx: int) -> int: // otherwise an index file in the user's home directory could override a // declaration in user source. fn so_materialize(side_i: int) -> int: - fni := str_intern(load_str_ptr(g_so_side_name, side_i * 8)) + fni := str_intern(cell_load_str(g_so_side_name, side_i * 8)) si := g_sym_count grow_syms(si + 1) sym_set_name(si, fni) sym_set_kind(si, SYM_SO_FN) - sym_set_type(si, r64(g_so_side_tags, side_i * 8)) - sym_set_node(si, r64(g_so_side_type, side_i * 8)) + sym_set_type(si, cell_load(g_so_side_tags, side_i * 8)) + sym_set_node(si, cell_load(g_so_side_type, side_i * 8)) g_sym_count = si + 1 return si @@ -509,8 +509,8 @@ fn res_imports(): // First segment: main source grow_segs(g_seg_count + 1) - w64(g_seg_starts, g_seg_count * 8, 0) - w64(g_seg_fileids, g_seg_count * 8, main_fni) + cell_store(g_seg_starts, g_seg_count * 8, 0) + cell_store(g_seg_fileids, g_seg_count * 8, main_fni) g_seg_count = g_seg_count + 1 total_accumulated : ., mut = 0 @@ -526,34 +526,34 @@ fn res_imports(): loop: if i >= g_token_count: break - tk := r64(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) + tk := cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) if tk == T_IMPORT: pos : ., mut = i + 1 is_project : ., mut = false project_name : ., mut = "" - if pos < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_AT: + if pos < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_AT: is_project = true pos = pos + 1 - if pos < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: - project_name = istr_get(r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) + if pos < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: + project_name = istr_get(cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) pos = pos + 1 import_fileid : ., mut = "" - if pos < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: - import_fileid = istr_get(r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) + if pos < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: + import_fileid = istr_get(cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) pos = pos + 1 // Handle :: segments: backend::x86_64 -> backend::x86_64 loop: - if pos + 1 < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_PATHSEP && r64(g_tokens, (pos + 1) * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: - import_fileid = import_fileid + "::" + istr_get(r64(g_tokens, (pos + 1) * ESZ_TOKEN + OFF_TK_LEXEME)) + if pos + 1 < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_PATHSEP && cell_load(g_tokens, (pos + 1) * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: + import_fileid = import_fileid + "::" + istr_get(cell_load(g_tokens, (pos + 1) * ESZ_TOKEN + OFF_TK_LEXEME)) pos = pos + 2 else: break alias_str : ., mut = "" - if pos < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_COLON_DECL: + if pos < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_COLON_DECL: pos = pos + 1 - if pos < g_token_count && r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: - alias_str = istr_get(r64(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) + if pos < g_token_count && cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT: + alias_str = istr_get(cell_load(g_tokens, pos * ESZ_TOKEN + OFF_TK_LEXEME)) pos = pos + 1 path : ., mut = "" content : ., mut = "" @@ -584,7 +584,7 @@ fn res_imports(): loop: if pi >= str_len(fs_path): break - if load8(fs_path, pi) == 58 && pi + 1 < str_len(fs_path) && load8(fs_path, pi+1) == 58: + if cell_load_byte(fs_path, pi) == 58 && pi + 1 < str_len(fs_path) && cell_load_byte(fs_path, pi+1) == 58: fs_path = str_sub(fs_path, 0, pi) + "/" + str_sub(fs_path, pi+2, str_len(fs_path)-pi-2) pi = pi + 1 // Try .so extension index: $HOME/.core/lib//index @@ -598,58 +598,26 @@ fn res_imports(): // the bootstrap-built corec evaluates both sides of &&. home_dir : ., mut = get_env("HOME") if str_len(home_dir) > 0: - so_idx_path : ., mut = home_dir + "/.core/lib/" + fs_path + "/index" + so_idx_path : ., mut = target_so_index_path(home_dir, fs_path) so_idx := read_file(so_idx_path) if str_len(so_idx) > 0: reg_so_funcs(so_idx, fs_path) - // Always load .cr for runtime implementation - // (module_get_source:打开文档优先,未打开读盘) + // Search the target's import roots. The target owns the + // directory order and the three-axis namespace; module.cr + // only asks for root i and appends the import path. path = g_source_dir + fs_path + ".cr" content = module_get_source(path) - if str_len(content) == 0: - path = "src/stdlib/" + fs_path + ".cr" - content = module_get_source(path) - if str_len(content) == 0: - path = "src/runtime/" + fs_path + ".cr" - content = module_get_source(path) - // Architecture, format, and OS directories. These are the only - // path for cross-axis imports. They come before src/compiler so - // an instance-specific file wins over the generic file. The - // existing names do not overlap: these directories contain - // instr, sizes, regalloc, elf, resolve, and ld, none of which - // exist in the earlier search directories. - if str_len(content) == 0: - path = "src/format/elf/" + fs_path + ".cr" - content = module_get_source(path) - if str_len(content) == 0: - path = "src/arch/x86_64/" + fs_path + ".cr" - content = module_get_source(path) - if str_len(content) == 0: - path = "src/os/linux/" + fs_path + ".cr" - content = module_get_source(path) - if str_len(content) == 0: - path = "src/compiler/" + fs_path + ".cr" - content = module_get_source(path) - if str_len(content) == 0: - path = "src/arch/hit/" + fs_path + ".cr" - content = module_get_source(path) - // ent_kernel.cr is the semantic kernel under src/lattice. It was - // moved out of the old instance directory when the x86 - // instance was split into the architecture, format, and OS - // axes. Both corec and corearch import it: the corec writer - // calls compute_live_ranges and grow_entries, and backend - // units reach it through this path. - if str_len(content) == 0: - path = "src/lattice/" + fs_path + ".cr" - content = module_get_source(path) - // Combination root named by the target triple. Project mode - // reads main.cr and _import.cr directly from this directory. - // It comes after compiler and lattice because main.cr here is - // not an import target and must not shadow the existing main - // or entry imports. - if str_len(content) == 0: - path = "src/targets/x86_64-linux/" + fs_path + ".cr" - content = module_get_source(path) + ri : ., mut = 0 + loop: + if str_len(content) > 0: + break + if ri >= target_import_root_count(): + break + root := target_import_root(ri) + if str_len(root) > 0: + path = root + fs_path + ".cr" + content = module_get_source(path) + ri = ri + 1 if str_len(content) == 0: path = fs_path + ".cr" content = module_get_source(path) @@ -669,7 +637,7 @@ fn res_imports(): loop: if mi >= g_mod_count: break - if r64(g_mods, mi * 24 + 8) == loaded_fni: + if cell_load(g_mods, mi * 24 + 8) == loaded_fni: already_loaded = 1 break mi = mi + 1 @@ -682,8 +650,8 @@ fn res_imports(): any_new = 1 seg_byte := main_len + total_accumulated + pass_bytes + 1 grow_segs(g_seg_count + 1) - w64(g_seg_starts, g_seg_count * 8, seg_byte) - w64(g_seg_fileids, g_seg_count * 8, loaded_fni) + cell_store(g_seg_starts, g_seg_count * 8, seg_byte) + cell_store(g_seg_fileids, g_seg_count * 8, loaded_fni) g_seg_count = g_seg_count + 1 pass_bytes = pass_bytes + 1 + content_len alias_ni : ., mut = -1 @@ -692,9 +660,9 @@ fn res_imports(): else: alias_ni = loaded_fni grow_mods(g_mod_count + 1) - w64(g_mods, g_mod_count * 24, alias_ni) - w64(g_mods, g_mod_count * 24 + 8, loaded_fni) - store_str_ptr(g_mods, g_mod_count * 24 + 16, path) + cell_store(g_mods, g_mod_count * 24, alias_ni) + cell_store(g_mods, g_mod_count * 24 + 8, loaded_fni) + cell_store_str(g_mods, g_mod_count * 24 + 16, path) g_mod_count = g_mod_count + 1 pass_src = pass_src + "\n" + content i = pos diff --git a/src/compiler/monomorph.cr b/src/compiler/monomorph.cr index 73df2128..e3d10228 100644 --- a/src/compiler/monomorph.cr +++ b/src/compiler/monomorph.cr @@ -43,7 +43,7 @@ fn grow_gen_instances(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 24) - _dyncpy(g_gen_instances, g_gen_instance_cap * 24, nb) + cell_copy(g_gen_instances, g_gen_instance_cap * 24, nb) g_gen_instances = nb g_gen_instance_cap = nc @@ -59,7 +59,7 @@ fn grow_purity_inst(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 24) - _dyncpy(g_purity_inst, g_purity_inst_cap * 24, nb) + cell_copy(g_purity_inst, g_purity_inst_cap * 24, nb) g_purity_inst = nb g_purity_inst_cap = nc @@ -73,10 +73,10 @@ fn grow_gen_subst(needed: int): nc = needed + 16 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_gen_subst_old, g_gen_subst_cap * 8, n1) + cell_copy(g_gen_subst_old, g_gen_subst_cap * 8, n1) g_gen_subst_old = n1 n2 := alloc(sz) - _dyncpy(g_gen_subst_new, g_gen_subst_cap * 8, n2) + cell_copy(g_gen_subst_new, g_gen_subst_cap * 8, n2) g_gen_subst_new = n2 g_gen_subst_cap = nc @@ -90,10 +90,10 @@ fn grow_gen_dedup(needed: int): nc = needed + 64 sz := nc * 8 n1 := alloc(sz) - _dyncpy(g_gen_dedup_old, g_gen_dedup_cap * 8, n1) + cell_copy(g_gen_dedup_old, g_gen_dedup_cap * 8, n1) g_gen_dedup_old = n1 n2 := alloc(sz) - _dyncpy(g_gen_dedup_new, g_gen_dedup_cap * 8, n2) + cell_copy(g_gen_dedup_new, g_gen_dedup_cap * 8, n2) g_gen_dedup_new = n2 g_gen_dedup_cap = nc @@ -107,7 +107,7 @@ fn grow_gen_subst_tis(needed: int): if nc < needed: nc = needed + 16 nb := alloc(nc * 8) - _dyncpy(g_gen_subst_tis, g_gen_subst_tis_cap * 8, nb) + cell_copy(g_gen_subst_tis, g_gen_subst_tis_cap * 8, nb) g_gen_subst_tis = nb g_gen_subst_tis_cap = nc @@ -128,23 +128,23 @@ fn gen_build_subst(func_ni: int, type_args: string): loop: if gi >= gc: break - if load8(type_args, pos) == 0: + if cell_load_byte(type_args, pos) == 0: break start := pos loop: - c := load8(type_args, pos) + c := cell_load_byte(type_args, pos) if c == 0 || c == 44: break // null or comma pos = pos + 1 type_name := str_sub(type_args, start, pos - start) - if load8(type_args, pos) == 44: + if cell_load_byte(type_args, pos) == 44: pos = pos + 1 gen_param_ni := fi_generic_name(func_ni, gi) concrete_ni := str_intern(type_name) grow_gen_subst(g_gen_subst_count + 1) - w64(g_gen_subst_old, g_gen_subst_count * 8, gen_param_ni) - w64(g_gen_subst_new, g_gen_subst_count * 8, concrete_ni) + cell_store(g_gen_subst_old, g_gen_subst_count * 8, gen_param_ni) + cell_store(g_gen_subst_new, g_gen_subst_count * 8, concrete_ni) g_gen_subst_count = g_gen_subst_count + 1 gi = gi + 1 @@ -162,7 +162,7 @@ fn gen_build_subst_tis(func_ni: int, bstart: int) -> int: return 0 if bstart >= g_gen_binds_count: return 0 - cnt := r64(g_gen_binds, bstart * 8) + cnt := cell_load(g_gen_binds, bstart * 8) if cnt != gc: return 0 // The block length must equal the parameter count; otherwise reject it. @@ -173,14 +173,14 @@ fn gen_build_subst_tis(func_ni: int, bstart: int) -> int: loop: if i >= gc: break - t := r64(g_gen_binds, (bstart + 1 + i) * 8) + t := cell_load(g_gen_binds, (bstart + 1 + i) * 8) if t < 0: return 0 // An unbound entry discards the whole block; no partial substitution. if inst_ti_concrete(t) == 0: return 0 // A remaining generic-parameter row also discards the whole block. - w64(g_gen_subst_tis, i * 8, t) + cell_store(g_gen_subst_tis, i * 8, t) g_gen_subst_tis_count = i + 1 i = i + 1 return g_gen_subst_tis_count @@ -192,8 +192,8 @@ fn gen_lookup_subst(name_ni: int) -> int: loop: if mi >= g_gen_subst_count: break - if r64(g_gen_subst_old, mi * 8) == name_ni: - return r64(g_gen_subst_new, mi * 8) + if cell_load(g_gen_subst_old, mi * 8) == name_ni: + return cell_load(g_gen_subst_new, mi * 8) mi = mi + 1 return -1 @@ -266,14 +266,14 @@ fn inst_key_of_ti(ti: int) -> string: s = istr_get(get_type_data(base)) s = s + "[" start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) i : ., mut = 0 loop: if i >= cnt: break if i > 0: s = s + "|" - s = s + inst_key_of_ti(r64(g_gen_apply_data, (start + 1 + i) * 8)) + s = s + inst_key_of_ti(cell_load(g_gen_apply_data, (start + 1 + i) * 8)) i = i + 1 return s + "]" if k == TYP_ARRAY: @@ -302,7 +302,7 @@ fn inst_key_of_ti(ti: int) -> string: break if i2 > 0: s = s + "|" - s = s + inst_key_of_ti(r64(g_gen_apply_data, (start2 + i2) * 8)) + s = s + inst_key_of_ti(cell_load(g_gen_apply_data, (start2 + i2) * 8)) i2 = i2 + 1 return s + ")" return "#" + int_str(ti) // unknown kind: fall back to the row number @@ -346,7 +346,7 @@ fn inst_type_node_of_ti(ti: int) -> int: if get_type_kind(base) != TYP_NAMED: return -1 start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) if cnt <= 0: return -1 first_an : ., mut = -1 @@ -354,7 +354,7 @@ fn inst_type_node_of_ti(ti: int) -> int: loop: if i >= cnt: break - at := r64(g_gen_apply_data, (start + 1 + i) * 8) + at := cell_load(g_gen_apply_data, (start + 1 + i) * 8) ak := get_type_kind(at) // Argument roots must be consecutive: accept only native, named, and generic-parameter rows. if ak != TYP_BASE && ak != TYP_NAMED && ak != TYP_GENERIC_PARAM: @@ -373,8 +373,8 @@ fn gen_lookup_subst_ti(name_ni: int) -> int: loop: if mi >= g_gen_subst_tis_count: break - if r64(g_gen_subst_old, mi * 8) == name_ni: - return r64(g_gen_subst_tis, mi * 8) + if cell_load(g_gen_subst_old, mi * 8) == name_ni: + return cell_load(g_gen_subst_tis, mi * 8) mi = mi + 1 return -1 @@ -400,7 +400,7 @@ fn gen_subst_ti(ti: int) -> int: return alloc_type(TYP_OPTIONAL, gen_subst_ti(get_type_data(ti)), 0) if k == TYP_GENERIC_APPLY: start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) // Two passes: substituting a child can append to g_gen_apply_data. subs : string, mut if cnt > 0: @@ -409,16 +409,16 @@ fn gen_subst_ti(ti: int) -> int: loop: if i >= cnt: break - w64(subs, i * 8, gen_subst_ti(r64(g_gen_apply_data, (start + 1 + i) * 8))) + cell_store(subs, i * 8, gen_subst_ti(cell_load(g_gen_apply_data, (start + 1 + i) * 8))) i = i + 1 ns := g_gen_apply_data_count grow_gen_apply_data(ns + 1 + cnt) - w64(g_gen_apply_data, ns * 8, cnt) + cell_store(g_gen_apply_data, ns * 8, cnt) i2 : ., mut = 0 loop: if i2 >= cnt: break - w64(g_gen_apply_data, (ns + 1 + i2) * 8, r64(subs, i2 * 8)) + cell_store(g_gen_apply_data, (ns + 1 + i2) * 8, cell_load(subs, i2 * 8)) i2 = i2 + 1 g_gen_apply_data_count = ns + 1 + cnt return alloc_type(TYP_GENERIC_APPLY, get_type_data(ti), ns) @@ -439,12 +439,12 @@ fn inst_ti_concrete(ti: int) -> int: return inst_ti_concrete(get_type_data(ti)) if k == TYP_GENERIC_APPLY: start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) i : ., mut = 0 loop: if i >= cnt: return 1 - if inst_ti_concrete(r64(g_gen_apply_data, (start + 1 + i) * 8)) == 0: + if inst_ti_concrete(cell_load(g_gen_apply_data, (start + 1 + i) * 8)) == 0: return 0 i = i + 1 return 1 @@ -455,7 +455,7 @@ fn inst_ti_concrete(ti: int) -> int: loop: if i2 >= cnt2: return 1 - if inst_ti_concrete(r64(g_gen_apply_data, (start2 + i2) * 8)) == 0: + if inst_ti_concrete(cell_load(g_gen_apply_data, (start2 + i2) * 8)) == 0: return 0 i2 = i2 + 1 return 1 @@ -473,7 +473,7 @@ fn gen_remap_binds(iv: int) -> int: start := iv - 1 if start >= g_gen_binds_count: return 0 - cnt := r64(g_gen_binds, start * 8) + cnt := cell_load(g_gen_binds, start * 8) if cnt <= 0: return 0 if start + 1 + cnt > g_gen_binds_count: @@ -486,19 +486,19 @@ fn gen_remap_binds(iv: int) -> int: loop: if i >= cnt: break - t := gen_subst_ti(r64(g_gen_binds, (start + 1 + i) * 8)) + t := gen_subst_ti(cell_load(g_gen_binds, (start + 1 + i) * 8)) if inst_ti_concrete(t) == 0: return 0 - w64(subs, i * 8, t) + cell_store(subs, i * 8, t) i = i + 1 ns := g_gen_binds_count grow_gen_binds(ns + 1 + cnt) - w64(g_gen_binds, ns * 8, cnt) + cell_store(g_gen_binds, ns * 8, cnt) i2 : ., mut = 0 loop: if i2 >= cnt: break - w64(g_gen_binds, (ns + 1 + i2) * 8, r64(subs, i2 * 8)) + cell_store(g_gen_binds, (ns + 1 + i2) * 8, cell_load(subs, i2 * 8)) i2 = i2 + 1 g_gen_binds_count = ns + 1 + cnt return ns + 1 @@ -512,15 +512,15 @@ fn gen_dedup_find(old_node: int) -> int: loop: if mi >= g_gen_dedup_count: break - if r64(g_gen_dedup_old, mi * 8) == old_node: - return r64(g_gen_dedup_new, mi * 8) + if cell_load(g_gen_dedup_old, mi * 8) == old_node: + return cell_load(g_gen_dedup_new, mi * 8) mi = mi + 1 return -1 fn gen_dedup_add(old_node: int, new_node: int): grow_gen_dedup(g_gen_dedup_count + 1) - w64(g_gen_dedup_old, g_gen_dedup_count * 8, old_node) - w64(g_gen_dedup_new, g_gen_dedup_count * 8, new_node) + cell_store(g_gen_dedup_old, g_gen_dedup_count * 8, old_node) + cell_store(g_gen_dedup_new, g_gen_dedup_count * 8, new_node) g_gen_dedup_count = g_gen_dedup_count + 1 // ============================================================ @@ -540,9 +540,9 @@ fn gen_clone_block(start: int, count: int) -> int: loop: if i >= count: break - old_stmt := r64(g_block_stmts, (start + i) * 8) + old_stmt := cell_load(g_block_stmts, (start + i) * 8) new_stmt := gen_clone_tree(old_stmt) - w64(g_block_stmts, (new_start + i) * 8, new_stmt) + cell_store(g_block_stmts, (new_start + i) * 8, new_stmt) i = i + 1 return new_start @@ -870,15 +870,15 @@ fn gen_clone_tree(node: int) -> int: vn : ., mut = -1 if b + i >= 0: vn = ast_a(b + i) - w64(nvs, i * 8, gen_clone_tree(vn)) - w64(nms, i * 8, ast_b(b + i)) + cell_store(nvs, i * 8, gen_clone_tree(vn)) + cell_store(nms, i * 8, ast_b(b + i)) i = i + 1 new_first := g_ast_count i = 0 loop: if i >= c: break - ast_alloc(EXPR_NONE, r64(nvs, i * 8), r64(nms, i * 8), 0, 0, 0, 0, ln, cl) + ast_alloc(EXPR_NONE, cell_load(nvs, i * 8), cell_load(nms, i * 8), 0, 0, 0, 0, ln, cl) i = i + 1 n := ast_alloc(k, a, new_first, c, iv, tv, d, ln, cl) gen_dedup_add(node, n) @@ -900,14 +900,14 @@ fn gen_clone_tree(node: int) -> int: vn : ., mut = -1 if a + i >= 0: vn = ast_a(a + i) - w64(nvs, i * 8, gen_clone_tree(vn)) + cell_store(nvs, i * 8, gen_clone_tree(vn)) i = i + 1 new_first := g_ast_count i = 0 loop: if i >= b: break - ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) + ast_alloc(EXPR_NONE, cell_load(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) i = i + 1 n := ast_alloc(k, new_first, b, c, iv, tv, d, ln, cl) gen_dedup_add(node, n) @@ -929,14 +929,14 @@ fn gen_clone_tree(node: int) -> int: vn : ., mut = -1 if a + i >= 0: vn = ast_a(a + i) - w64(nvs, i * 8, gen_clone_tree(vn)) + cell_store(nvs, i * 8, gen_clone_tree(vn)) i = i + 1 new_first := g_ast_count i = 0 loop: if i >= b: break - ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) + ast_alloc(EXPR_NONE, cell_load(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) i = i + 1 n := ast_alloc(k, new_first, b, c, iv, tv, d, ln, cl) gen_dedup_add(node, n) @@ -998,14 +998,14 @@ fn gen_clone_tree(node: int) -> int: vn : ., mut = -1 if b + i >= 0: vn = ast_a(b + i) - w64(nvs, i * 8, gen_clone_tree(vn)) + cell_store(nvs, i * 8, gen_clone_tree(vn)) i = i + 1 new_first := g_ast_count i = 0 loop: if i >= c: break - ast_alloc(EXPR_NONE, r64(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) + ast_alloc(EXPR_NONE, cell_load(nvs, i * 8), 0, 0, 0, 0, 0, ln, cl) i = i + 1 n := ast_alloc(k, a, new_first, c, iv, tv, d, ln, cl) gen_dedup_add(node, n) @@ -1052,10 +1052,10 @@ fn gen_find_cached(func_ni: int, type_args: string) -> int: loop: if i >= g_gen_instance_count: break - if r64(g_gen_instances, i * 24) == func_ni: - cached_args_ni := r64(g_gen_instances, i * 24 + 8) + if cell_load(g_gen_instances, i * 24) == func_ni: + cached_args_ni := cell_load(g_gen_instances, i * 24 + 8) if istr_eq(cached_args_ni, type_args) != 0: - return r64(g_gen_instances, i * 24 + 16) + return cell_load(g_gen_instances, i * 24 + 16) i = i + 1 return -1 @@ -1173,9 +1173,9 @@ fn gen_create_instance(func_ni: int, type_args: string, bstart: int) -> int: // 4. Cache the new function type_args_ni := str_intern(type_args) grow_gen_instances(g_gen_instance_count + 1) - w64(g_gen_instances, g_gen_instance_count * 24, func_ni) - w64(g_gen_instances, g_gen_instance_count * 24 + 8, type_args_ni) - w64(g_gen_instances, g_gen_instance_count * 24 + 16, new_fi) + cell_store(g_gen_instances, g_gen_instance_count * 24, func_ni) + cell_store(g_gen_instances, g_gen_instance_count * 24 + 8, type_args_ni) + cell_store(g_gen_instances, g_gen_instance_count * 24 + 16, new_fi) g_gen_instance_count = g_gen_instance_count + 1 // Record the instance-to-source mapping for purity. Do not copy @@ -1184,9 +1184,9 @@ fn gen_create_instance(func_ni: int, type_args: string, bstart: int) -> int: // computed in df_state_finalize; the source purity is the conjunction of its // instances, filled by compute_all_purity. grow_purity_inst(g_purity_inst_count + 1) - w64(g_purity_inst, g_purity_inst_count * 24, func_ni) - w64(g_purity_inst, g_purity_inst_count * 24 + 8, type_args_ni) - w64(g_purity_inst, g_purity_inst_count * 24 + 16, new_fi) + cell_store(g_purity_inst, g_purity_inst_count * 24, func_ni) + cell_store(g_purity_inst, g_purity_inst_count * 24 + 8, type_args_ni) + cell_store(g_purity_inst, g_purity_inst_count * 24 + 16, new_fi) g_purity_inst_count = g_purity_inst_count + 1 // 5. Return new function index diff --git a/src/compiler/opt.cr b/src/compiler/opt.cr index 229f9fee..4586efb3 100644 --- a/src/compiler/opt.cr +++ b/src/compiler/opt.cr @@ -96,7 +96,7 @@ fn ast_optimize_body(body: int): loop: if i >= sc: break - sn := r64(g_block_stmts, (ss + i) * 8) + sn := cell_load(g_block_stmts, (ss + i) * 8) ast_optimize_body(sn) i = i + 1 return @@ -265,8 +265,8 @@ fn pass_cse(): loop: if fi >= g_ir_func_count: break - ic := r64(g_ir_func_instr_count, fi * 8) - ist := r64(g_ir_func_instr_start, fi * 8) + ic := cell_load(g_ir_func_instr_count, fi * 8) + ist := cell_load(g_ir_func_instr_start, fi * 8) if ic <= 0: fi = fi + 1 continue @@ -352,26 +352,26 @@ fn pass_cse(): if sj >= seen_count: break so := sj * 32 - if r64(seen, so) == op && r64(seen, so+8) == s1 && r64(seen, so+16) == s2: + if cell_load(seen, so) == op && cell_load(seen, so+8) == s1 && cell_load(seen, so+16) == s2: found = sj break sj = sj + 1 if found >= 0: // Duplicate: map d to the canonical destination and replace // this instruction with a no-op. - canonical := r64(seen, found * 32 + 24) - w64(replace_map, replace_count * 8, d) - w64(replace_map, replace_count * 8 + 8, canonical) + canonical := cell_load(seen, found * 32 + 24) + cell_store(replace_map, replace_count * 8, d) + cell_store(replace_map, replace_count * 8 + 8, canonical) replace_count = replace_count + 1 // Replace the instruction with a no-op. iri_set_op(inst, IR_NOP) else: // New expression: record it. so := seen_count * 32 - w64(seen, so, op) - w64(seen, so+8, s1) - w64(seen, so+16, s2) - w64(seen, so+24, d) + cell_store(seen, so, op) + cell_store(seen, so+8, s1) + cell_store(seen, so+16, s2) + cell_store(seen, so+24, d) seen_count = seen_count + 1 // Apply replacements to the operands of every instruction. @@ -380,8 +380,8 @@ fn pass_cse(): loop: if rj >= replace_count: break - old_v := r64(replace_map, rj * 8) - new_v := r64(replace_map, rj * 8 + 8) + old_v := cell_load(replace_map, rj * 8) + new_v := cell_load(replace_map, rj * 8 + 8) if iri_s1(inst) == old_v: iri_set_s1(inst, new_v) if iri_s2(inst) == old_v: diff --git a/src/compiler/parser.cr b/src/compiler/parser.cr index 0e7abe79..7a93b56e 100644 --- a/src/compiler/parser.cr +++ b/src/compiler/parser.cr @@ -17,15 +17,15 @@ fn alloc_node(kind: int, a: int, b: int, c: int, iv: int, tv: int, d: int, line: fn cur_tok() -> int: return g_token_pos fn tok_k(p: int) -> int: - return r64(g_tokens, p * ESZ_TOKEN + OFF_TK_KIND) + return cell_load(g_tokens, p * ESZ_TOKEN + OFF_TK_KIND) fn tok_lx(p: int) -> string: - return istr_get(r64(g_tokens, p * ESZ_TOKEN + OFF_TK_LEXEME)) + return istr_get(cell_load(g_tokens, p * ESZ_TOKEN + OFF_TK_LEXEME)) fn tok_iv(p: int) -> int: - return r64(g_tokens, p * ESZ_TOKEN + OFF_TK_INTVAL) + return cell_load(g_tokens, p * ESZ_TOKEN + OFF_TK_INTVAL) fn tok_ln(p: int) -> int: - return r64(g_tokens, p * ESZ_TOKEN + OFF_TK_LINE) + return cell_load(g_tokens, p * ESZ_TOKEN + OFF_TK_LINE) fn tok_cl(p: int) -> int: - return r64(g_tokens, p * ESZ_TOKEN + OFF_TK_COL) + return cell_load(g_tokens, p * ESZ_TOKEN + OFF_TK_COL) fn advance_tok() -> int: t := cur_tok() @@ -227,20 +227,20 @@ fn parse_type() -> int: res = alloc_node(EXPR_IDENT, 0, 0, 0, ni, 0, 0, line, col) else if tok_k(t) == T_LBRACE: grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_EXPECTED) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "expected type after '->', got '{' — missing return type?") - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_EXPECTED) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "expected type after '->', got '{' — missing return type?") + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) g_diag_count = g_diag_count + 1 res = alloc_node(0, 0, 0, 0, 0, TY_UNIT, 0, line, col) else: grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_EXPECTED) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "expected type after '->'") - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_EXPECTED) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "expected type after '->'") + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, line) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, col) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(line)) g_diag_count = g_diag_count + 1 res = alloc_node(0, 0, 0, 0, 0, TY_UNIT, 0, line, col) // `T?` produces EXPR_OPTIONAL with the inner type node in slot a. @@ -573,7 +573,7 @@ fn parse_primary() -> int: return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_BIGINT: advance_tok() - tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) + tl := cell_load(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) return alloc_node(EXPR_BIGINT, tl, 0, 0, 0, TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_DEX: advance_tok() @@ -581,7 +581,7 @@ fn parse_primary() -> int: // the token lexeme; int_val = fixed-point scaled integer (the exact default // path). Width suffixes were removed, so the data slot is always 0. bits : int = 0 - tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) // 词素串下标(-1 = 无) + tl := cell_load(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) // 词素串下标(-1 = 无) if tl >= 0: bits = str_to_f64_bits(istr_get(tl)) return alloc_node(EXPR_DEX, bits, 0, 0, tok_iv(t), TY_DEX, 0, tok_ln(t), tok_cl(t)) @@ -652,15 +652,15 @@ fn parse_primary() -> int: if fc >= cap: ncap := cap * 2 nv := alloc(ncap * 8) - _dyncpy(vals, cap * 8, nv) + cell_copy(vals, cap * 8, nv) vals = nv nn := alloc(ncap * 8) - _dyncpy(names, cap * 8, nn) + cell_copy(names, cap * 8, nn) names = nn cap = ncap g_want_value = 1 // 字段值 = 表达式位 - w64(vals, fc * 8, parse_expr()) // 字段值(子树自占若干槽) - w64(names, fc * 8, fni) // field name index (name binding) + cell_store(vals, fc * 8, parse_expr()) // 字段值(子树自占若干槽) + cell_store(names, fc * 8, fni) // field name index (name binding) fc = fc + 1 if check(T_COMMA): advance_tok() @@ -670,13 +670,13 @@ fn parse_primary() -> int: loop: if fi2 >= fc: break - vn := r64(vals, fi2 * 8) + vn := cell_load(vals, fi2 * 8) ln : ., mut = tok_ln(t) cl : ., mut = tok_cl(t) if vn >= 0: ln = ast_line(vn) cl = ast_col(vn) - wl := ast_alloc(EXPR_NONE, vn, r64(names, fi2 * 8), 0, 0, 0, 0, ln, cl) + wl := ast_alloc(EXPR_NONE, vn, cell_load(names, fi2 * 8), 0, 0, 0, 0, ln, cl) if fi2 == 0: ff = wl fi2 = fi2 + 1 @@ -694,18 +694,18 @@ fn parse_primary() -> int: // values first, then build the contiguous wrappers. cap : ., mut = 8 vals : string, mut = alloc(cap * 8) - w64(vals, 0, e) + cell_store(vals, 0, e) ec : ., mut = 1 loop: advance_tok() // consume comma if ec >= cap: ncap := cap * 2 nv := alloc(ncap * 8) - _dyncpy(vals, cap * 8, nv) + cell_copy(vals, cap * 8, nv) vals = nv cap = ncap g_want_value = 1 // 元组元素 = 表达式位 - w64(vals, ec * 8, parse_expr()) // 元素值(子树自占若干槽) + cell_store(vals, ec * 8, parse_expr()) // 元素值(子树自占若干槽) ec = ec + 1 if !check(T_COMMA): break @@ -715,7 +715,7 @@ fn parse_primary() -> int: loop: if ei >= ec: break - vn := r64(vals, ei * 8) + vn := cell_load(vals, ei * 8) ln : ., mut = tok_ln(t) cl : ., mut = tok_cl(t) if vn >= 0: @@ -805,7 +805,7 @@ fn parse_primary() -> int: ec : ., mut = 0 if !check(T_RBRACKET): g_want_value = 1 // 数组元素 = 表达式位 - w64(vals, 0, parse_expr()) // 元素值(子树自占若干槽) + cell_store(vals, 0, parse_expr()) // 元素值(子树自占若干槽) ec = 1 if check(T_SEMI): // Repeat array [value; count]: parse the value once, then append @@ -815,14 +815,14 @@ fn parse_primary() -> int: cnt : ., mut = tok_iv(ct) if cnt > cap: nv := alloc(cnt * 8) - _dyncpy(vals, cap * 8, nv) + cell_copy(vals, cap * 8, nv) vals = nv cap = cnt ri : ., mut = 1 loop: if ri >= cnt: break - w64(vals, ri * 8, r64(vals, 0)) + cell_store(vals, ri * 8, cell_load(vals, 0)) ri = ri + 1 ec = cnt else: @@ -833,11 +833,11 @@ fn parse_primary() -> int: if ec >= cap: ncap := cap * 2 nv := alloc(ncap * 8) - _dyncpy(vals, cap * 8, nv) + cell_copy(vals, cap * 8, nv) vals = nv cap = ncap g_want_value = 1 // 数组元素 = 表达式位 - w64(vals, ec * 8, parse_expr()) // 元素值(子树自占若干槽) + cell_store(vals, ec * 8, parse_expr()) // 元素值(子树自占若干槽) ec = ec + 1 advance_tok() ef : ., mut = -1 @@ -845,7 +845,7 @@ fn parse_primary() -> int: loop: if ei2 >= ec: break - vn := r64(vals, ei2 * 8) + vn := cell_load(vals, ei2 * 8) ln : ., mut = tok_ln(t) cl : ., mut = tok_cl(t) if vn >= 0: @@ -873,10 +873,10 @@ fn block_push(n: int): ncap = 16 nv := alloc(ncap * 8) if g_pb_cap > 0: - _dyncpy(g_pb_stmts, g_pb_cap * 8, nv) + cell_copy(g_pb_stmts, g_pb_cap * 8, nv) g_pb_stmts = nv g_pb_cap = ncap - w64(g_pb_stmts, g_pb_count * 8, n) + cell_store(g_pb_stmts, g_pb_count * 8, n) g_pb_count = g_pb_count + 1 // Common entry for all regions: head and body. @@ -947,7 +947,7 @@ fn parse_block_items(head: int) -> int: if i >= g_pb_count: break grow_block_stmts(g_block_stmt_count + 1) - w64(g_block_stmts, g_block_stmt_count * 8, r64(g_pb_stmts, i * 8)) + cell_store(g_block_stmts, g_block_stmt_count * 8, cell_load(g_pb_stmts, i * 8)) g_block_stmt_count = g_block_stmt_count + 1 i = i + 1 return alloc_node(EXPR_BLOCK, si, g_pb_count, 0, 0, 0, 0, tok_ln(t), tok_cl(t)) @@ -988,7 +988,7 @@ fn parse_new_var_decl() -> int: // Parse name list nt := advance_tok() unsafe: - w64(names, nc * 8, @ptr_of(tok_lx(nt))) + cell_store(names, nc * 8, @ptr_of(tok_lx(nt))) // 2(a):串字入裸字表 nc = nc + 1 // Batch: a, b : type = ... @@ -999,7 +999,7 @@ fn parse_new_var_decl() -> int: advance_tok() // , nt2 := advance_tok() unsafe: - w64(names, nc * 8, @ptr_of(tok_lx(nt2))) + cell_store(names, nc * 8, @ptr_of(tok_lx(nt2))) nc = nc + 1 typ : ., mut = -1 @@ -1016,14 +1016,14 @@ fn parse_new_var_decl() -> int: if check(T_COLON_EQ): advance_tok() g_want_value = 1 // 声明右值 = 表达式位 - w64(values, vc * 8, parse_expr()) + cell_store(values, vc * 8, parse_expr()) vc = vc + 1 loop: if !check(T_COMMA): break advance_tok() g_want_value = 1 - w64(values, vc * 8, parse_expr()) + cell_store(values, vc * 8, parse_expr()) vc = vc + 1 else: // consume ':' @@ -1055,35 +1055,35 @@ fn parse_new_var_decl() -> int: tni := str_intern(tag) ei := find_plugin_entry(g_plugin_tags, g_plugin_tag_count, tni, -1) if ei >= 0: - pd := r64(g_plugin_tags, ei*24+16) + pd := cell_load(g_plugin_tags, ei*24+16) if pd != 0: is_mut = 1 else: // 未知标签:TA08(已知标签 = mut/pub/apx + 插件注册标签) grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_TA_UNKNOWN_TAG) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "unknown declaration tag '" + tag + "'") - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(tag_t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(tag_t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(tag_t))) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, EC_TA_UNKNOWN_TAG) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "unknown declaration tag '" + tag + "'") + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(tag_t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(tag_t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(tag_t))) g_diag_count = g_diag_count + 1 if !check(T_COMMA): break advance_tok() // Parse values - w64(values, 0 * 8, -1) + cell_store(values, 0 * 8, -1) if check(T_EQ): advance_tok() g_want_value = 1 // 声明右值 = 表达式位 - w64(values, vc * 8, parse_expr()) + cell_store(values, vc * 8, parse_expr()) vc = vc + 1 loop: if !check(T_COMMA): break advance_tok() g_want_value = 1 - w64(values, vc * 8, parse_expr()) + cell_store(values, vc * 8, parse_expr()) vc = vc + 1 // A trailing semicolon is optional; end_decl finishes the statement at a line @@ -1096,9 +1096,9 @@ fn parse_new_var_decl() -> int: if i >= nc: break name_str := unsafe: - @str_of(r64(names, i * 8)) + @str_of(cell_load(names, i * 8)) ni := str_intern(name_str) // 2(a):裸字读出按串用 - nv := r64(values, i * 8) + nv := cell_load(values, i * 8) // `apx` is valid only on an explicit `dex` or `int` declaration. Other // forms have no representation path and would carry the tag without the // approximation semantics. Reject them with P26 and produce no output. @@ -1123,7 +1123,7 @@ fn parse_new_var_decl() -> int: g_extra_lets = alloc(128) g_extra_lets_cap = 16 if g_extra_let_count < g_extra_lets_cap: - w64(g_extra_lets, g_extra_let_count * 8, node) + cell_store(g_extra_lets, g_extra_let_count * 8, node) g_extra_let_count = g_extra_let_count + 1 i = i + 1 @@ -1239,7 +1239,7 @@ fn parse_stmt() -> int: // Drain batch extras from previous call if g_extra_let_count > 0: g_extra_let_count = g_extra_let_count - 1 - return r64(g_extra_lets, g_extra_let_count * 8) + return cell_load(g_extra_lets, g_extra_let_count * 8) t := cur_tok() // Nested fn and flow declarations are not statements. Function declarations are @@ -1422,7 +1422,7 @@ fn parse_pattern() -> int: return alloc_node(EXPR_INT, 0, 0, 0, tok_iv(t), TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_BIGINT: advance_tok() - tl := r64(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) + tl := cell_load(g_tokens, t * ESZ_TOKEN + OFF_TK_LEXEME) return alloc_node(EXPR_BIGINT, tl, 0, 0, 0, TY_INT, 0, tok_ln(t), tok_cl(t)) if tok_k(t) == T_STRING || tok_k(t) == T_INTERP: return parse_interp_run() @@ -1509,10 +1509,10 @@ fn parse_pattern() -> int: if fc >= cap: ncap := cap * 2 nv := alloc(ncap * 8) - _dyncpy(vals, cap * 8, nv) + cell_copy(vals, cap * 8, nv) vals = nv cap = ncap - w64(vals, fc * 8, parse_pattern()) + cell_store(vals, fc * 8, parse_pattern()) fc = fc + 1 if check(T_COMMA): advance_tok() @@ -1522,7 +1522,7 @@ fn parse_pattern() -> int: loop: if fi2 >= fc: break - vn := r64(vals, fi2 * 8) + vn := cell_load(vals, fi2 * 8) ln : ., mut = tok_ln(t) cl : ., mut = tok_cl(t) if vn >= 0: @@ -1573,7 +1573,7 @@ fn parse_generics_into(names: string, constrs: string) -> int: loop: if ci >= 4: break - w64(constrs, ci * 8, -1) + cell_store(constrs, ci * 8, -1) ci = ci + 1 open_tok : ., mut = 0 close_tok : ., mut = 0 @@ -1593,13 +1593,13 @@ fn parse_generics_into(names: string, constrs: string) -> int: break gt := advance_tok() unsafe: - w64(names, gc * 8, @ptr_of(tok_lx(gt))) + cell_store(names, gc * 8, @ptr_of(tok_lx(gt))) // Check for constraint: T: Interface - w64(constrs, gc * 8, -1) + cell_store(constrs, gc * 8, -1) if check(T_COLON_DECL): advance_tok() ct := advance_tok() - w64(constrs, gc * 8, str_intern(tok_lx(ct))) + cell_store(constrs, gc * 8, str_intern(tok_lx(ct))) gc = gc + 1 if !check(T_COMMA): break @@ -1615,7 +1615,7 @@ fn save_func_generics(fi: int, names: string, count: int): if gi >= count: break name_str := unsafe: - @str_of(r64(names, gi * 8)) + @str_of(cell_load(names, gi * 8)) ni := str_intern(name_str) fi_set_generic_name(fi, gi, ni) gi = gi + 1 @@ -1625,10 +1625,10 @@ fn save_func_gen_constrs(fi: int, constrs: string, count: int): loop: if gi >= count: break - if r64(constrs, gi * 8) >= 0: + if cell_load(constrs, gi * 8) >= 0: idx := fi * MAX_GENERICS + gi grow_gen_constr(idx + 1) - w64(g_generic_constr, idx * 8, r64(constrs, gi * 8)) + cell_store(g_generic_constr, idx * 8, cell_load(constrs, gi * 8)) if idx + 1 > g_generic_constr_count: g_generic_constr_count = idx + 1 gi = gi + 1 @@ -1648,7 +1648,7 @@ fn save_struct_gen_constrs(si: int, constrs: string, count: int): loop: if zi >= MAX_GENERICS: break - w64(g_sgen_constr, (si * MAX_GENERICS + zi) * 8, -1) + cell_store(g_sgen_constr, (si * MAX_GENERICS + zi) * 8, -1) zi = zi + 1 gi : ., mut = 0 loop: @@ -1656,8 +1656,8 @@ fn save_struct_gen_constrs(si: int, constrs: string, count: int): break if gi >= MAX_GENERICS: break - if r64(constrs, gi * 8) >= 0: - w64(g_sgen_constr, (si * MAX_GENERICS + gi) * 8, r64(constrs, gi * 8)) + if cell_load(constrs, gi * 8) >= 0: + cell_store(g_sgen_constr, (si * MAX_GENERICS + gi) * 8, cell_load(constrs, gi * 8)) gi = gi + 1 fn save_enum_gen_constrs(ei: int, constrs: string, count: int): @@ -1668,7 +1668,7 @@ fn save_enum_gen_constrs(ei: int, constrs: string, count: int): loop: if zi >= MAX_GENERICS: break - w64(g_egen_constr, (ei * MAX_GENERICS + zi) * 8, -1) + cell_store(g_egen_constr, (ei * MAX_GENERICS + zi) * 8, -1) zi = zi + 1 gi : ., mut = 0 loop: @@ -1676,8 +1676,8 @@ fn save_enum_gen_constrs(ei: int, constrs: string, count: int): break if gi >= MAX_GENERICS: break - if r64(constrs, gi * 8) >= 0: - w64(g_egen_constr, (ei * MAX_GENERICS + gi) * 8, r64(constrs, gi * 8)) + if cell_load(constrs, gi * 8) >= 0: + cell_store(g_egen_constr, (ei * MAX_GENERICS + gi) * 8, cell_load(constrs, gi * 8)) gi = gi + 1 fn add_func(name: string, pc: int, rt: int, an: int) -> int: @@ -1701,9 +1701,9 @@ fn add_struct(name: string) -> int: loop: if zi >= ESZ_STRUCTINFO: break - w8(g_structs, base + zi, 0) + cell_store8(g_structs, base + zi, 0) zi = zi + 1 - w64(g_structs, base + OFF_SI_NAME, ni) + cell_store(g_structs, base + OFF_SI_NAME, ni) // Field storage starts at the side table high-water mark. Writes use base + // index; si_commit_fields commits the range at the end of the body. si_set_field_base(idx, g_si_f_used) @@ -1720,9 +1720,9 @@ fn add_enum(name: string) -> int: loop: if zi >= ESZ_ENUMINFO: break - w8(g_enums, base + zi, 0) + cell_store8(g_enums, base + zi, 0) zi = zi + 1 - w64(g_enums, base + OFF_EI_NAME, ni) + cell_store(g_enums, base + OFF_EI_NAME, ni) // Variant storage starts at the side table high-water mark. Writes use base + // index; ei_commit_variants commits the range at the end of the body. ei_set_variant_base(idx, g_ei_v_used) @@ -2167,13 +2167,13 @@ fn parse_declaration(): si := add_struct(name) if si >= 0: if sg_count > 0: - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, sg_count) + cell_store(g_structs, si * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, sg_count) sgi : ., mut = 0 loop: if sgi >= sg_count: break unsafe: - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + sgi * 8, str_intern(@str_of(r64(sg_names, sgi * 8)))) + cell_store(g_structs, si * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + sgi * 8, str_intern(@str_of(cell_load(sg_names, sgi * 8)))) sgi = sgi + 1 // Register struct generic constraints in the struct side table. // The old code wrote them to a dummy buffer and dropped them, so @@ -2204,7 +2204,7 @@ fn parse_declaration(): fc = fc + 1 if check(T_COMMA): advance_tok() - w64(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) + cell_store(g_structs, si * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) si_commit_fields(si, fc) // 提交字段区(高水位 = base + fc) if has_items != 0: close_region() @@ -2229,13 +2229,13 @@ fn parse_declaration(): ei := add_enum(name) if ei >= 0: if eg_count > 0: - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, eg_count) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, eg_count) egi : ., mut = 0 loop: if egi >= eg_count: break unsafe: - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + egi * 8, str_intern(@str_of(r64(eg_names, egi * 8)))) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + egi * 8, str_intern(@str_of(cell_load(eg_names, egi * 8)))) egi = egi + 1 // Enum generic constraints are registered the same way as struct // constraints. @@ -2275,7 +2275,7 @@ fn parse_declaration(): vc = vc + 1 if check(T_COMMA): advance_tok() - w64(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) ei_commit_variants(ei, vc) // 提交变体区(高水位 = base + vc) if has_items != 0: close_region() @@ -2306,10 +2306,10 @@ fn parse_declaration(): loop: if zi >= ESZ_IFACEINFO: break - w8(g_ifaces, iface_base + zi, 0) + cell_store8(g_ifaces, iface_base + zi, 0) zi = zi + 1 - w64(g_ifaces, iface_base + OFF_IF_NAME, iface_ni) - w64(g_ifaces, iface_base + OFF_IF_GENERIC_COUNT, ig_count) + cell_store(g_ifaces, iface_base + OFF_IF_NAME, iface_ni) + cell_store(g_ifaces, iface_base + OFF_IF_GENERIC_COUNT, ig_count) method_count : ., mut = 0 loop: @@ -2334,7 +2334,7 @@ fn parse_declaration(): loop: if pi2 >= 8: break - w64(param_tis, pi2 * 8, TY_UNIT) + cell_store(param_tis, pi2 * 8, TY_UNIT) pi2 = pi2 + 1 // Signature type-node slots are written next to the raw code slots, // as struct and enum fields are. The self receiver slot has no node; @@ -2347,7 +2347,7 @@ fn parse_declaration(): loop: if pni2 >= 8: break - w64(param_nodes, pni2 * 8, -1) + cell_store(param_nodes, pni2 * 8, -1) pni2 = pni2 + 1 self_mode : ., mut = 0 if !check(T_RPAREN): @@ -2365,7 +2365,7 @@ fn parse_declaration(): // &mut self nt2 := advance_tok() // self if pc < 8: - w64(param_tis, pc * 8, 0) + cell_store(param_tis, pc * 8, 0) // match function's default for &self if pc == 0: self_mode = self_mode_t @@ -2376,8 +2376,8 @@ fn parse_declaration(): advance_tok() // : ptype := parse_type() if pc < 8: - w64(param_tis, pc * 8, unpack_type(ptype)) - w64(param_nodes, pc * 8, ptype) + cell_store(param_tis, pc * 8, unpack_type(ptype)) + cell_store(param_nodes, pc * 8, ptype) pc = pc + 1 if !check(T_COMMA): break @@ -2402,38 +2402,38 @@ fn parse_declaration(): // dyn_arr.cr constant comment. if method_count >= MAX_IFACE_METHODS: grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_FIELD_SYNTAX) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "interface '" + iface_name + "' exceeds max 16 methods") - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(t))) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_FIELD_SYNTAX) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "interface '" + iface_name + "' exceeds max 16 methods") + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(t))) g_diag_count = g_diag_count + 1 else: mbase := iface_base + OFF_IF_METHODS + method_count * ESZ_IFMETHOD - w64(g_ifaces, mbase + OFF_IFM_NAME, method_ni) - w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) - w64(g_ifaces, mbase + OFF_IFM_RET_TI, ret_ti) - w64(g_ifaces, mbase + OFF_IFM_RET_NODE, ret_node) - w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, self_mode) + cell_store(g_ifaces, mbase + OFF_IFM_NAME, method_ni) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) + cell_store(g_ifaces, mbase + OFF_IFM_RET_TI, ret_ti) + cell_store(g_ifaces, mbase + OFF_IFM_RET_NODE, ret_node) + cell_store(g_ifaces, mbase + OFF_IFM_SELF_MODE, self_mode) pj : ., mut = 0 loop: if pj >= 8 || pj >= pc: break - w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, r64(param_tis, pj * 8)) - w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, r64(param_nodes, pj * 8)) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, cell_load(param_tis, pj * 8)) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, cell_load(param_nodes, pj * 8)) pj = pj + 1 if pc > 8: grow_diags(g_diag_count + 1) - w64(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_PARAM_TYPE) - store_str_ptr(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "method '" + istr_get(method_ni) + "' in interface exceeds max 8 params") - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(t)) - w64(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(t))) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE, EC_P_PARAM_TYPE) + cell_store_str(g_diags, g_diag_count * DIAG_REC_SIZE + 8, "method '" + istr_get(method_ni) + "' in interface exceeds max 8 params") + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 16, tok_ln(t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 24, tok_cl(t)) + cell_store(g_diags, g_diag_count * DIAG_REC_SIZE + 32, diag_fileid_for_line(tok_ln(t))) g_diag_count = g_diag_count + 1 method_count = method_count + 1 else: advance_tok() - w64(g_ifaces, iface_base + OFF_IF_METHOD_COUNT, method_count) + cell_store(g_ifaces, iface_base + OFF_IF_METHOD_COUNT, method_count) g_iface_count = g_iface_count + 1 if has_items != 0: close_region() @@ -2471,9 +2471,9 @@ fn parse_declaration(): parse_body(mangled, mangled_ni, tok_ln(ft), tok_cl(ft), 0) // Register in method lookup table grow_methods(g_method_count + 1) - w64(g_methods, g_method_count * 24, type_ni) - w64(g_methods, g_method_count * 24 + 8, method_ni) - w64(g_methods, g_method_count * 24 + 16, mangled_ni) + cell_store(g_methods, g_method_count * 24, type_ni) + cell_store(g_methods, g_method_count * 24 + 8, method_ni) + cell_store(g_methods, g_method_count * 24 + 16, mangled_ni) g_method_count = g_method_count + 1 else: advance_tok() @@ -2482,8 +2482,8 @@ fn parse_declaration(): // Store impl-for relationship after processing all methods if trait_ni >= 0: grow_impl_for(g_impl_for_count + 1) - w64(g_impl_for, g_impl_for_count * 16, trait_ni) - w64(g_impl_for, g_impl_for_count * 16 + 8, type_ni) + cell_store(g_impl_for, g_impl_for_count * 16, trait_ni) + cell_store(g_impl_for, g_impl_for_count * 16 + 8, type_ni) g_impl_for_count = g_impl_for_count + 1 return @@ -2499,8 +2499,8 @@ fn parse_declaration(): type_node := parse_type() end_decl() grow_type_aliases(g_type_alias_count + 1) - w64(g_type_aliases, g_type_alias_count * 16, name_idx) - w64(g_type_aliases, g_type_alias_count * 16 + 8, type_node) + cell_store(g_type_aliases, g_type_alias_count * 16, name_idx) + cell_store(g_type_aliases, g_type_alias_count * 16 + 8, type_node) g_type_alias_count = g_type_alias_count + 1 return @@ -2520,7 +2520,7 @@ fn parse_declaration(): mod_ni := str_intern(path_name) // Store mod path for checker registration grow_mod_paths(g_mod_path_count + 1) - w64(g_mod_path_names, g_mod_path_count * 8, mod_ni) + cell_store(g_mod_path_names, g_mod_path_count * 8, mod_ni) g_mod_path_count = g_mod_path_count + 1 // `mod path` ends at a line boundary. `mod path:` with a region skips the // whole region body. @@ -2553,7 +2553,7 @@ fn parse_declaration(): if check(T_IDENT) && is_new_var_decl(): node := parse_new_var_decl() grow_global_lets(g_global_let_count + 1) - w64(g_global_lets, g_global_let_count * 8, node) + cell_store(g_global_lets, g_global_let_count * 8, node) g_global_let_count = g_global_let_count + 1 // Drain batch extras to globals _drained : ., mut = 0 @@ -2562,7 +2562,7 @@ fn parse_declaration(): break g_extra_let_count = g_extra_let_count - 1 grow_global_lets(g_global_let_count + 1) - w64(g_global_lets, g_global_let_count * 8, r64(g_extra_lets, g_extra_let_count * 8)) + cell_store(g_global_lets, g_global_let_count * 8, cell_load(g_extra_lets, g_extra_let_count * 8)) g_global_let_count = g_global_let_count + 1 _drained = _drained + 1 end_decl() // v4:全局声明以行边界收尾(原可选 `;` 退场) diff --git a/src/compiler/prelude.cr b/src/compiler/prelude.cr index b0d42ed2..e64a9cc6 100644 --- a/src/compiler/prelude.cr +++ b/src/compiler/prelude.cr @@ -1,6 +1,11 @@ // === prelude.cr === // Frontend prelude: configuration read at process start and the small set of -// externally provided builtins. +// externally provided builtins. The concrete ABI names live in the target's +// frontend_abi.cr; this file only owns the table and its reset. + +g_rt_builtin_count : int, mut +g_rt_builtin_names : string, mut +g_rt_builtin_ret_types : string, mut fn prelude_init(): g_s1p_seen = 1 @@ -9,12 +14,28 @@ fn prelude_init(): if str_len(ev2) > 0 && ev2 == "1": g_s1p_on = 1 +fn prelude_builtin_init(): + g_rt_builtin_count = 0 + g_rt_builtin_names = alloc(8 * 8) + g_rt_builtin_ret_types = alloc(8 * 8) + target_builtin_register() + +fn prelude_builtin_add(name: string, ret_ti: int): + ni := str_intern(name) + if g_rt_builtin_count * 8 + 8 > str_len(g_rt_builtin_names): + nc := g_rt_builtin_count * 2 + 16 + nb := alloc(nc * 8) + cell_copy(g_rt_builtin_names, g_rt_builtin_count * 8, nb) + g_rt_builtin_names = nb + if g_rt_builtin_count * 8 + 8 > str_len(g_rt_builtin_ret_types): + nc := g_rt_builtin_count * 2 + 16 + nb := alloc(nc * 8) + cell_copy(g_rt_builtin_ret_types, g_rt_builtin_count * 8, nb) + g_rt_builtin_ret_types = nb + cell_store(g_rt_builtin_names, g_rt_builtin_count * 8, ni) + cell_store(g_rt_builtin_ret_types, g_rt_builtin_count * 8, ret_ti) + g_rt_builtin_count = g_rt_builtin_count + 1 + fn prelude_builtin_return_ti(name_ni: int) -> int: - if name_ni < 0: - return -1 - s := istr_get(name_ni) - if s == "syscall3": - return TI_INT - if s == "syscall4": - return TI_INT - return -1 + return target_builtin_return_ti(name_ni) + diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index f015dd75..52c4a66d 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -8,10 +8,10 @@ fn get_alloc_size(alloc_seq: int) -> int: // g_pa_alloc_nodes before reading the allocation node. if alloc_seq < 0 || alloc_seq >= g_pa_alloc_count: return -1 - an := r64(g_pa_alloc_nodes, alloc_seq * 8) - op := r64(g_df_nodes, an * ESZ_DFNODE + OFF_DF_OPCODE) - s1 := r64(g_df_nodes, an * ESZ_DFNODE + OFF_DF_S1) - s3 := r64(g_df_nodes, an * ESZ_DFNODE + OFF_DF_S3) + an := cell_load(g_pa_alloc_nodes, alloc_seq * 8) + op := cell_load(g_df_nodes, an * ESZ_DFNODE + OFF_DF_OPCODE) + s1 := cell_load(g_df_nodes, an * ESZ_DFNODE + OFF_DF_S1) + s3 := cell_load(g_df_nodes, an * ESZ_DFNODE + OFF_DF_S3) // IR_ALLOC marks a scalar variable slot, not a heap allocation, so it has no // allocation size. @@ -37,18 +37,18 @@ fn get_alloc_size(alloc_seq: int) -> int: fn get_alloc_var(alloc_seq: int) -> int: if alloc_seq < 0 || alloc_seq >= g_pa_alloc_count: return -1 - an := r64(g_pa_alloc_nodes, alloc_seq * 8) - return r64(g_df_nodes, an * ESZ_DFNODE + OFF_DF_DEST) + an := cell_load(g_pa_alloc_nodes, alloc_seq * 8) + return cell_load(g_df_nodes, an * ESZ_DFNODE + OFF_DF_DEST) fn pv_is_in_unsafe(node_seq: int) -> int: si : ., mut = 0 loop: if si >= g_sg_count: break - kind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) + kind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) if kind == SG_UNSAFE: - nstart := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) - ncount := r64(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) + nstart := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + ncount := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) if node_seq >= nstart && node_seq < nstart + ncount: return 1 si = si + 1 @@ -59,12 +59,12 @@ fn provenance_verify_func(nstart: int, ncount: int): loop: if ni >= nstart + ncount: break - op := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) - s1 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) + op := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) + s1 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) // Read the access width in bytes from OFF_DF_AUX. Before the single-slot // change it lived in the shared tk slot. Reading slot 40 would treat a // type-term index as a width. - width : ., mut = r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX) + width : ., mut = cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX) if (op == IR_DEREF || op == IR_STORE_PTR) && s1 >= 0: // Skip checks in unsafe blocks @@ -89,8 +89,8 @@ fn provenance_verify_func(nstart: int, ncount: int): if width <= 0: width = 8 - pts := r64(g_pts, s1 * 8) - off := r64(g_offsets, s1 * 8) + pts := cell_load(g_pts, s1 * 8) + off := cell_load(g_offsets, s1 * 8) runtime_targets : ., mut = 0 runtime_size : ., mut = 0 runtime_base : ., mut = -1 @@ -127,12 +127,12 @@ fn provenance_verify_func(nstart: int, ncount: int): bi = bi + 1 if off < 0 && runtime_targets == 1 && runtime_base >= 0: - w64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3, runtime_size) + cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3, runtime_size) if op == IR_DEREF: - w64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2, runtime_base) + cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2, runtime_base) else: // IR_STORE_PTR has no result, so dest carries the runtime base. - w64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST, runtime_base) + cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST, runtime_base) else if off < 0 && runtime_targets > 1: check_error(EC_TK_INDEX, "runtime pointer bounds check has multiple allocation targets", @@ -145,7 +145,7 @@ fn provenance_verify_all(): loop: if fi >= g_ir_func_count: break - nstart := r64(g_df_func_node_start, fi * 8) - ncount := r64(g_df_func_node_count, fi * 8) + nstart := cell_load(g_df_func_node_start, fi * 8) + ncount := cell_load(g_df_func_node_count, fi * 8) provenance_verify_func(nstart, ncount) fi = fi + 1 diff --git a/src/compiler/ptr_analysis.cr b/src/compiler/ptr_analysis.cr index cd5cd3b6..89db534c 100644 --- a/src/compiler/ptr_analysis.cr +++ b/src/compiler/ptr_analysis.cr @@ -18,10 +18,10 @@ fn pa_in_unsafe(node_seq: int) -> int: loop: if si >= g_sg_count: break - kind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) + kind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) if kind == SG_UNSAFE: - nstart := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) - ncount := r64(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) + nstart := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + ncount := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) if node_seq >= nstart && node_seq < nstart + ncount: return 1 si = si + 1 @@ -43,7 +43,7 @@ fn grow_alloc_pts(n: int): nc = nc * 2 nb := alloc(nc * 8) if g_alloc_pts_cap > 0: - _dyncpy(g_alloc_pts, g_alloc_pts_cap * 8, nb) + cell_copy(g_alloc_pts, g_alloc_pts_cap * 8, nb) g_alloc_pts = nb g_alloc_pts_cap = nc @@ -59,7 +59,7 @@ fn grow_pa_alloc_nodes(n: int): nc = nc * 2 nb := alloc(nc * 8) if g_pa_alloc_nodes_cap > 0: - _dyncpy(g_pa_alloc_nodes, g_pa_alloc_nodes_cap * 8, nb) + cell_copy(g_pa_alloc_nodes, g_pa_alloc_nodes_cap * 8, nb) g_pa_alloc_nodes = nb g_pa_alloc_nodes_cap = nc @@ -94,8 +94,8 @@ fn pa_merge_pts(dst_var: int, src_var: int) -> int: grow_pts(dst_var + 1) if g_pts_cap <= src_var: grow_pts(src_var + 1) - dst := r64(g_pts, dst_var * 8) - src := r64(g_pts, src_var * 8) + dst := cell_load(g_pts, dst_var * 8) + src := cell_load(g_pts, src_var * 8) if src == 0: return 0 // Union: start from dst and add src bits that are not set. @@ -112,7 +112,7 @@ fn pa_merge_pts(dst_var: int, src_var: int) -> int: mask = mask * 2 bi = bi + 1 if changed != 0: - w64(g_pts, dst_var * 8, out_val) + cell_store(g_pts, dst_var * 8, out_val) return changed // Merge alloc_pts[alloc_seq] into pts[dst_var] and return 1 when changed. @@ -123,10 +123,10 @@ fn pa_merge_alloc_pts(dst_var: int, alloc_seq: int) -> int: grow_pts(dst_var + 1) if g_alloc_pts_cap <= alloc_seq: grow_alloc_pts(alloc_seq + 1) - src := r64(g_alloc_pts, alloc_seq * 8) + src := cell_load(g_alloc_pts, alloc_seq * 8) if src == 0: return 0 - prev := r64(g_pts, dst_var * 8) + prev := cell_load(g_pts, dst_var * 8) changed : int, mut = 0 mask : int, mut = 1 bi : ., mut = 0 @@ -139,16 +139,16 @@ fn pa_merge_alloc_pts(dst_var: int, alloc_seq: int) -> int: mask = mask * 2 bi = bi + 1 if changed != 0: - w64(g_pts, dst_var * 8, prev) + cell_store(g_pts, dst_var * 8, prev) return changed // Propagate pts[src_var] into alloc_pts[alloc_seq] and return 1 when changed. fn pa_store_to_alloc(alloc_seq: int, src_var: int) -> int: - src := r64(g_pts, src_var * 8) + src := cell_load(g_pts, src_var * 8) if src == 0: return 0 grow_alloc_pts(alloc_seq + 1) - prev := r64(g_alloc_pts, alloc_seq * 8) + prev := cell_load(g_alloc_pts, alloc_seq * 8) changed : int, mut = 0 mask : int, mut = 1 bi : ., mut = 0 @@ -161,13 +161,13 @@ fn pa_store_to_alloc(alloc_seq: int, src_var: int) -> int: mask = mask * 2 bi = bi + 1 if changed != 0: - w64(g_alloc_pts, alloc_seq * 8, prev) + cell_store(g_alloc_pts, alloc_seq * 8, prev) return changed // Load rule: for each allocation in pts(p), add alloc_pts[alloc] to pts(r). fn pa_load(deref_node: int, dst_var: int, ptr_var: int) -> int: changed : int, mut = 0 - pts_ptr := r64(g_pts, ptr_var * 8) + pts_ptr := cell_load(g_pts, ptr_var * 8) mask : int, mut = 1 bi : ., mut = 0 loop: @@ -183,7 +183,7 @@ fn pa_load(deref_node: int, dst_var: int, ptr_var: int) -> int: // Store rule: for each allocation in pts(p), add pts(v) to alloc_pts[alloc]. fn pa_store(ptr_var: int, val_var: int) -> int: changed : int, mut = 0 - pts_ptr := r64(g_pts, ptr_var * 8) + pts_ptr := cell_load(g_pts, ptr_var * 8) mask : int, mut = 1 bi : ., mut = 0 loop: @@ -204,9 +204,9 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): break var_idx := vstart + vi grow_pts(var_idx + 1) - w64(g_pts, var_idx * 8, 0) + cell_store(g_pts, var_idx * 8, 0) grow_offsets(var_idx + 1) - w64(g_offsets, var_idx * 8, 0) + cell_store(g_offsets, var_idx * 8, 0) vi = vi + 1 changed : int, mut = 1 @@ -224,11 +224,11 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): loop: if ni >= nstart + ncount: break - op := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) - d := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) - s1 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) - s2 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2) - s3 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3) + op := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) + d := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) + s1 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) + s2 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2) + s3 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3) // Suppress pointer tracking inside unsafe blocks. if pa_in_unsafe(ni) != 0: @@ -240,22 +240,22 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if op == IR_ALLOC_STRUCT || op == IR_ALLOC_ARRAY: // IR_ALLOC marks a scalar slot and is not tracked. Only heap // allocations receive points-to bits. - if r64(g_pts, d * 8) == 0: + if cell_load(g_pts, d * 8) == 0: // Give each allocation its own bit and record the mapping // from bit to DF node for size queries. if g_pa_alloc_count < 64: grow_pa_alloc_nodes(g_pa_alloc_count + 1) - w64(g_pa_alloc_nodes, g_pa_alloc_count * 8, ni) - w64(g_pts, d * 8, pa_set_bit(0, g_pa_alloc_count)) + cell_store(g_pa_alloc_nodes, g_pa_alloc_count * 8, ni) + cell_store(g_pts, d * 8, pa_set_bit(0, g_pa_alloc_count)) g_pa_alloc_count = g_pa_alloc_count + 1 changed = 1 - w64(g_offsets, d * 8, 0) + cell_store(g_offsets, d * 8, 0) if op == IR_REF && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 // Propagate the offset. - w64(g_offsets, d * 8, r64(g_offsets, s1 * 8)) + cell_store(g_offsets, d * 8, cell_load(g_offsets, s1 * 8)) if op == IR_ADDR_INDEX && s1 >= 0: if pa_merge_pts(d, s1) != 0: @@ -263,61 +263,61 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): // The index decides the offset. A constant index gives // index times the mapping stride; a dynamic index gives -1, // which makes provenance_verify emit a runtime check. - base_off := r64(g_offsets, s1 * 8) + base_off := cell_load(g_offsets, s1 * 8) idx_val : int = -1 if s2 >= 0: - prod := r64(g_df_var_producer, s2 * 8) + prod := cell_load(g_df_var_producer, s2 * 8) if prod >= 0: - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_CONST: - idx_val = r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) + idx_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if idx_val >= 0 && base_off >= 0: - w64(g_offsets, d * 8, base_off + mp_offset(idx_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off + mp_offset(idx_val, TI_INT)) else: - w64(g_offsets, d * 8, -1) + cell_store(g_offsets, d * 8, -1) // IR_SLICE propagates the underlying array provenance for // d = &arr[low]. A constant low gives low times the mapping stride. if op == IR_SLICE && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 - base_off := r64(g_offsets, s1 * 8) + base_off := cell_load(g_offsets, s1 * 8) low_val : int = -1 if s2 >= 0: - prod := r64(g_df_var_producer, s2 * 8) + prod := cell_load(g_df_var_producer, s2 * 8) if prod >= 0: - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_CONST: - low_val = r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) + low_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if low_val >= 0 && base_off >= 0: - w64(g_offsets, d * 8, base_off + mp_offset(low_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off + mp_offset(low_val, TI_INT)) else: - w64(g_offsets, d * 8, -1) + cell_store(g_offsets, d * 8, -1) // Pointer add and subtract propagate provenance and offset. if op == IR_BINARY && (s3 == OP_PTR_ADD || s3 == OP_PTR_SUB) && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 // Evaluate a constant offset precisely. - base_off := r64(g_offsets, s1 * 8) + base_off := cell_load(g_offsets, s1 * 8) off_val : ., mut = 0 // Check whether s2 comes from a CONST instruction. if s2 >= 0: - prod := r64(g_df_var_producer, s2 * 8) + prod := cell_load(g_df_var_producer, s2 * 8) if prod >= 0: - prod_op := r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) + prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_CONST: - off_val = r64(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) + off_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if s3 == OP_PTR_ADD: - w64(g_offsets, d * 8, base_off + off_val * 8) + cell_store(g_offsets, d * 8, base_off + mp_offset(off_val, TI_INT)) else: - w64(g_offsets, d * 8, base_off - off_val * 8) + cell_store(g_offsets, d * 8, base_off - mp_offset(off_val, TI_INT)) // LOAD: copy provenance from s1 to d. if op == IR_LOAD && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 - w64(g_offsets, d * 8, r64(g_offsets, s1 * 8)) + cell_store(g_offsets, d * 8, cell_load(g_offsets, s1 * 8)) // IR_STORE is handled outside the d >= 0 block because its dest // is always -1. @@ -327,17 +327,17 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): changed = 1 if s2 >= 0 && pa_merge_pts(d, s2) != 0: changed = 1 - w64(g_offsets, d * 8, 0) // approximate offset after the merge + cell_store(g_offsets, d * 8, 0) // approximate offset after the merge // CALL: leave the result set empty. if op == IR_CALL: - w64(g_offsets, d * 8, 0) + cell_store(g_offsets, d * 8, 0) // STORE: copy provenance from s2 to s1. if op == IR_STORE && s1 >= 0 && s2 >= 0: if pa_merge_pts(s1, s2) != 0: changed = 1 - w64(g_offsets, s1 * 8, r64(g_offsets, s2 * 8)) + cell_store(g_offsets, s1 * 8, cell_load(g_offsets, s2 * 8)) // Store rule: s1 is the pointer and s2 the value. if op == IR_STORE_PTR && s1 >= 0 && s2 >= 0: @@ -360,9 +360,9 @@ fn ptr_analysis_all(): loop: if fi >= g_ir_func_count: break - nstart := r64(g_df_func_node_start, fi * 8) - ncount := r64(g_df_func_node_count, fi * 8) - vstart := r64(g_ir_func_var_start, fi * 8) - vcount := r64(g_ir_func_var_count, fi * 8) + nstart := cell_load(g_df_func_node_start, fi * 8) + ncount := cell_load(g_df_func_node_count, fi * 8) + vstart := cell_load(g_ir_func_var_start, fi * 8) + vcount := cell_load(g_ir_func_var_count, fi * 8) ptr_analysis_func(nstart, ncount, vstart, vcount) fi = fi + 1 diff --git a/src/compiler/purity_selftest.cr b/src/compiler/purity_selftest.cr index 045a00d7..e36f4e11 100644 --- a/src/compiler/purity_selftest.cr +++ b/src/compiler/purity_selftest.cr @@ -80,7 +80,7 @@ fn ps_ir_index_of(name: string) -> int: loop: if irf >= g_ir_func_count: return -1 - if r64(g_ir_func_name_idx, irf * 8) == ni: + if cell_load(g_ir_func_name_idx, irf * 8) == ni: return irf irf = irf + 1 return -1 @@ -88,14 +88,14 @@ fn ps_ir_index_of(name: string) -> int: // Index of the first DF node in function irf that calls callee. -1 means none. fn ps_call_node(irf: int, callee: string) -> int: cni := str_intern(callee) - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) n : ., mut = start loop: if n >= start + cnt: return -1 - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: return n n = n + 1 return -1 @@ -106,15 +106,15 @@ fn ps_count_call_nodes(irf: int, callee: string) -> int: if irf < 0: return -1 cni := str_intern(callee) - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) r : ., mut = 0 n : ., mut = start loop: if n >= start + cnt: return r - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: r = r + 1 n = n + 1 return r @@ -123,13 +123,13 @@ fn ps_count_call_nodes(irf: int, callee: string) -> int: fn ps_op_node(irf: int, opcode: int) -> int: if irf < 0: return -1 - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) n : ., mut = start loop: if n >= start + cnt: return -1 - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == opcode: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == opcode: return n n = n + 1 return -1 @@ -155,15 +155,15 @@ fn ps_calls_in_chain(irf: int, callee: string) -> int: if irf < 0: return -1 cni := str_intern(callee) - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) r : ., mut = 0 n : ., mut = start loop: if n >= start + cnt: return r - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: - if r64(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_OPCODE) == IR_CALL: + if cell_load(g_df_nodes, n * ESZ_DFNODE + OFF_DF_S3) == cni: if ps_has_state_in(n) == 1: r = r + 1 n = n + 1 @@ -178,10 +178,10 @@ fn ps_state_touch(nid: int) -> int: loop: if e >= g_df_edge_count: break - if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1: - if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) == nid: + if cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1: + if cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) == nid: r = r + 1 - if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_FROM) == nid: + if cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_FROM) == nid: r = r + 2 e = e + 1 return r @@ -189,16 +189,16 @@ fn ps_state_touch(nid: int) -> int: // Whether function irf has a kind-1 edge to a label node. A loop exit must // depend on the last side effect in the loop body. fn ps_has_label_state_target(irf: int) -> int: - start := r64(g_df_func_node_start, irf * 8) - cnt := r64(g_df_func_node_count, irf * 8) + start := cell_load(g_df_func_node_start, irf * 8) + cnt := cell_load(g_df_func_node_count, irf * 8) e : ., mut = 0 loop: if e >= g_df_edge_count: break - if r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1: - tgt := r64(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) + if cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_KIND) == 1: + tgt := cell_load(g_df_edges, e * ESZ_DFEDGE + OFF_DFE_TO) if tgt >= start && tgt < start + cnt: - if r64(g_df_nodes, tgt * ESZ_DFNODE + OFF_DF_OPCODE) == IR_LABEL: + if cell_load(g_df_nodes, tgt * ESZ_DFNODE + OFF_DF_OPCODE) == IR_LABEL: return 1 e = e + 1 return 0 @@ -214,7 +214,7 @@ fn purity_selftest_run() -> int: src = src + "fn call_effect():\n effect()\n" src = src + "extern fn ext_probe(x: int) -> int\n" src = src + "fn call_extern(x: int) -> int:\n return ext_probe(x)\n" - src = src + "fn call_builtin(x: int) -> int:\n return load64(x)\n" + src = src + "fn call_builtin(x: int) -> int:\n return cell_load64(x)\n" src = src + "fn self_rec(n: int) -> int:\n if n <= 0:\n return 0\n return self_rec(n - 1)\n" src = src + "fn mut_rec_a(n: int) -> int:\n if n <= 0:\n return 0\n return mut_rec_b(n - 1)\n" src = src + "fn mut_rec_b(n: int) -> int:\n if n <= 0:\n return 0\n return mut_rec_a(n - 1)\n" diff --git a/src/compiler/region_check.cr b/src/compiler/region_check.cr index 422e16e0..c501de41 100644 --- a/src/compiler/region_check.cr +++ b/src/compiler/region_check.cr @@ -5,7 +5,7 @@ fn subgraph_containing(node_seq: int) -> int: // O(1) region lookup. df_create_node writes g_df_node_region. if node_seq >= 0 && node_seq < g_df_node_count: - return r64(g_df_node_region, node_seq * 8) + return cell_load(g_df_node_region, node_seq * 8) return -1 fn is_in_unsafe(node_seq: int) -> int: @@ -13,10 +13,10 @@ fn is_in_unsafe(node_seq: int) -> int: loop: if si >= g_sg_count: break - kind := r64(g_sgs, si * ESZ_SG + OFF_SG_KIND) + kind := cell_load(g_sgs, si * ESZ_SG + OFF_SG_KIND) if kind == SG_UNSAFE: - nstart := r64(g_sgs, si * ESZ_SG + OFF_SG_NSTART) - ncount := r64(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) + nstart := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NSTART) + ncount := cell_load(g_sgs, si * ESZ_SG + OFF_SG_NCOUNT) if node_seq >= nstart && node_seq < nstart + ncount: return 1 si = si + 1 @@ -44,7 +44,7 @@ fn is_in_unsafe(node_seq: int) -> int: fn rc_sg_releases(sg: int) -> int: if sg < 0: return 0 - k := r64(g_sgs, sg * ESZ_SG + OFF_SG_KIND) + k := cell_load(g_sgs, sg * ESZ_SG + OFF_SG_KIND) if k == SG_LOOP || k == SG_FOR || k == SG_UNSAFE: return 1 return 0 @@ -59,16 +59,16 @@ fn rc_releasing_region(alloc_node: int) -> int: return -1 if rc_sg_releases(sg) != 0: return sg - sg = r64(g_sgs, sg * ESZ_SG + OFF_SG_PARENT) + sg = cell_load(g_sgs, sg * ESZ_SG + OFF_SG_PARENT) // Release node for allocation bit bi, mapped through g_pa_alloc_nodes to a DF // node. Returns the EXIT ordinal, or -1 when no region releases the allocation. fn rc_bit_exit(bi: int) -> int: - alloc_node := r64(g_pa_alloc_nodes, bi * 8) + alloc_node := cell_load(g_pa_alloc_nodes, bi * 8) sg := rc_releasing_region(alloc_node) if sg < 0: return -1 - return r64(g_sgs, sg * ESZ_SG + OFF_SG_EXIT) + return cell_load(g_sgs, sg * ESZ_SG + OFF_SG_EXIT) fn rc_bit_dead(bi: int, ni: int) -> int: ex := rc_bit_exit(bi) @@ -108,22 +108,22 @@ fn rc_reassign_clears(ptr_var: int, lo: int, hi: int) -> int: loop: if i >= hi: break - op := r64(g_df_nodes, i * ESZ_DFNODE + OFF_DF_OPCODE) - d := r64(g_df_nodes, i * ESZ_DFNODE + OFF_DF_DEST) - s1 := r64(g_df_nodes, i * ESZ_DFNODE + OFF_DF_S1) - s2 := r64(g_df_nodes, i * ESZ_DFNODE + OFF_DF_S2) + op := cell_load(g_df_nodes, i * ESZ_DFNODE + OFF_DF_OPCODE) + d := cell_load(g_df_nodes, i * ESZ_DFNODE + OFF_DF_DEST) + s1 := cell_load(g_df_nodes, i * ESZ_DFNODE + OFF_DF_S1) + s2 := cell_load(g_df_nodes, i * ESZ_DFNODE + OFF_DF_S2) if op == IR_STORE && s1 == ptr_var: if s2 < 0: return 0 - if rc_pts_dead(r64(g_pts, s2 * 8), i) != 0: + if rc_pts_dead(cell_load(g_pts, s2 * 8), i) != 0: return 0 saw_safe = 1 if op == IR_PHI && d == ptr_var: if s1 < 0 || s2 < 0: return 0 - if rc_pts_dead(r64(g_pts, s1 * 8), i) != 0: + if rc_pts_dead(cell_load(g_pts, s1 * 8), i) != 0: return 0 - if rc_pts_dead(r64(g_pts, s2 * 8), i) != 0: + if rc_pts_dead(cell_load(g_pts, s2 * 8), i) != 0: return 0 saw_safe = 1 i = i + 1 @@ -138,7 +138,7 @@ fn rc_var_dead(ptr_var: int, ni: int) -> int: ti := irv_type(ptr_var) if ti < 0 || (get_type_kind(ti) != TYP_PTR && get_type_kind(ti) != TYP_REF): return 0 - pts := r64(g_pts, ptr_var * 8) + pts := cell_load(g_pts, ptr_var * 8) if pts == 0: return 0 mask : int, mut = 1 @@ -181,10 +181,10 @@ fn region_check_func(nstart: int, ncount: int): loop: if ni >= nstart + ncount: break - op := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) - d := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) - s1 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) - s2 := r64(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2) + op := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) + d := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST) + s1 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) + s2 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2) if is_in_unsafe(ni) != 0: ni = ni + 1 @@ -212,7 +212,7 @@ fn region_check_all(): loop: if fi >= g_ir_func_count: break - nstart := r64(g_df_func_node_start, fi * 8) - ncount := r64(g_df_func_node_count, fi * 8) + nstart := cell_load(g_df_func_node_start, fi * 8) + ncount := cell_load(g_df_func_node_count, fi * 8) region_check_func(nstart, ncount) fi = fi + 1 diff --git a/src/compiler/ty_shadow.cr b/src/compiler/ty_shadow.cr index bb770851..76b86151 100644 --- a/src/compiler/ty_shadow.cr +++ b/src/compiler/ty_shadow.cr @@ -93,7 +93,7 @@ fn sh_map_cap_init(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_term_map = nb g_term_map_cap = nc @@ -109,7 +109,7 @@ fn sh_map_find_nogrow(ti: int) -> int: p : ., mut = tt_mod(ti, cap) ret : ., mut = -1 loop: - k := r64(g_term_map, p * 16) + k := cell_load(g_term_map, p * 16) if k < 0: ret = p break @@ -141,7 +141,7 @@ fn sh_map_rehash(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_term_map = nb g_term_map_cap = nc @@ -150,11 +150,11 @@ fn sh_map_rehash(): loop: if j >= old_cap: break - k := r64(old, j * 16) + k := cell_load(old, j * 16) if k >= 0: s := sh_map_find_nogrow(k) - w64(g_term_map, s * 16, k) - w64(g_term_map, s * 16 + 8, r64(old, j * 16 + 8)) + cell_store(g_term_map, s * 16, k) + cell_store(g_term_map, s * 16 + 8, cell_load(old, j * 16 + 8)) g_term_map_entries = g_term_map_entries + 1 j = j + 1 @@ -162,7 +162,7 @@ fn sh_map_rehash(): // The layout is read from checker's TUPLE branch and field access: // data = field count; extra = start index of field type rows in // g_gen_apply_data, 8 bytes per element. Field i is -// r64(g_gen_apply_data, (extra + i) * 8). There is no g_tuple_* table. +// cell_load(g_gen_apply_data, (extra + i) * 8). There is no g_tuple_* table. // CONS 列表逆序构造(i 自末尾向前 cons):DAG 项不可变,正向追加需改写已建项, // 不可行;逆序构造的结果顺序仍 = 声明顺序。 // ─── 序列项 / ref mut 标记(桥接的两个新构造点)─── @@ -210,7 +210,7 @@ fn sh_tuple_to_product(ti: int) -> int: loop: if i < 0: break - et := sh_term_of_ti(r64(g_gen_apply_data, (start + i) * 8)) + et := sh_term_of_ti(cell_load(g_gen_apply_data, (start + i) * 8)) if et < 0: return -1 // 任一字段无法翻译 -> 整项无法翻译(不缓存) @@ -256,7 +256,7 @@ fn sh_named_identity_term(ti: int) -> int: // 1 = 签名数据递归 sh_sig_term_of_ti)。 fn sh_apply_args_chain(ti: int, variant: int) -> int: start := get_type_extra(ti) - cnt := r64(g_gen_apply_data, start * 8) + cnt := cell_load(g_gen_apply_data, start * 8) if cnt < 0: return -1 tail : ., mut = tt_nil() @@ -264,7 +264,7 @@ fn sh_apply_args_chain(ti: int, variant: int) -> int: loop: if j < 0: break - ai := r64(g_gen_apply_data, (start + 1 + j) * 8) + ai := cell_load(g_gen_apply_data, (start + 1 + j) * 8) at : ., mut = -1 if variant == 0: at = sh_term_of_ti(ai) @@ -301,8 +301,8 @@ fn sh_term_of_ti(ti: int) -> int: if nak >= 0: return tt_atom(nak, ti, -1) slot := sh_map_find(ti) - if r64(g_term_map, slot * 16) == ti: - return r64(g_term_map, slot * 16 + 8) + if cell_load(g_term_map, slot * 16) == ti: + return cell_load(g_term_map, slot * 16 + 8) // 未命中:按 kind 翻译(递归子项) term : ., mut = -1 k1 := get_type_kind(ti) @@ -366,10 +366,10 @@ fn sh_term_of_ti(ti: int) -> int: return -1 // 写回前重探(递归翻译期间可能已扩容,见文件头注记2) slot2 := sh_map_find(ti) - if r64(g_term_map, slot2 * 16) == ti: - return r64(g_term_map, slot2 * 16 + 8) // 后备:递归期间同 ti 已入表 - w64(g_term_map, slot2 * 16, ti) - w64(g_term_map, slot2 * 16 + 8, term) + if cell_load(g_term_map, slot2 * 16) == ti: + return cell_load(g_term_map, slot2 * 16 + 8) // 后备:递归期间同 ti 已入表 + cell_store(g_term_map, slot2 * 16, ti) + cell_store(g_term_map, slot2 * 16 + 8, term) g_term_map_entries = g_term_map_entries + 1 return term @@ -669,7 +669,7 @@ fn sh_unf_cap_init(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_unf_map = nb g_unf_map_cap = nc @@ -682,7 +682,7 @@ fn sh_unf_find_nogrow(ti: int) -> int: p : ., mut = tt_mod(ti, cap) ret : ., mut = -1 loop: - k := r64(g_unf_map, p * 16) + k := cell_load(g_unf_map, p * 16) if k < 0: ret = p break @@ -713,7 +713,7 @@ fn sh_unf_rehash(): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 g_unf_map = nb g_unf_map_cap = nc @@ -722,11 +722,11 @@ fn sh_unf_rehash(): loop: if j >= old_cap: break - k := r64(old, j * 16) + k := cell_load(old, j * 16) if k >= 0: s := sh_unf_find_nogrow(k) - w64(g_unf_map, s * 16, k) - w64(g_unf_map, s * 16 + 8, r64(old, j * 16 + 8)) + cell_store(g_unf_map, s * 16, k) + cell_store(g_unf_map, s * 16 + 8, cell_load(old, j * 16 + 8)) g_unf_entries = g_unf_entries + 1 j = j + 1 @@ -765,14 +765,14 @@ fn sh_struct_field_term(sa: int, fi: int, ga: int) -> int: if is_struct_generic(sa, fni): if ga >= 0 && get_type_kind(ga) == TYP_GENERIC_APPLY: gstart := get_type_extra(ga) - gcnt := r64(g_gen_apply_data, gstart * 8) + gcnt := cell_load(g_gen_apply_data, gstart * 8) gpi : ., mut = 0 loop: if gpi >= si_generic_count(sa): break if si_generic_name(sa, gpi) == fni: if gpi < gcnt: - return sh_term_of_ti(r64(g_gen_apply_data, (gstart + 1 + gpi) * 8)) + return sh_term_of_ti(cell_load(g_gen_apply_data, (gstart + 1 + gpi) * 8)) break // 实参缺位 -> 落空到名义项(下方 res_type_node),不得取越界槽 gpi = gpi + 1 fti := res_type_node(fnode) @@ -792,9 +792,9 @@ fn sh_struct_term(ti: int) -> int: if sa < 0: return -1 slot := sh_unf_find(ti) - if r64(g_unf_map, slot * 16) == ti: + if cell_load(g_unf_map, slot * 16) == ti: g_unf_hits = g_unf_hits + 1 - return r64(g_unf_map, slot * 16 + 8) + return cell_load(g_unf_map, slot * 16 + 8) ga : ., mut = -1 if get_type_kind(ti) == TYP_GENERIC_APPLY: ga = ti @@ -817,11 +817,11 @@ fn sh_struct_term(ti: int) -> int: term := tt_atom(AK_PRODUCT, ti, tail) // 写回前重探(字段解析期间可能已扩容,照 sh_term_of_ti 注记2同款) slot2 := sh_unf_find(ti) - if r64(g_unf_map, slot2 * 16) == ti: + if cell_load(g_unf_map, slot2 * 16) == ti: g_unf_hits = g_unf_hits + 1 - return r64(g_unf_map, slot2 * 16 + 8) - w64(g_unf_map, slot2 * 16, ti) - w64(g_unf_map, slot2 * 16 + 8, term) + return cell_load(g_unf_map, slot2 * 16 + 8) + cell_store(g_unf_map, slot2 * 16, ti) + cell_store(g_unf_map, slot2 * 16 + 8, term) g_unf_entries = g_unf_entries + 1 return term @@ -854,9 +854,9 @@ fn sh_enum_domain_term(ti: int) -> int: if ea < 0: return -1 slot := sh_unf_find(ti) - if r64(g_unf_map, slot * 16) == ti: + if cell_load(g_unf_map, slot * 16) == ti: g_unf_hits = g_unf_hits + 1 - return r64(g_unf_map, slot * 16 + 8) + return cell_load(g_unf_map, slot * 16 + 8) acc : ., mut = tt_bot() i : ., mut = 0 loop: @@ -870,11 +870,11 @@ fn sh_enum_domain_term(ti: int) -> int: acc = tt_union(acc, v) i = i + 1 slot2 := sh_unf_find(ti) - if r64(g_unf_map, slot2 * 16) == ti: + if cell_load(g_unf_map, slot2 * 16) == ti: g_unf_hits = g_unf_hits + 1 - return r64(g_unf_map, slot2 * 16 + 8) - w64(g_unf_map, slot2 * 16, ti) - w64(g_unf_map, slot2 * 16 + 8, acc) + return cell_load(g_unf_map, slot2 * 16 + 8) + cell_store(g_unf_map, slot2 * 16, ti) + cell_store(g_unf_map, slot2 * 16 + 8, acc) g_unf_entries = g_unf_entries + 1 return acc @@ -947,7 +947,7 @@ fn sh_sig_term_of_ti(ti: int) -> int: loop: if i < 0: break - et := sh_sig_term_of_ti(r64(g_gen_apply_data, (start + i) * 8)) + et := sh_sig_term_of_ti(cell_load(g_gen_apply_data, (start + i) * 8)) if et < 0: return -1 tail = tt_cons(et, tail) @@ -979,23 +979,23 @@ fn sh_iface_sig_param_term(ii: int, mi: int, idx: int) -> int: if idx >= MAX_IFACE_METHOD_PARAMS: return -1 mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - if r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) <= idx: + if cell_load(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) <= idx: return -1 - smode := r64(g_ifaces, mbase + OFF_IFM_SELF_MODE) + smode := cell_load(g_ifaces, mbase + OFF_IFM_SELF_MODE) if idx == 0 && smode != 0: return sh_sig_term_of_ti(TI_UNIT) - return sh_sig_term_of_node(r64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + idx * 8)) + return sh_sig_term_of_node(cell_load(g_ifaces, mbase + OFF_IFM_PARAM_NODES + idx * 8)) fn sh_iface_sig_ret_term(ii: int, mi: int) -> int: if ii < 0 || mi < 0: return -1 mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD - return sh_sig_term_of_node(r64(g_ifaces, mbase + OFF_IFM_RET_NODE)) + return sh_sig_term_of_node(cell_load(g_ifaces, mbase + OFF_IFM_RET_NODE)) fn sh_iface_self_mode(ii: int, mi: int) -> int: if ii < 0 || mi < 0: return -1 - return r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD + OFF_IFM_SELF_MODE) + return cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD + OFF_IFM_SELF_MODE) // impl 侧(函数行)签名的参数项(idx 自 0 起;从 AST 参数列表前扫解析,形参节点不连续, // 每参的类型节点插在其后,照 ir_gen/checker 同款扫描约定)。接收者槽同占位约定。 @@ -1108,7 +1108,7 @@ fn sh_iface_shape_term(iface_ni: int) -> int: ii := find_iface(iface_ni) if ii < 0: return -1 - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) if mc < 0: return -1 if mc > MAX_IFACE_METHODS: @@ -1119,8 +1119,8 @@ fn sh_iface_shape_term(iface_ni: int) -> int: if i < 0: break mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + i * ESZ_IFMETHOD - m_ni := r64(g_ifaces, mbase + OFF_IFM_NAME) - pc := r64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) + m_ni := cell_load(g_ifaces, mbase + OFF_IFM_NAME) + pc := cell_load(g_ifaces, mbase + OFF_IFM_PARAM_COUNT) if pc < 0 || pc > MAX_IFACE_METHOD_PARAMS: return -1 rt2 := sh_iface_sig_ret_term(ii, i) @@ -1278,12 +1278,12 @@ fn grow_cov_bits(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc) - _dyncpy(g_cov_bits, g_cov_cap, nb) + cell_copy(g_cov_bits, g_cov_cap, nb) z : ., mut = g_cov_cap loop: if z >= nc: break - store8(nb, z, 0) + cell_store_byte(nb, z, 0) z = z + 1 g_cov_bits = nb g_cov_cap = nc @@ -1296,7 +1296,7 @@ fn mc_add(vi: int): grow_cov_bits(vi / 8 + 1) if vi + 1 > g_cov_len: g_cov_len = vi + 1 - b := bu8(g_cov_bits, vi / 8) + b := cell_load8(g_cov_bits, vi / 8) pw : ., mut = 1 k : ., mut = vi % 8 loop: @@ -1305,7 +1305,7 @@ fn mc_add(vi: int): pw = pw * 2 k = k - 1 if (b / pw) % 2 == 0: - store8(g_cov_bits, vi / 8, b + pw) + cell_store_byte(g_cov_bits, vi / 8, b + pw) fn mc_has(vi: int) -> int: if vi < 0: return 0 @@ -1313,7 +1313,7 @@ fn mc_has(vi: int) -> int: return 0 if vi / 8 >= g_cov_cap: return 0 - b := bu8(g_cov_bits, vi / 8) + b := cell_load8(g_cov_bits, vi / 8) pw : ., mut = 1 k : ., mut = vi % 8 loop: @@ -1339,7 +1339,7 @@ fn mc_end(saved: int): if i >= g_cov_len: break if i / 8 < g_cov_cap: - store8(g_cov_bits, i / 8, 0) + cell_store_byte(g_cov_bits, i / 8, 0) i = i + 1 g_cov_len = saved @@ -1454,9 +1454,9 @@ fn sh_variant_payload_at(ea: int, vi: int, i: int, ga: int) -> int: // 泛型形参:泛型应用行的对应实参(缺位 -> 落空到名义,照 sh_struct_field_term) if ga >= 0 && get_type_kind(ga) == TYP_GENERIC_APPLY: gstart2 := get_type_extra(ga) - gcnt2 := r64(g_gen_apply_data, gstart2 * 8) + gcnt2 := cell_load(g_gen_apply_data, gstart2 * 8) if gi2 < gcnt2: - return sh_term_of_ti(r64(g_gen_apply_data, (gstart2 + 1 + gi2) * 8)) + return sh_term_of_ti(cell_load(g_gen_apply_data, (gstart2 + 1 + gi2) * 8)) break gi2 = gi2 + 1 pti := res_type_node(pn) diff --git a/src/compiler/type_engine.cr b/src/compiler/type_engine.cr index 9db8fa7f..b144ceed 100644 --- a/src/compiler/type_engine.cr +++ b/src/compiler/type_engine.cr @@ -54,7 +54,7 @@ fn grow_ty_lits(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * 8) - _dyncpy(g_ty_lits, g_ty_lits_cap * 8, nb) + cell_copy(g_ty_lits, g_ty_lits_cap * 8, nb) g_ty_lits = nb g_ty_lits_cap = nc @@ -64,11 +64,11 @@ fn ty_lits_reset(): fn ty_lits_push(t: int): if g_ty_lits_count + 1 > g_ty_lits_cap: grow_ty_lits(g_ty_lits_count + 1) - w64(g_ty_lits, g_ty_lits_count * 8, t) + cell_store(g_ty_lits, g_ty_lits_count * 8, t) g_ty_lits_count = g_ty_lits_count + 1 fn ty_lit_at(k: int) -> int: - return r64(g_ty_lits, k * 8) + return cell_load(g_ty_lits, k * 8) // Collect the literals of a product (an intersection sequence) or a single literal // into g_ty_lits. @@ -167,12 +167,12 @@ fn lits_contradictory_buf(buf: string, n: int) -> int: loop: if i >= n: break - p := r64(buf, i * 8) + p := cell_load(buf, i * 8) j : ., mut = i + 1 loop: if j >= n: break - q := r64(buf, j * 8) + q := cell_load(buf, j * 8) if lit_empty_pair(p, q) == 1: return 1 j = j + 1 @@ -191,7 +191,7 @@ fn lits_copy(p: int) -> string: loop: if i >= n: break - w64(buf, i * 8, ty_lit_at(i)) + cell_store(buf, i * 8, ty_lit_at(i)) i = i + 1 return buf @@ -586,14 +586,14 @@ fn grow_ty_memo(needed: int): loop: if i >= nc: break - w64(nb, i * 16, -1) + cell_store(nb, i * 16, -1) i = i + 1 nv := alloc(nc * 8) i2 : ., mut = 0 loop: if i2 >= nc: break - w64(nv, i2 * 8, 0) + cell_store(nv, i2 * 8, 0) i2 = i2 + 1 g_ty_memo_keys = nb g_ty_memo_vals = nv @@ -615,8 +615,8 @@ fn ty_memo_rehash(): loop: if i >= nc: break - w64(nb, i * 16, -1) - w64(nv, i * 8, 0) + cell_store(nb, i * 16, -1) + cell_store(nv, i * 8, 0) i = i + 1 g_ty_memo_keys = nb g_ty_memo_vals = nv @@ -626,13 +626,13 @@ fn ty_memo_rehash(): loop: if j >= old_cap: break - k := r64(old_keys, j * 16) + k := cell_load(old_keys, j * 16) if k >= 0: - b2 := r64(old_keys, j * 16 + 8) + b2 := cell_load(old_keys, j * 16 + 8) s := ty_memo_slot_no_grow(k, b2) - w64(g_ty_memo_keys, s * 16, k) - w64(g_ty_memo_keys, s * 16 + 8, b2) - w64(g_ty_memo_vals, s * 8, r64(old_vals, j * 8)) + cell_store(g_ty_memo_keys, s * 16, k) + cell_store(g_ty_memo_keys, s * 16 + 8, b2) + cell_store(g_ty_memo_vals, s * 8, cell_load(old_vals, j * 8)) g_ty_memo_count = g_ty_memo_count + 1 j = j + 1 @@ -642,10 +642,10 @@ fn ty_memo_slot_no_grow(a: int, b: int) -> int: h = 0 - h p : ., mut = h loop: - k := r64(g_ty_memo_keys, p * 16) + k := cell_load(g_ty_memo_keys, p * 16) if k < 0: return p - if k == a && r64(g_ty_memo_keys, p * 16 + 8) == b: + if k == a && cell_load(g_ty_memo_keys, p * 16 + 8) == b: return p p = p + 1 if p >= g_ty_memo_cap: @@ -665,17 +665,17 @@ fn ty_memo_slot(a: int, b: int) -> int: return ty_memo_slot_no_grow(a, b) fn ty_memo_state(slot: int) -> int: - if r64(g_ty_memo_keys, slot * 16) < 0: + if cell_load(g_ty_memo_keys, slot * 16) < 0: return -2 // Empty slot (unused). - return r64(g_ty_memo_vals, slot * 8) + return cell_load(g_ty_memo_vals, slot * 8) fn ty_memo_set(slot: int, a: int, b: int, v: int): - if r64(g_ty_memo_keys, slot * 16) < 0: - w64(g_ty_memo_keys, slot * 16, a) - w64(g_ty_memo_keys, slot * 16 + 8, b) + if cell_load(g_ty_memo_keys, slot * 16) < 0: + cell_store(g_ty_memo_keys, slot * 16, a) + cell_store(g_ty_memo_keys, slot * 16 + 8, b) g_ty_memo_count = g_ty_memo_count + 1 - w64(g_ty_memo_vals, slot * 8, v) + cell_store(g_ty_memo_vals, slot * 8, v) // Mu expansion by substitution. fn tt_subst_list(p: int, v: int, r: int) -> int: @@ -773,13 +773,13 @@ fn sub_cover(p: int, q: int) -> int: loop: if qi >= qn: break - lq := r64(bufq, qi * 8) + lq := cell_load(bufq, qi * 8) covered : ., mut = 0 pi : ., mut = 0 loop: if pi >= pn: break - lp := r64(bufp, pi * 8) + lp := cell_load(bufp, pi * 8) li := lit_implies(lp, lq) if li == 1: covered = 1 @@ -813,8 +813,8 @@ fn ty_sub_core(a: int, b: int) -> int: if r == -1: // Do not cache -1 from budget exhaustion; a later call with a larger budget // would read a false -1. Clear the slot. - w64(g_ty_memo_keys, slot * 16, -1) - w64(g_ty_memo_vals, slot * 8, 0) + cell_store(g_ty_memo_keys, slot * 16, -1) + cell_store(g_ty_memo_vals, slot * 8, 0) else: ty_memo_set(slot, a, b, r) return r diff --git a/src/compiler/type_selftest.cr b/src/compiler/type_selftest.cr index 5ae818da..176b7b75 100644 --- a/src/compiler/type_selftest.cr +++ b/src/compiler/type_selftest.cr @@ -49,7 +49,7 @@ fn ts_diag_code_at(idx: int) -> int: return -1 if idx >= g_diag_count: return -1 - return r64(g_diags, idx * DIAG_REC_SIZE) + return cell_load(g_diags, idx * DIAG_REC_SIZE) // Read the message pointer of diagnostic idx. check_error stores that pointer. fn ts_diag_msg_at(idx: int) -> string: @@ -57,7 +57,7 @@ fn ts_diag_msg_at(idx: int) -> string: return "" if idx >= g_diag_count: return "" - return load_str_ptr(g_diags, idx * DIAG_REC_SIZE + 8) + return cell_load_str(g_diags, idx * DIAG_REC_SIZE + 8) // Frozen expected AK mapping, compared cell by cell with iface_by_ty_code. // This table is expected data, not a second production implementation. @@ -116,7 +116,7 @@ fn ts_unf_mk_int_struct(name: string) -> int: si_set_field_name(sa, 0, str_intern("a")) si_set_field_type(sa, 0, TY_INT) si_set_field_type_node(sa, 0, alloc_node(0, 0, 0, 0, 0, TY_INT, 0, 0, 0)) - w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) + cell_store(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) return alloc_named_type(str_intern(name)) // Count union leaves in a domain term. This walks the union tree by term shape @@ -135,8 +135,8 @@ fn ts_gen_map_ti(name_ni: int) -> int: loop: if i >= g_gen_map_count: return -1 - if r64(g_gen_map_names, i * 8) == name_ni: - return r64(g_gen_map_types, i * 8) + if cell_load(g_gen_map_names, i * 8) == name_ni: + return cell_load(g_gen_map_types, i * 8) i = i + 1 return -1 @@ -144,19 +144,19 @@ fn ts_gen_map_ti(name_ni: int) -> int: // 函数 save_struct_gen_constrs 写入侧表,测试真实路径,不手写侧表。 fn ts_t5_mk_constr_struct(name: string, cname: string) -> int: sa := add_struct(name) - w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1) - w64(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, str_intern("T5_CS_T")) + cell_store(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1) + cell_store(g_structs, sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, str_intern("T5_CS_T")) cb := alloc(8) - w64(cb, 0, str_intern(cname)) + cell_store(cb, 0, str_intern(cname)) save_struct_gen_constrs(sa, cb, 1) return sa fn ts_t5_mk_constr_enum(name: string, cname: string) -> int: ea := add_enum(name) - w64(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) - w64(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T5_CE_T")) + cell_store(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) + cell_store(g_enums, ea * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T5_CE_T")) cb := alloc(8) - w64(cb, 0, str_intern(cname)) + cell_store(cb, 0, str_intern(cname)) save_enum_gen_constrs(ea, cb, 1) return ea @@ -169,10 +169,10 @@ fn ts_t5_mk_iface(name: string) -> int: loop: if zi >= ESZ_IFACEINFO: break - w8(g_ifaces, base + zi, 0) + cell_store8(g_ifaces, base + zi, 0) zi = zi + 1 - w64(g_ifaces, base + OFF_IF_NAME, str_intern(name)) - w64(g_ifaces, base + OFF_IF_GENERIC_COUNT, 0) + cell_store(g_ifaces, base + OFF_IF_NAME, str_intern(name)) + cell_store(g_ifaces, base + OFF_IF_GENERIC_COUNT, 0) g_iface_count = g_iface_count + 1 return g_iface_count - 1 @@ -231,8 +231,8 @@ fn ts_t5_run() -> int: fails = fails + ts_check_b("t5.f4_later_param_declared_type", (t5_ti_bound == TI_STR && ts_gen_map_ti(t5_u_ni) == -1), true) fails = fails + ts_check_b("t5.f4_bind_seg_recorded", - (g_gen_binds_count == 2 && r64(g_gen_binds, 0 * 8) == 1 && - r64(g_gen_binds, 1 * 8) == TI_STR && ast_int_val(t5_calln) == 1), true) + (g_gen_binds_count == 2 && cell_load(g_gen_binds, 0 * 8) == 1 && + cell_load(g_gen_binds, 1 * 8) == TI_STR && ast_int_val(t5_calln) == 1), true) // 2. 约束名解析和本质轴判定结果。 t5_ni_int := str_intern("int") t5_ni_str := str_intern("string") @@ -271,7 +271,7 @@ fn ts_t5_run() -> int: ei_generic_count(t5_ei) == 1 && ei_gen_constr(t5_ei, 0) == t5_ni_int && ei_gen_constr(t5_ei, 4) == -1), true) t5_args := alloc(8) - w64(t5_args, 0 * 8, TI_STR) + cell_store(t5_args, 0 * 8, TI_STR) t5_gnode := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 41, 3) t5_d0 := g_diag_count gen_inst_constr_check(t5_gnode, t5_si, -1, t5_args, 1) @@ -281,12 +281,12 @@ fn ts_t5_run() -> int: t5_gnode2 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 42, 3) gen_inst_constr_check(t5_gnode2, t5_si, -1, t5_args, 1) // 新节点 -> 再报(去重不跨节点) t5_d3 := g_diag_count - w64(t5_args, 0 * 8, TI_INT) + cell_store(t5_args, 0 * 8, TI_INT) t5_gnode3 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5BoxInt"), -1, 1, 0, 0, 0, 43, 3) gen_inst_constr_check(t5_gnode3, t5_si, -1, t5_args, 1) // 正控:满足 -> 零诊断 gen_inst_constr_check(t5_gnode3, t5_ei, -1, t5_args, 1) // 枚举侧同(满足) t5_d4 := g_diag_count - w64(t5_args, 0 * 8, TI_STR) + cell_store(t5_args, 0 * 8, TI_STR) t5_gnode4 := alloc_node(EXPR_GENERIC_APPLY, str_intern("T5EBoxInt"), -1, 1, 0, 0, 0, 44, 3) gen_inst_constr_check(t5_gnode4, -1, t5_ei, t5_args, 1) // 枚举侧违反 -> 诊断 t5_d5 := g_diag_count @@ -330,8 +330,8 @@ fn ts_t5_run() -> int: // 绑定段按 checker 的写入形态人工构造:块 = [count, ti...],节点 int_val = 起始 + 1。 grow_gen_binds(g_gen_binds_count + 2) t5_bstart := g_gen_binds_count - w64(g_gen_binds, t5_bstart * 8, 1) - w64(g_gen_binds, (t5_bstart + 1) * 8, t5_slice_ti) + cell_store(g_gen_binds, t5_bstart * 8, 1) + cell_store(g_gen_binds, (t5_bstart + 1) * 8, t5_slice_ti) g_gen_binds_count = t5_bstart + 2 t5_key_s := inst_key_of_ti(t5_slice_ti) t5_f1 := gen_find_or_create_bind(t5_m_fi, t5_key_s, t5_bstart) @@ -360,23 +360,23 @@ fn ts_t5_run() -> int: // 追加接口方法,写入位置与 parser 的 interface 分支相同:mbase + name、param_count、ret_ti。 fn ts_isat_add_method(ii: int, mname: string, pc: int, rt: int) -> int: - n := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + n := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + n * ESZ_IFMETHOD - w64(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)) - w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) - w64(g_ifaces, mbase + OFF_IFM_RET_TI, rt) + cell_store(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) + cell_store(g_ifaces, mbase + OFF_IFM_RET_TI, rt) // 返回类型节点槽与 parser 相同:基类型节点是 kind 0,type_val = TY_*。 // 接收者模式 0 表示无接收者,本组夹具使用这种形态。 - w64(g_ifaces, mbase + OFF_IFM_RET_NODE, ts_ifc_base_node(rt)) - w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, 0) + cell_store(g_ifaces, mbase + OFF_IFM_RET_NODE, ts_ifc_base_node(rt)) + cell_store(g_ifaces, mbase + OFF_IFM_SELF_MODE, 0) pj : ., mut = 0 loop: if pj >= pc || pj >= MAX_IFACE_METHOD_PARAMS: break - w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT) - w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, ts_ifc_base_node(TY_INT)) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, ts_ifc_base_node(TY_INT)) pj = pj + 1 - w64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1) + cell_store(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1) return mbase // 基类型节点,与 parser 的 parse_type 产物同形:kind 0,type_val = TY_*。 @@ -407,9 +407,9 @@ fn ts_isat_add_impl_method(tname_ni: int, mname: string, pc: int, rt: int) -> in fn_node := alloc_node(EXPR_FN, mangled_ni, pf, pc, rt, rtn, -1, 0, 0) fi := add_func(mn, pc, rt, fn_node) grow_methods(g_method_count + 1) - w64(g_methods, g_method_count * 24, tname_ni) - w64(g_methods, g_method_count * 24 + 8, str_intern(mname)) - w64(g_methods, g_method_count * 24 + 16, mangled_ni) + cell_store(g_methods, g_method_count * 24, tname_ni) + cell_store(g_methods, g_method_count * 24 + 8, str_intern(mname)) + cell_store(g_methods, g_method_count * 24 + 16, mangled_ni) g_method_count = g_method_count + 1 return fi @@ -509,8 +509,8 @@ fn ts_isat_run() -> int: // 13. 泛型应用记录:查方法使用基名,走 decl_name_of_ti 的 TYP_GENERIC_APPLY 分支。 grow_gen_apply_data(g_gen_apply_data_count + 2) isat_ga_s := g_gen_apply_data_count - w64(g_gen_apply_data, isat_ga_s * 8, 1) - w64(g_gen_apply_data, (isat_ga_s + 1) * 8, TI_INT) + cell_store(g_gen_apply_data, isat_ga_s * 8, 1) + cell_store(g_gen_apply_data, (isat_ga_s + 1) * 8, TI_INT) g_gen_apply_data_count = isat_ga_s + 2 isat_ga_ti := alloc_type(TYP_GENERIC_APPLY, isat_s_ti, isat_ga_s) fails = fails + ts_check("isat.generic_apply_base_name", @@ -566,7 +566,7 @@ fn ts_x2_run() -> int: x2_rw_view := alloc_type(TYP_REF, x2_slice, 1) // &mut [int](可写视图) grow_gen_apply_data(g_gen_apply_data_count + 1) // 元组行:字段列(product 类型表记录) x2_tup_start := g_gen_apply_data_count - w64(g_gen_apply_data, x2_tup_start * 8, TI_INT) + cell_store(g_gen_apply_data, x2_tup_start * 8, TI_INT) g_gen_apply_data_count = x2_tup_start + 1 x2_tup := alloc_type(TYP_TUPLE, 1, x2_tup_start) x2_named := alloc_named_type(str_intern("X2ShapeN")) @@ -808,13 +808,13 @@ fn ts_ifc_apply_node(base_name: string, arg_node: int) -> int: // 接口方法条目(节点数据可控:pnodes = 参数节点缓冲(可 -1 = 全无节点);rnode = 返回型节点; // smode = 接收者模式(0 = 无接收者,1/2/3 = self/&self/&mut self)) fn ts_ifc_add_method_n(ii: int, mname: string, pc: int, smode: int, n0: int, n1: int, n2: int, n3: int, rnode: int) -> int: - n := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + n := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) mbase := ii * ESZ_IFACEINFO + OFF_IF_METHODS + n * ESZ_IFMETHOD - w64(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)) - w64(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) - w64(g_ifaces, mbase + OFF_IFM_RET_TI, 0) - w64(g_ifaces, mbase + OFF_IFM_RET_NODE, rnode) - w64(g_ifaces, mbase + OFF_IFM_SELF_MODE, smode) + cell_store(g_ifaces, mbase + OFF_IFM_NAME, str_intern(mname)) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_COUNT, pc) + cell_store(g_ifaces, mbase + OFF_IFM_RET_TI, 0) + cell_store(g_ifaces, mbase + OFF_IFM_RET_NODE, rnode) + cell_store(g_ifaces, mbase + OFF_IFM_SELF_MODE, smode) pj : ., mut = 0 loop: if pj >= pc || pj >= MAX_IFACE_METHOD_PARAMS: @@ -828,10 +828,10 @@ fn ts_ifc_add_method_n(ii: int, mname: string, pc: int, smode: int, n0: int, n1: pn = n2 if pj == 3: pn = n3 - w64(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT) - w64(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, pn) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_TYPES + pj * 8, TY_INT) + cell_store(g_ifaces, mbase + OFF_IFM_PARAM_NODES + pj * 8, pn) pj = pj + 1 - w64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1) + cell_store(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT, n + 1) return mbase // impl 侧函数行(节点数据可控;mode0 = 首参接收者模式(≠0 时该参无节点,接收者槽约定))。 @@ -860,9 +860,9 @@ fn ts_ifc_add_impl(tname_ni: int, mname: string, pc: int, mode0: int, n0: int, n fn_node := alloc_node(EXPR_FN, mangled_ni, pf, pc, 0, rnode, -1, 0, 0) fi := add_func(mn, pc, 0, fn_node) grow_methods(g_method_count + 1) - w64(g_methods, g_method_count * 24, tname_ni) - w64(g_methods, g_method_count * 24 + 8, str_intern(mname)) - w64(g_methods, g_method_count * 24 + 16, mangled_ni) + cell_store(g_methods, g_method_count * 24, tname_ni) + cell_store(g_methods, g_method_count * 24 + 8, str_intern(mname)) + cell_store(g_methods, g_method_count * 24 + 16, mangled_ni) g_method_count = g_method_count + 1 return fi @@ -1091,15 +1091,15 @@ fn ts_t3n_run() -> int: // 2 泛型应用:规范形 b = 基型记录 + 序列 [基名令牌, 实参项…] grow_gen_apply_data(g_gen_apply_data_count + 2) gs1 := g_gen_apply_data_count - w64(g_gen_apply_data, gs1 * 8, 1) - w64(g_gen_apply_data, (gs1 + 1) * 8, TI_INT) + cell_store(g_gen_apply_data, gs1 * 8, 1) + cell_store(g_gen_apply_data, (gs1 + 1) * 8, TI_INT) g_gen_apply_data_count = gs1 + 2 ga1 := alloc_type(TYP_GENERIC_APPLY, n_a, gs1) ga2 := alloc_type(TYP_GENERIC_APPLY, n_a, gs1) // 另一行,同基型同实参 grow_gen_apply_data(g_gen_apply_data_count + 2) gs2 := g_gen_apply_data_count - w64(g_gen_apply_data, gs2 * 8, 1) - w64(g_gen_apply_data, (gs2 + 1) * 8, TI_STR) + cell_store(g_gen_apply_data, gs2 * 8, 1) + cell_store(g_gen_apply_data, (gs2 + 1) * 8, TI_STR) g_gen_apply_data_count = gs2 + 2 ga3 := alloc_type(TYP_GENERIC_APPLY, n_a, gs2) // 同基型异实参 t_ga1 := sh_term_of_ti(ga1) @@ -1124,14 +1124,14 @@ fn ts_t3n_run() -> int: // 2 嵌套应用:Box[Box[int]] 两处(外层实参分别指向同实参的不同行)-> 同节点 grow_gen_apply_data(g_gen_apply_data_count + 2) gs3 := g_gen_apply_data_count - w64(g_gen_apply_data, gs3 * 8, 1) - w64(g_gen_apply_data, (gs3 + 1) * 8, ga1) + cell_store(g_gen_apply_data, gs3 * 8, 1) + cell_store(g_gen_apply_data, (gs3 + 1) * 8, ga1) g_gen_apply_data_count = gs3 + 2 ga4 := alloc_type(TYP_GENERIC_APPLY, n_a, gs3) grow_gen_apply_data(g_gen_apply_data_count + 2) gs4 := g_gen_apply_data_count - w64(g_gen_apply_data, gs4 * 8, 1) - w64(g_gen_apply_data, (gs4 + 1) * 8, ga2) + cell_store(g_gen_apply_data, gs4 * 8, 1) + cell_store(g_gen_apply_data, (gs4 + 1) * 8, ga2) g_gen_apply_data_count = gs4 + 2 ga5 := alloc_type(TYP_GENERIC_APPLY, n_a, gs4) fails = fails + ts_check_b("t3n.nested_apply_same", @@ -1343,11 +1343,11 @@ fn ts_ccr_roundtrip_ok() -> int: loop: if i >= rc: break - if buf_read_i64(g_ccr_type_seg, pos) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND): + if buf_read_i64(g_ccr_type_seg, pos) != cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_KIND): return 0 - if buf_read_i64(g_ccr_type_seg, pos + 8) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA): + if buf_read_i64(g_ccr_type_seg, pos + 8) != cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_DATA): return 0 - if buf_read_i64(g_ccr_type_seg, pos + 16) != r64(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA): + if buf_read_i64(g_ccr_type_seg, pos + 16) != cell_load(g_types, i * ESZ_TYPE_ROW + OFF_TR_EXTRA): return 0 pos = pos + ESZ_TYPE_ROW i = i + 1 @@ -1399,11 +1399,11 @@ fn ts_ccr_dedup_ok(opt_a: int, opt_b: int, arr3: int, arr4: int) -> int: fn ts_ccr_purity_ok() -> int: n0 := g_ccr_type_seg_len cp := alloc(n0 + 8) - _dyncpy(g_ccr_type_seg, n0, cp) + cell_copy(g_ccr_type_seg, n0, cp) // IFACE 段也纳入。类型表和接口表的装填都是纯函数。 i0 := g_ccr_iface_seg_len ip := alloc(i0 + 8) - _dyncpy(g_ccr_iface_seg, i0, ip) + cell_copy(g_ccr_iface_seg, i0, ip) // 判定历史扰动:跨项判定 + 规范化(向项表追加新项,旧直接序列化活表必在此变) pa := tt_atom(AK_INT, TI_INT, -1) pb := tt_atom(AK_STRING, TI_STR, -1) @@ -1436,7 +1436,7 @@ fn ts_ccr_purity_ok() -> int: loop: if k >= n0: break - if load8(g_ccr_type_seg, k) != load8(cp, k): + if cell_load_byte(g_ccr_type_seg, k) != cell_load_byte(cp, k): print("ct.seg_determinism: buffer byte mismatch at ") println(int_str(k)) return 0 @@ -1452,7 +1452,7 @@ fn ts_ccr_purity_ok() -> int: loop: if k2 >= i0: break - if load8(g_ccr_iface_seg, k2) != load8(ip, k2): + if cell_load_byte(g_ccr_iface_seg, k2) != cell_load_byte(ip, k2): print("ct.seg_determinism: iface buffer byte mismatch at ") println(int_str(k2)) return 0 @@ -1507,15 +1507,15 @@ fn ts_ccr_run() -> int: // 复位三部分缓存。 fn ts_ccr2_row_ok(b: string, p: int, i: int) -> int: eo := i * ESZ_IFACE_ENTRY - if buf_read_i32(b, p) != r64(g_iface_entries, eo + OFF_IE_AK): + if buf_read_i32(b, p) != cell_load(g_iface_entries, eo + OFF_IE_AK): return 0 - if buf_read_i32(b, p + 4) != r64(g_iface_entries, eo + OFF_IE_TI): + if buf_read_i32(b, p + 4) != cell_load(g_iface_entries, eo + OFF_IE_TI): return 0 - if buf_read_i32(b, p + 8) != r64(g_iface_entries, eo + OFF_IE_NAME): + if buf_read_i32(b, p + 8) != cell_load(g_iface_entries, eo + OFF_IE_NAME): return 0 - if buf_read_i32(b, p + 12) != r64(g_iface_entries, eo + OFF_IE_LIT): + if buf_read_i32(b, p + 12) != cell_load(g_iface_entries, eo + OFF_IE_LIT): return 0 - if buf_read_i64(b, p + 16) != r64(g_iface_entries, eo + OFF_IE_OPS): + if buf_read_i64(b, p + 16) != cell_load(g_iface_entries, eo + OFF_IE_OPS): return 0 return 1 @@ -1546,9 +1546,9 @@ fn ts_ccr2_seg_walk_ok() -> int: loop: if si >= g_iface_shape_count: break - if buf_read_i32(b, p) != r64(g_iface_shape_names, si * 8): + if buf_read_i32(b, p) != cell_load(g_iface_shape_names, si * 8): return 0 - if buf_read_i32(b, p + 4) != r64(g_iface_shape_terms, si * 8): + if buf_read_i32(b, p + 4) != cell_load(g_iface_shape_terms, si * 8): return 0 p = p + 8 si = si + 1 @@ -1561,12 +1561,12 @@ fn ts_ccr2_seg_walk_ok() -> int: if fi >= g_iface_count: break fo := fi * ESZ_IFACEINFO - mc := r64(g_ifaces, fo + OFF_IF_METHOD_COUNT) - if buf_read_i32(b, p) != r64(g_ifaces, fo + OFF_IF_NAME): + mc := cell_load(g_ifaces, fo + OFF_IF_METHOD_COUNT) + if buf_read_i32(b, p) != cell_load(g_ifaces, fo + OFF_IF_NAME): return 0 if buf_read_i32(b, p + 4) != mc: return 0 - if buf_read_i32(b, p + 8) != r64(g_ifaces, fo + OFF_IF_GENERIC_COUNT): + if buf_read_i32(b, p + 8) != cell_load(g_ifaces, fo + OFF_IF_GENERIC_COUNT): return 0 p = p + 16 mj : ., mut = 0 @@ -1574,20 +1574,20 @@ fn ts_ccr2_seg_walk_ok() -> int: if mj >= mc: break mo := fo + OFF_IF_METHODS + mj * ESZ_IFMETHOD - if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_NAME): + if buf_read_i32(b, p) != cell_load(g_ifaces, mo + OFF_IFM_NAME): return 0 - if buf_read_i32(b, p + 4) != r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT): + if buf_read_i32(b, p + 4) != cell_load(g_ifaces, mo + OFF_IFM_PARAM_COUNT): return 0 - if buf_read_i32(b, p + 8) != r64(g_ifaces, mo + OFF_IFM_SELF_MODE): + if buf_read_i32(b, p + 8) != cell_load(g_ifaces, mo + OFF_IFM_SELF_MODE): return 0 - if buf_read_i32(b, p + 12) != r64(g_ifaces, mo + OFF_IFM_RET_TERM): + if buf_read_i32(b, p + 12) != cell_load(g_ifaces, mo + OFF_IFM_RET_TERM): return 0 p = p + 16 kq : ., mut = 0 loop: if kq >= MAX_IFACE_METHOD_PARAMS: break - if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + kq * 8): + if buf_read_i32(b, p) != cell_load(g_ifaces, mo + OFF_IFM_PARAM_TERMS + kq * 8): return 0 p = p + 4 kq = kq + 1 @@ -1595,7 +1595,7 @@ fn ts_ccr2_seg_walk_ok() -> int: loop: if qq >= MAX_IFACE_METHOD_PARAMS: break - if buf_read_i32(b, p) != r64(g_ifaces, mo + OFF_IFM_PARAM_TYPES + qq * 8): + if buf_read_i32(b, p) != cell_load(g_ifaces, mo + OFF_IFM_PARAM_TYPES + qq * 8): return 0 p = p + 4 qq = qq + 1 @@ -1609,9 +1609,9 @@ fn ts_ccr2_seg_walk_ok() -> int: loop: if gi >= g_impl_for_count: break - if buf_read_i32(b, p) != r64(g_impl_for, gi * 16): + if buf_read_i32(b, p) != cell_load(g_impl_for, gi * 16): return 0 - if buf_read_i32(b, p + 4) != r64(g_impl_for, gi * 16 + 8): + if buf_read_i32(b, p + 4) != cell_load(g_impl_for, gi * 16 + 8): return 0 p = p + 8 gi = gi + 1 @@ -1623,11 +1623,11 @@ fn ts_ccr2_seg_walk_ok() -> int: loop: if mi >= g_method_count: break - if buf_read_i32(b, p) != r64(g_methods, mi * 24): + if buf_read_i32(b, p) != cell_load(g_methods, mi * 24): return 0 - if buf_read_i32(b, p + 4) != r64(g_methods, mi * 24 + 8): + if buf_read_i32(b, p + 4) != cell_load(g_methods, mi * 24 + 8): return 0 - if buf_read_i32(b, p + 8) != r64(g_methods, mi * 24 + 16): + if buf_read_i32(b, p + 8) != cell_load(g_methods, mi * 24 + 16): return 0 p = p + 12 mi = mi + 1 @@ -1641,25 +1641,25 @@ fn ts_ccr2_sig_slots_ok() -> int: loop: if ii >= g_iface_count: break - mc := r64(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) + mc := cell_load(g_ifaces, ii * ESZ_IFACEINFO + OFF_IF_METHOD_COUNT) mi : ., mut = 0 loop: if mi >= mc: break mo := ii * ESZ_IFACEINFO + OFF_IF_METHODS + mi * ESZ_IFMETHOD // 返回槽 == 活算(签名的唯一来源 = sh_iface_sig_ret_term) - if r64(g_ifaces, mo + OFF_IFM_RET_TERM) != sh_iface_sig_ret_term(ii, mi): + if cell_load(g_ifaces, mo + OFF_IFM_RET_TERM) != sh_iface_sig_ret_term(ii, mi): return 0 - pc := r64(g_ifaces, mo + OFF_IFM_PARAM_COUNT) + pc := cell_load(g_ifaces, mo + OFF_IFM_PARAM_COUNT) pj : ., mut = 0 loop: if pj >= MAX_IFACE_METHOD_PARAMS: break if pj < pc: - if r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != sh_iface_sig_param_term(ii, mi, pj): + if cell_load(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != sh_iface_sig_param_term(ii, mi, pj): return 0 else: - if r64(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != -1: + if cell_load(g_ifaces, mo + OFF_IFM_PARAM_TERMS + pj * 8) != -1: return 0 // 未用槽 = -1 pj = pj + 1 @@ -1788,13 +1788,13 @@ fn ts_ccr2_run() -> int: ts_ifc_add_method_n(ct2_ii, "a", 1, 1, -1, -1, -1, -1, ct2_int_n) // fn a(self) -> int ts_ifc_add_method_n(ct2_ii, "b", 1, 0, ct2_n, -1, -1, -1, ct2_int_n) // fn b(x: Ct2Named) -> int grow_impl_for(1) - w64(g_impl_for, 0, str_intern("Ct2Iface")) - w64(g_impl_for, 8, str_intern("Ct2Named")) + cell_store(g_impl_for, 0, str_intern("Ct2Iface")) + cell_store(g_impl_for, 8, str_intern("Ct2Named")) g_impl_for_count = 1 grow_methods(1) - w64(g_methods, 0, str_intern("Ct2Named")) - w64(g_methods, 8, str_intern("b")) - w64(g_methods, 16, str_intern("Ct2Named.b")) + cell_store(g_methods, 0, str_intern("Ct2Named")) + cell_store(g_methods, 8, str_intern("b")) + cell_store(g_methods, 16, str_intern("Ct2Named.b")) g_method_count = 1 prep := ccr_seg_prepare_save() fails = fails + ts_check("ct2.seg_prepare", prep, 0) @@ -2433,8 +2433,8 @@ fn type_selftest_run() -> int: // g_gen_apply_data 的起始下标,每项 8 字节。 grow_gen_apply_data(g_gen_apply_data_count + 2) tup_start := g_gen_apply_data_count - w64(g_gen_apply_data, tup_start * 8, TI_INT) - w64(g_gen_apply_data, (tup_start + 1) * 8, TI_STR) + cell_store(g_gen_apply_data, tup_start * 8, TI_INT) + cell_store(g_gen_apply_data, (tup_start + 1) * 8, TI_STR) g_gen_apply_data_count = g_gen_apply_data_count + 2 t_tup := sh_term_of_ti(alloc_type(TYP_TUPLE, 2, tup_start)) total = total + 1 @@ -2541,11 +2541,11 @@ fn type_selftest_run() -> int: (array_len_constraint_ok(f2_outer3, f2_outer3) == 1 && array_len_constraint_ok(f2_outer4, f2_outer3) == 0), true) grow_gen_apply_data(g_gen_apply_data_count + 4) f2_ga_s1 := g_gen_apply_data_count - w64(g_gen_apply_data, f2_ga_s1 * 8, 1) - w64(g_gen_apply_data, (f2_ga_s1 + 1) * 8, f2_arr3) + cell_store(g_gen_apply_data, f2_ga_s1 * 8, 1) + cell_store(g_gen_apply_data, (f2_ga_s1 + 1) * 8, f2_arr3) f2_ga_s2 := f2_ga_s1 + 2 - w64(g_gen_apply_data, f2_ga_s2 * 8, 1) - w64(g_gen_apply_data, (f2_ga_s2 + 1) * 8, f2_arr4) + cell_store(g_gen_apply_data, f2_ga_s2 * 8, 1) + cell_store(g_gen_apply_data, (f2_ga_s2 + 1) * 8, f2_arr4) g_gen_apply_data_count = f2_ga_s2 + 2 // 基类型记录裸分配:人造记录不登记到名字表,避免假键破坏 name->ti 映射。 f2_base := alloc_type(TYP_NAMED, 1202, 0) @@ -2561,11 +2561,11 @@ fn type_selftest_run() -> int: (array_len_constraint_ok(f2_pt3, f2_pt3) == 1 && array_len_constraint_ok(f2_pt4, f2_pt3) == 0), true) grow_gen_apply_data(g_gen_apply_data_count + 4) f2_tp_s1 := g_gen_apply_data_count - w64(g_gen_apply_data, f2_tp_s1 * 8, TI_INT) - w64(g_gen_apply_data, (f2_tp_s1 + 1) * 8, f2_arr3) + cell_store(g_gen_apply_data, f2_tp_s1 * 8, TI_INT) + cell_store(g_gen_apply_data, (f2_tp_s1 + 1) * 8, f2_arr3) f2_tp_s2 := f2_tp_s1 + 2 - w64(g_gen_apply_data, f2_tp_s2 * 8, TI_INT) - w64(g_gen_apply_data, (f2_tp_s2 + 1) * 8, f2_arr4) + cell_store(g_gen_apply_data, f2_tp_s2 * 8, TI_INT) + cell_store(g_gen_apply_data, (f2_tp_s2 + 1) * 8, f2_arr4) g_gen_apply_data_count = f2_tp_s2 + 2 f2_tup3 := alloc_type(TYP_TUPLE, 2, f2_tp_s1) f2_tup4 := alloc_type(TYP_TUPLE, 2, f2_tp_s2) @@ -2815,22 +2815,22 @@ fn type_selftest_run() -> int: // 的情况,所以下面另加显式断言。 // 1. TY 码表:9 个 TY_* 码和 1 个未映射码。 t2_codes := alloc(10 * 8) - w64(t2_codes, 0, TY_INT) - w64(t2_codes, 8, TY_DEX) - w64(t2_codes, 16, TY_BOOL) - w64(t2_codes, 24, TY_STRING) - w64(t2_codes, 32, TY_UNIT) - w64(t2_codes, 40, TY_NEVER) - w64(t2_codes, 48, TY_CHAR) - w64(t2_codes, 56, TY_GENERIC_PARAM) - w64(t2_codes, 64, TY_DEX_S) - w64(t2_codes, 72, 999) + cell_store(t2_codes, 0, TY_INT) + cell_store(t2_codes, 8, TY_DEX) + cell_store(t2_codes, 16, TY_BOOL) + cell_store(t2_codes, 24, TY_STRING) + cell_store(t2_codes, 32, TY_UNIT) + cell_store(t2_codes, 40, TY_NEVER) + cell_store(t2_codes, 48, TY_CHAR) + cell_store(t2_codes, 56, TY_GENERIC_PARAM) + cell_store(t2_codes, 64, TY_DEX_S) + cell_store(t2_codes, 72, 999) t2_ty_bad : ., mut = 0 t2_i : ., mut = 0 loop: if t2_i >= 10: break - t2_code := r64(t2_codes, t2_i * 8) + t2_code := cell_load(t2_codes, t2_i * 8) if iface_by_ty_code(t2_code) != ts_t4_ak_expected(t2_code): t2_ty_bad = t2_ty_bad + 1 t2_i = t2_i + 1 @@ -2851,7 +2851,7 @@ fn type_selftest_run() -> int: // 比较必须能区分两个已知不同的格子。 total = total + 1 fails = fails + ts_check_b("iface.scan_coverage", - (t2_i == 10 && r64(t2_codes, 56) == TY_GENERIC_PARAM && r64(t2_codes, 64) == TY_DEX_S && + (t2_i == 10 && cell_load(t2_codes, 56) == TY_GENERIC_PARAM && cell_load(t2_codes, 64) == TY_DEX_S && t2_j == g_type_count && t2_j >= 9), true) total = total + 1 fails = fails + ts_check_b("iface.compare_is_live", @@ -2928,25 +2928,25 @@ fn type_selftest_run() -> int: // while 1 仍报 TC04。 // 另设许可为 1 的格数哨兵,全 0 表会失败;再加条目数据一致性和端到端段。 o4_aks := alloc(13 * 8) // 13 类枚举(顺序 = 条目表声明序) - w64(o4_aks, 0, AK_INT) - w64(o4_aks, 8, AK_DEX) - w64(o4_aks, 16, AK_STRING) - w64(o4_aks, 24, AK_BOOL) - w64(o4_aks, 32, AK_UNIT) - w64(o4_aks, 40, AK_NEVER) - w64(o4_aks, 48, AK_CHAR) - w64(o4_aks, 56, AK_DYN) - w64(o4_aks, 64, AK_PRODUCT) - w64(o4_aks, 72, AK_SEQUENCE) - w64(o4_aks, 80, AK_REF) - w64(o4_aks, 88, AK_PTR) - w64(o4_aks, 96, AK_NAMED) + cell_store(o4_aks, 0, AK_INT) + cell_store(o4_aks, 8, AK_DEX) + cell_store(o4_aks, 16, AK_STRING) + cell_store(o4_aks, 24, AK_BOOL) + cell_store(o4_aks, 32, AK_UNIT) + cell_store(o4_aks, 40, AK_NEVER) + cell_store(o4_aks, 48, AK_CHAR) + cell_store(o4_aks, 56, AK_DYN) + cell_store(o4_aks, 64, AK_PRODUCT) + cell_store(o4_aks, 72, AK_SEQUENCE) + cell_store(o4_aks, 80, AK_REF) + cell_store(o4_aks, 88, AK_PTR) + cell_store(o4_aks, 96, AK_NAMED) o4_ops := alloc(5 * 8) // 算术族 5 op(检查消费点:OP_ADD..OP_MOD) - w64(o4_ops, 0, OP_ADD) - w64(o4_ops, 8, OP_SUB) - w64(o4_ops, 16, OP_MUL) - w64(o4_ops, 24, OP_DIV) - w64(o4_ops, 32, OP_MOD) + cell_store(o4_ops, 0, OP_ADD) + cell_store(o4_ops, 8, OP_SUB) + cell_store(o4_ops, 16, OP_MUL) + cell_store(o4_ops, 24, OP_DIV) + cell_store(o4_ops, 32, OP_MOD) // 1 算术检查:逐 ak × 逐 op 对拍许可 ⟺ int|dex o4_arith_bad : ., mut = 0 o4_arith_yes : ., mut = 0 @@ -2954,12 +2954,12 @@ fn type_selftest_run() -> int: loop: if o4_i >= 13: break - o4_ak := r64(o4_aks, o4_i * 8) + o4_ak := cell_load(o4_aks, o4_i * 8) o4_j : ., mut = 0 loop: if o4_j >= 5: break - o4_op := r64(o4_ops, o4_j * 8) + o4_op := cell_load(o4_ops, o4_j * 8) o4_want : ., mut = 0 if o4_ak == AK_INT || o4_ak == AK_DEX: o4_want = 1 @@ -2980,7 +2980,7 @@ fn type_selftest_run() -> int: loop: if o4_k >= 13: break - o4_ak2 := r64(o4_aks, o4_k * 8) + o4_ak2 := cell_load(o4_aks, o4_k * 8) o4_want2 : ., mut = 0 if o4_ak2 == AK_INT || o4_ak2 == AK_BOOL: o4_want2 = 1 @@ -3003,7 +3003,7 @@ fn type_selftest_run() -> int: loop: if o4_m >= 13: break - o4_ak3 := r64(o4_aks, o4_m * 8) + o4_ak3 := cell_load(o4_aks, o4_m * 8) o4_want3 : ., mut = 0 if o4_ak3 == AK_INT || o4_ak3 == AK_BOOL: o4_want3 = 1 @@ -3031,7 +3031,7 @@ fn type_selftest_run() -> int: loop: if o4_n >= 13: break - o4_ak4 := r64(o4_aks, o4_n * 8) + o4_ak4 := cell_load(o4_aks, o4_n * 8) o4_n2 : ., mut = 0 loop: if o4_n2 >= 6: @@ -3084,8 +3084,8 @@ fn type_selftest_run() -> int: loop: if o4_r >= g_iface_entry_count: break - o4_akr := r64(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_AK) - if iface_ops(o4_akr) != r64(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_OPS): + o4_akr := cell_load(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_AK) + if iface_ops(o4_akr) != cell_load(g_iface_entries, o4_r * ESZ_IFACE_ENTRY + OFF_IE_OPS): o4_row_bad = o4_row_bad + 1 if iface_ops(o4_akr) == 0: if o4_akr == AK_NULL || o4_akr == AK_SUM || o4_akr == AK_FN: @@ -3415,25 +3415,25 @@ fn type_selftest_run() -> int: unf_gp := str_intern("UnfTGparamU") def_sym(unf_gp, SYM_TYPE, alloc_type(TYP_GENERIC_PARAM, unf_gp, 0), -1) unf_box_sa := add_struct("UnfBoxU") - w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1) - w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, unf_gp) + cell_store(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 1) + cell_store(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES, unf_gp) si_set_field_name(unf_box_sa, 0, str_intern("val")) si_set_field_type(unf_box_sa, 0, 0) // 裸码槽是 parser 对非基型的塌缩值(0 = TY_INT);本层不读这个槽。 si_set_field_type_node(unf_box_sa, 0, alloc_node(EXPR_IDENT, -1, -1, -1, unf_gp, 0, -1, 0, 0)) - w64(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) + cell_store(g_structs, unf_box_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) unf_box_ti := alloc_named_type(str_intern("UnfBoxU")) // 应用记录 `UnfBoxU[int]` / `UnfBoxU[string]`(g_gen_apply_data: [count, arg]) grow_gen_apply_data(g_gen_apply_data_count + 2) unf_gas1 := g_gen_apply_data_count - w64(g_gen_apply_data, unf_gas1 * 8, 1) - w64(g_gen_apply_data, (unf_gas1 + 1) * 8, TI_INT) + cell_store(g_gen_apply_data, unf_gas1 * 8, 1) + cell_store(g_gen_apply_data, (unf_gas1 + 1) * 8, TI_INT) g_gen_apply_data_count = unf_gas1 + 2 unf_ga_int := alloc_type(TYP_GENERIC_APPLY, unf_box_ti, unf_gas1) grow_gen_apply_data(g_gen_apply_data_count + 2) unf_gas2 := g_gen_apply_data_count - w64(g_gen_apply_data, unf_gas2 * 8, 1) - w64(g_gen_apply_data, (unf_gas2 + 1) * 8, TI_STR) + cell_store(g_gen_apply_data, unf_gas2 * 8, 1) + cell_store(g_gen_apply_data, (unf_gas2 + 1) * 8, TI_STR) g_gen_apply_data_count = unf_gas2 + 2 unf_ga_str := alloc_type(TYP_GENERIC_APPLY, unf_box_ti, unf_gas2) unf_ta := sh_struct_term(unf_ga_int) @@ -3456,7 +3456,7 @@ fn type_selftest_run() -> int: si_set_field_name(unf_p1_sa, 1, str_intern("b")) si_set_field_type(unf_p1_sa, 1, TY_STRING) si_set_field_type_node(unf_p1_sa, 1, alloc_node(0, 0, 0, 0, 0, TY_STRING, 0, 0, 0)) - w64(g_structs, unf_p1_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 2) + cell_store(g_structs, unf_p1_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 2) unf_p1_ti := alloc_named_type(str_intern("UnfP1U")) unf_tp1 := sh_struct_term(unf_p1_ti) total = total + 1 @@ -3470,7 +3470,7 @@ fn type_selftest_run() -> int: si_set_field_name(unf_p2_sa, 0, str_intern("x")) si_set_field_type(unf_p2_sa, 0, 0) si_set_field_type_node(unf_p2_sa, 0, unf_arrn) - w64(g_structs, unf_p2_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) + cell_store(g_structs, unf_p2_sa * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, 1) unf_p2_ti := alloc_named_type(str_intern("UnfP2U")) unf_tp2 := sh_struct_term(unf_p2_ti) total = total + 1 @@ -3506,7 +3506,7 @@ fn type_selftest_run() -> int: ei_set_variant_name(unf_col_ei, 0, str_intern("UnfRedU")) ei_set_variant_name(unf_col_ei, 1, str_intern("UnfGreenU")) ei_set_variant_name(unf_col_ei, 2, str_intern("UnfBlueU")) - w64(g_enums, unf_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3) + cell_store(g_enums, unf_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3) unf_col_ti := alloc_named_type(str_intern("UnfColorU")) unf_dom := sh_enum_domain_term(unf_col_ti) total = total + 1 @@ -3522,7 +3522,7 @@ fn type_selftest_run() -> int: // 变体身份包含枚举名。引擎只比较 a 和 c 两个槽,所以身份必须放进参数列表;不同枚举的同名变体不得判等价。 unf_dup_ei := add_enum("UnfDupU") ei_set_variant_name(unf_dup_ei, 0, str_intern("UnfRedU")) - w64(g_enums, unf_dup_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) + cell_store(g_enums, unf_dup_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) unf_dup_ti := alloc_named_type(str_intern("UnfDupU")) unf_vdup := sh_variant_term(unf_dup_ti, str_intern("UnfRedU")) total = total + 1 @@ -3530,7 +3530,7 @@ fn type_selftest_run() -> int: (unf_vdup >= 0 && ty_sub(unf_vdup, unf_vr) != 1 && ty_sub(unf_vr, unf_vdup) != 1), true) // 空枚举的域是底部。 unf_emp_ei := add_enum("UnfEmptyU") - w64(g_enums, unf_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0) + cell_store(g_enums, unf_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0) unf_emp_ti := alloc_named_type(str_intern("UnfEmptyU")) total = total + 1 fails = fails + ts_check("unf.enum_empty_domain_bot", tt_tag(sh_enum_domain_term(unf_emp_ti)), TT_BOT) @@ -3540,13 +3540,13 @@ fn type_selftest_run() -> int: // 非基型塌缩为 0,本层不读它;携带值不进入变体项,见 ty_shadow.cr // 展开段的说明。 unf_opt_ei := add_enum("UnfOptU") - w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) - w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("UnfTOptU")) + cell_store(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) + cell_store(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("UnfTOptU")) ei_set_variant_name(unf_opt_ei, 0, str_intern("UnfNoneU")) ei_set_variant_name(unf_opt_ei, 1, str_intern("UnfSomeU")) ei_set_variant_type_count(unf_opt_ei, 1, 1) ei_set_variant_type(unf_opt_ei, 1, 0, TY_INT) // 裸码槽(parser 事实;本层不读) - w64(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) + cell_store(g_enums, unf_opt_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) unf_opt_ti := alloc_named_type(str_intern("UnfOptU")) unf_odom := sh_enum_domain_term(unf_opt_ti) unf_onone := sh_variant_term(unf_opt_ti, str_intern("UnfNoneU")) @@ -3640,11 +3640,11 @@ fn type_selftest_run() -> int: (array_len_constraint_ok(t1_out_sl, t1_out_arr) == 0 && array_len_constraint_ok(t1_out_arr, t1_out_sl) == 1), true) grow_gen_apply_data(g_gen_apply_data_count + 4) t1_tp_s1 := g_gen_apply_data_count - w64(g_gen_apply_data, t1_tp_s1 * 8, TI_INT) - w64(g_gen_apply_data, (t1_tp_s1 + 1) * 8, t1_sl) + cell_store(g_gen_apply_data, t1_tp_s1 * 8, TI_INT) + cell_store(g_gen_apply_data, (t1_tp_s1 + 1) * 8, t1_sl) t1_tp_s2 := t1_tp_s1 + 2 - w64(g_gen_apply_data, t1_tp_s2 * 8, TI_INT) - w64(g_gen_apply_data, (t1_tp_s2 + 1) * 8, t1_arr3) + cell_store(g_gen_apply_data, t1_tp_s2 * 8, TI_INT) + cell_store(g_gen_apply_data, (t1_tp_s2 + 1) * 8, t1_arr3) g_gen_apply_data_count = t1_tp_s2 + 2 t1_tup_sl := alloc_type(TYP_TUPLE, 2, t1_tp_s1) // (int, [int]) t1_tup_arr := alloc_type(TYP_TUPLE, 2, t1_tp_s2) // (int, [int;3]) @@ -3653,11 +3653,11 @@ fn type_selftest_run() -> int: (array_len_constraint_ok(t1_tup_sl, t1_tup_arr) == 0 && array_len_constraint_ok(t1_tup_arr, t1_tup_sl) == 1), true) grow_gen_apply_data(g_gen_apply_data_count + 4) t1_ga_s1 := g_gen_apply_data_count - w64(g_gen_apply_data, t1_ga_s1 * 8, 1) - w64(g_gen_apply_data, (t1_ga_s1 + 1) * 8, t1_sl) + cell_store(g_gen_apply_data, t1_ga_s1 * 8, 1) + cell_store(g_gen_apply_data, (t1_ga_s1 + 1) * 8, t1_sl) t1_ga_s2 := t1_ga_s1 + 2 - w64(g_gen_apply_data, t1_ga_s2 * 8, 1) - w64(g_gen_apply_data, (t1_ga_s2 + 1) * 8, t1_arr3) + cell_store(g_gen_apply_data, t1_ga_s2 * 8, 1) + cell_store(g_gen_apply_data, (t1_ga_s2 + 1) * 8, t1_arr3) g_gen_apply_data_count = t1_ga_s2 + 2 t1_base := alloc_type(TYP_NAMED, 1203, 0) // 人造基类型记录,裸分配,避免假键破坏 name->ti 映射。 t1_ga_sl := alloc_type(TYP_GENERIC_APPLY, t1_base, t1_ga_s1) @@ -3754,7 +3754,7 @@ fn type_selftest_run() -> int: ei_set_variant_name(t3_col_ei, 0, str_intern("T3Red")) ei_set_variant_name(t3_col_ei, 1, str_intern("T3Green")) ei_set_variant_name(t3_col_ei, 2, str_intern("T3Blue")) - w64(g_enums, t3_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3) + cell_store(g_enums, t3_col_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 3) t3_col_ti := alloc_named_type(str_intern("T3Color")) t3_r := sh_match_variant_term(t3_col_ti, 0) t3_g := sh_match_variant_term(t3_col_ti, 1) @@ -3802,7 +3802,7 @@ fn type_selftest_run() -> int: // 4. 单变体枚举:有臂返回 1,无臂返回 0,空序列的补集是全域。 t3_only_ei := add_enum("T3Only") ei_set_variant_name(t3_only_ei, 0, str_intern("T3One")) - w64(g_enums, t3_only_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) + cell_store(g_enums, t3_only_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) t3_only_ti := alloc_named_type(str_intern("T3Only")) t3_one := sh_match_variant_term(t3_only_ti, 0) total = total + 1 @@ -3822,7 +3822,7 @@ fn type_selftest_run() -> int: sh_match_exhaustive(t3_col_ti, tt_cons(t3_r, tt_cons(t3_g, tt_cons(t3_b, tt_nil()))), 1), -1) // 6. 空枚举的域是底部,补集语义下空洞穷尽:没有值可漏,也没有反例。 t3_emp_ei := add_enum("T3Empty") - w64(g_enums, t3_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0) + cell_store(g_enums, t3_emp_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 0) t3_emp_ti := alloc_named_type(str_intern("T3Empty")) total = total + 1 fails = fails + ts_check_b("t3.empty_domain_vacuous", @@ -3833,7 +3833,7 @@ fn type_selftest_run() -> int: ei_set_variant_name(t3_mix_ei, 1, str_intern("T3Some")) ei_set_variant_type_count(t3_mix_ei, 1, 1) ei_set_variant_type(t3_mix_ei, 1, 0, TY_INT) - w64(g_enums, t3_mix_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) + cell_store(g_enums, t3_mix_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) t3_mix_ti := alloc_named_type(str_intern("T3Mix")) t3_none := sh_match_variant_term(t3_mix_ti, 0) t3_some := sh_match_variant_term(t3_mix_ti, 1) @@ -3871,18 +3871,18 @@ fn type_selftest_run() -> int: sh_match_first_missing(sh_match_bit(0), 3) == 1), true) // 10. 泛型应用记录 T3GOpt[int]:域可展开,全覆盖返回 1,变体身份与实参无关。 t3_go_ei := add_enum("T3GOpt") - w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) - w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T3GT")) + cell_store(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) + cell_store(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T3GT")) ei_set_variant_name(t3_go_ei, 0, str_intern("T3GN")) ei_set_variant_name(t3_go_ei, 1, str_intern("T3GS")) ei_set_variant_type_count(t3_go_ei, 1, 1) ei_set_variant_type(t3_go_ei, 1, 0, 0) - w64(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) + cell_store(g_enums, t3_go_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) t3_go_ti := alloc_named_type(str_intern("T3GOpt")) grow_gen_apply_data(g_gen_apply_data_count + 2) t3_gas := g_gen_apply_data_count - w64(g_gen_apply_data, t3_gas * 8, 1) - w64(g_gen_apply_data, (t3_gas + 1) * 8, TI_INT) + cell_store(g_gen_apply_data, t3_gas * 8, 1) + cell_store(g_gen_apply_data, (t3_gas + 1) * 8, TI_INT) g_gen_apply_data_count = t3_gas + 2 t3_go_ga := alloc_type(TYP_GENERIC_APPLY, t3_go_ti, t3_gas) t3_gn := sh_match_variant_term(t3_go_ga, 0) @@ -3957,23 +3957,23 @@ fn type_selftest_run() -> int: t4_dj : ., mut = 0 t4_k : ., mut = 0 t4_aks := alloc(13 * 8) - w64(t4_aks, 0 * 8, AK_INT) - w64(t4_aks, 1 * 8, AK_DEX) - w64(t4_aks, 2 * 8, AK_STRING) - w64(t4_aks, 3 * 8, AK_BOOL) - w64(t4_aks, 4 * 8, AK_UNIT) - w64(t4_aks, 5 * 8, AK_NEVER) - w64(t4_aks, 6 * 8, AK_CHAR) - w64(t4_aks, 7 * 8, AK_DYN) - w64(t4_aks, 8 * 8, AK_PRODUCT) - w64(t4_aks, 9 * 8, AK_SUM) - w64(t4_aks, 10 * 8, AK_SEQUENCE) - w64(t4_aks, 11 * 8, AK_REF) - w64(t4_aks, 12 * 8, AK_PTR) + cell_store(t4_aks, 0 * 8, AK_INT) + cell_store(t4_aks, 1 * 8, AK_DEX) + cell_store(t4_aks, 2 * 8, AK_STRING) + cell_store(t4_aks, 3 * 8, AK_BOOL) + cell_store(t4_aks, 4 * 8, AK_UNIT) + cell_store(t4_aks, 5 * 8, AK_NEVER) + cell_store(t4_aks, 6 * 8, AK_CHAR) + cell_store(t4_aks, 7 * 8, AK_DYN) + cell_store(t4_aks, 8 * 8, AK_PRODUCT) + cell_store(t4_aks, 9 * 8, AK_SUM) + cell_store(t4_aks, 10 * 8, AK_SEQUENCE) + cell_store(t4_aks, 11 * 8, AK_REF) + cell_store(t4_aks, 12 * 8, AK_PTR) loop: if t4_k >= 13: break - ka := r64(t4_aks, t4_k * 8) + ka := cell_load(t4_aks, t4_k * 8) want : ., mut = 1 if ka == AK_DYN: want = 0 @@ -4028,7 +4028,7 @@ fn type_selftest_run() -> int: ei_set_variant_type_count(t4_p_ei, 1, 1) ei_set_variant_type(t4_p_ei, 1, 0, TY_INT) ei_set_variant_type_node(t4_p_ei, 1, 0, alloc_node(0, 0, 0, 0, 0, TY_STRING, 0, 0, 0)) - w64(g_enums, t4_p_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) + cell_store(g_enums, t4_p_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 2) t4_p_ti := alloc_named_type(str_intern("T4Payload")) total = total + 1 fails = fails + ts_check_b("t4.payload_node_beats_bare_code", @@ -4040,24 +4040,24 @@ fn type_selftest_run() -> int: // application takes the payload term from its argument. The int and string // instances must produce different terms. t4_g_ei := add_enum("T4GP") - w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) - w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T4GT")) + cell_store(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 1) + cell_store(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES, str_intern("T4GT")) ei_set_variant_name(t4_g_ei, 0, str_intern("T4GS")) ei_set_variant_type_count(t4_g_ei, 0, 1) ei_set_variant_type(t4_g_ei, 0, 0, TY_INT) ei_set_variant_type_node(t4_g_ei, 0, 0, alloc_node(EXPR_IDENT, 0, 0, 0, str_intern("T4GT"), 0, 0, 0, 0)) - w64(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) + cell_store(g_enums, t4_g_ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, 1) t4_g_ti := alloc_named_type(str_intern("T4GP")) grow_gen_apply_data(g_gen_apply_data_count + 2) t4_gs := g_gen_apply_data_count - w64(g_gen_apply_data, t4_gs * 8, 1) - w64(g_gen_apply_data, (t4_gs + 1) * 8, TI_INT) + cell_store(g_gen_apply_data, t4_gs * 8, 1) + cell_store(g_gen_apply_data, (t4_gs + 1) * 8, TI_INT) g_gen_apply_data_count = t4_gs + 2 t4_g_ga := alloc_type(TYP_GENERIC_APPLY, t4_g_ti, t4_gs) grow_gen_apply_data(g_gen_apply_data_count + 2) t4_gs2 := g_gen_apply_data_count - w64(g_gen_apply_data, t4_gs2 * 8, 1) - w64(g_gen_apply_data, (t4_gs2 + 1) * 8, TI_STR) + cell_store(g_gen_apply_data, t4_gs2 * 8, 1) + cell_store(g_gen_apply_data, (t4_gs2 + 1) * 8, TI_STR) g_gen_apply_data_count = t4_gs2 + 2 t4_g_ga2 := alloc_type(TYP_GENERIC_APPLY, t4_g_ti, t4_gs2) total = total + 1 diff --git a/src/compiler/type_terms.cr b/src/compiler/type_terms.cr index 0540ffd7..98076219 100644 --- a/src/compiler/type_terms.cr +++ b/src/compiler/type_terms.cr @@ -31,15 +31,15 @@ TT_CONS : int = 10 // ─── 访问器(i = 类型项行号;调用方保证 0 ≤ i < tt_count())─── fn tt_tag(i: int) -> int: - return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG) + return cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG) fn tt_a(i: int) -> int: - return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A) + return cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A) fn tt_b(i: int) -> int: - return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B) + return cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B) fn tt_c(i: int) -> int: - return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C) + return cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C) fn tt_d(i: int) -> int: - return r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D) + return cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D) fn tt_count() -> int: return g_type_term_count @@ -108,7 +108,7 @@ fn grow_type_terms(needed: int): if nc < needed: nc = needed + 64 nb := alloc(nc * ESZ_TYPE_TERM) - _dyncpy(g_type_terms, g_type_term_cap * ESZ_TYPE_TERM, nb) + cell_copy(g_type_terms, g_type_term_cap * ESZ_TYPE_TERM, nb) g_type_terms = nb g_type_term_cap = nc @@ -132,7 +132,7 @@ fn grow_tt_index(needed: int): loop: if i >= nc: break - w64(nb, i * 8, -1) + cell_store(nb, i * 8, -1) i = i + 1 g_tt_index = nb g_tt_index_cap = nc @@ -145,16 +145,16 @@ fn tt_reindex(): loop: if i >= g_type_term_count: break - h := r64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH) + h := cell_load(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH) cap := g_tt_index_cap p : ., mut = tt_mod(h, cap) loop: - if r64(g_tt_index, p * 8) < 0: + if cell_load(g_tt_index, p * 8) < 0: break p = p + 1 if p >= cap: p = 0 - w64(g_tt_index, p * 8, i) + cell_store(g_tt_index, p * 8, i) g_tt_index_count = g_tt_index_count + 1 i = i + 1 @@ -217,7 +217,7 @@ fn tt_term(tag: int, a: int, b: int, c: int, d: int) -> int: cap := g_tt_index_cap p : ., mut = tt_mod(h, cap) loop: - idx := r64(g_tt_index, p * 8) + idx := cell_load(g_tt_index, p * 8) if idx < 0: break // Key comparison uses all five fields. The stored hash is not part of @@ -234,14 +234,14 @@ fn tt_term(tag: int, a: int, b: int, c: int, d: int) -> int: // 未命中:追加新项,写入本轮探测到的空槽(轮内索引未被改动) grow_type_terms(g_type_term_count + 1) i := g_type_term_count - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG, tag) - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A, a) - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B, b) - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C, c) - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D, d) - w64(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH, h) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_TAG, tag) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_A, a) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_B, b) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_C, c) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_D, d) + cell_store(g_type_terms, i * ESZ_TYPE_TERM + OFF_TT_HASH, h) g_type_term_count = i + 1 - w64(g_tt_index, p * 8, i) + cell_store(g_tt_index, p * 8, i) g_tt_index_count = g_tt_index_count + 1 ret = i break diff --git a/src/kernel/bytes.cr b/src/kernel/bytes.cr index 58cd664d..061791ad 100644 --- a/src/kernel/bytes.cr +++ b/src/kernel/bytes.cr @@ -71,3 +71,44 @@ fn _dyncpy(src: string, nbytes: int, dst: string): break w8(dst, ci, bu8(src, ci)) ci = ci + 1 + +// ── Compiler cell API ── +// The frontend uses these names for compiler-owned table slots. The cell width +// and raw machine access stay in this low-level module; semantic passes do not +// read or write w64/r64/r32/load8 directly. + +fn cell_store(buf: string, pos: int, val: int): + w64(buf, pos, val) + +fn cell_load(buf: string, pos: int) -> int: + return r64(buf, pos) + +fn cell_store32(buf: string, pos: int, val: int): + w32(buf, pos, val) + +fn cell_load32(buf: string, pos: int) -> int: + return r32(buf, pos) + +fn cell_store8(buf: string, pos: int, val: int): + w8(buf, pos, val) + +fn cell_load8(buf: string, pos: int) -> int: + return bu8(buf, pos) + +fn cell_store_byte(buf: string, pos: int, val: int): + store8(buf, pos, val) + +fn cell_load_byte(buf: string, pos: int) -> int: + return load8(buf, pos) + +fn cell_load64(buf: string, pos: int) -> int: + return load64(buf, pos) + +fn cell_copy(src: string, nbytes: int, dst: string): + _dyncpy(src, nbytes, dst) + +fn cell_load_str(buf: string, pos: int) -> string: + return load_str_ptr(buf, pos) + +fn cell_store_str(buf: string, pos: int, val: string) -> int: + return store_str_ptr(buf, pos, val) diff --git a/src/os/linux/linux_frontend_abi.cr b/src/os/linux/linux_frontend_abi.cr new file mode 100644 index 00000000..4ad84e60 --- /dev/null +++ b/src/os/linux/linux_frontend_abi.cr @@ -0,0 +1,19 @@ +// === os/linux/frontend_abi.cr === +// OS-axis frontend ABI builtins. +// +// Process-entry access and Linux syscall names belong to the OS axis, not to +// the semantic checker. The target composition root calls this registration; +// prelude_builtin_return_ti delegates syscall3/syscall4 here. + +fn linux_frontend_builtin_register(): + prelude_builtin_add("get_arg", TI_STR) + +fn linux_frontend_builtin_return_ti(name_ni: int) -> int: + if name_ni < 0: + return -1 + s := istr_get(name_ni) + if s == "syscall3": + return TI_INT + if s == "syscall4": + return TI_INT + return -1 diff --git a/src/runtime/runtime_frontend_abi.cr b/src/runtime/runtime_frontend_abi.cr new file mode 100644 index 00000000..1cbc0b77 --- /dev/null +++ b/src/runtime/runtime_frontend_abi.cr @@ -0,0 +1,27 @@ +// === runtime/frontend_abi.cr === +// Runtime/execution-strategy frontend ABI builtins. +// +// Allocation and scheduler/fiber entry points are runtime policy, not target +// value layout. They stay on the runtime axis and are composed by the target +// root; the checker only receives their return types. + +fn runtime_frontend_builtin_register(): + prelude_builtin_add("alloc", TI_STR) + prelude_builtin_add("sched_call_0", TI_INT) + prelude_builtin_add("sched_call_1", TI_INT) + prelude_builtin_add("sched_call_2", TI_INT) + prelude_builtin_add("sched_call_3", TI_INT) + prelude_builtin_add("sched_call_4", TI_INT) + prelude_builtin_add("fiber_switch", TI_INT) + prelude_builtin_add("fiber_init", TI_INT) + prelude_builtin_add("sched_go", TI_INT) + prelude_builtin_add("m_start_workers", TI_UNIT) + prelude_builtin_add("g_set_curg", TI_UNIT) + prelude_builtin_add("g_get_curg", TI_INT) + +fn runtime_frontend_builtin_is_alloc(name_ni: int) -> int: + if name_ni < 0: + return 0 + if istr_get(name_ni) == "alloc": + return 1 + return 0 diff --git a/src/targets/x86_64-linux-elf/Core.toml b/src/targets/x86_64-linux-elf/Core.toml new file mode 100644 index 00000000..50ee6a77 --- /dev/null +++ b/src/targets/x86_64-linux-elf/Core.toml @@ -0,0 +1,4 @@ +name = "corearch" +arch = "x86_64" +system = "linux" +container = "elf" diff --git a/src/targets/x86_64-linux/_import.cr b/src/targets/x86_64-linux-elf/_import.cr similarity index 99% rename from src/targets/x86_64-linux/_import.cr rename to src/targets/x86_64-linux-elf/_import.cr index 0eb3d5e4..e4af2728 100644 --- a/src/targets/x86_64-linux/_import.cr +++ b/src/targets/x86_64-linux-elf/_import.cr @@ -7,6 +7,7 @@ import toml import ast import ccr_schema import globals +import bytes import dyn_arr import backend_schema import backend_globals diff --git a/src/targets/x86_64-linux-elf/frontend_target.cr b/src/targets/x86_64-linux-elf/frontend_target.cr new file mode 100644 index 00000000..ab96b9ef --- /dev/null +++ b/src/targets/x86_64-linux-elf/frontend_target.cr @@ -0,0 +1,65 @@ +// === targets/x86_64-linux-elf/frontend_target.cr === +// Frontend-visible target description for the x86_64 / Linux / ELF instance. +// +// The three axes are explicit. The frontend asks this file for import roots, +// the backend command, and runtime source locations; it does not embed target +// directory names or backend flags itself. + +fn target_name() -> string: + return "x86_64-linux-elf" + +fn target_arch_name() -> string: + return "x86_64" + +fn target_system_name() -> string: + return "linux" + +fn target_container_name() -> string: + return "elf" + +fn target_import_root_count() -> int: + return 10 + +// Return a search root with its trailing slash. The source directory is +// handled separately by module.cr because it is project-relative. +fn target_import_root(i: int) -> string: + if i == 0: + return "src/stdlib/" + if i == 1: + return "src/runtime/" + if i == 2: + return "src/kernel/" + if i == 3: + return "src/format/elf/" + if i == 4: + return "src/arch/x86_64/" + if i == 5: + return "src/os/linux/" + if i == 6: + return "src/compiler/" + if i == 7: + return "src/arch/hit/" + if i == 8: + return "src/lattice/" + if i == 9: + return "src/targets/x86_64-linux-elf/" + return "" + +fn target_so_index_path(home_dir: string, fs_path: string) -> string: + return home_dir + "/.core/lib/" + fs_path + "/index" + +fn target_static_runtime_source() -> string: + return "src/runtime/rt.cr" + +// Build the backend command for this target. The caller prefixes the sibling +// corearch directory; the target owns the backend name and ABI flags. +fn target_backend_command(ccr_path: string, out_path: string, is_static: int, opt_level: int) -> string: + cmd : ., mut = "corearch " + ccr_path + " --elf" + if opt_level > 0: + cmd = cmd + " --opt-level " + int_str(opt_level) + if is_static != 0: + cmd = cmd + " --static" + else: + cmd = cmd + " --link auto" + cmd = cmd + " -o " + out_path + return cmd diff --git a/src/targets/x86_64-linux/main.cr b/src/targets/x86_64-linux-elf/main.cr similarity index 98% rename from src/targets/x86_64-linux/main.cr rename to src/targets/x86_64-linux-elf/main.cr index ccb16f9f..463670b6 100644 --- a/src/targets/x86_64-linux/main.cr +++ b/src/targets/x86_64-linux-elf/main.cr @@ -1,9 +1,9 @@ -// === targets/x86_64-linux/main.cr === +// === targets/x86_64-linux-elf/main.cr === // Backend project entry: .ccr to ELF, assembly, or shared object. // Self-hosted counterpart of src/arch/x86_64/corearch.cr. // -// This file is the live project-mode entry for the x86_64-linux target. -// `corec build src/targets/x86_64-linux` starts at main, calls corearch_main, +// This file is the live project-mode entry for the x86_64-linux-elf target. +// `corec build src/targets/x86_64-linux-elf` starts at main, calls corearch_main, // loads the .ccr, and rebuilds the linear schedule. The backend bootstrap stages // all pass through this entry. // There are two live entries: the corearch concat entry in diff --git a/src/targets/x86_64-linux-elf/target_abi.cr b/src/targets/x86_64-linux-elf/target_abi.cr new file mode 100644 index 00000000..26a19132 --- /dev/null +++ b/src/targets/x86_64-linux-elf/target_abi.cr @@ -0,0 +1,14 @@ +// === targets/x86_64-linux-elf/target_abi.cr === +// Combination-root frontend ABI composition for x86_64-linux-elf. +// +// The root owns no target facts. It composes the architecture, OS, and runtime +// axes; the container axis (ELF) is provided by src/format/elf and is not part +// of this frontend ABI table. + +fn target_builtin_register(): + x86_frontend_builtin_register() + linux_frontend_builtin_register() + runtime_frontend_builtin_register() + +fn target_builtin_return_ti(name_ni: int) -> int: + return linux_frontend_builtin_return_ti(name_ni) diff --git a/src/targets/x86_64-linux/Core.toml b/src/targets/x86_64-linux/Core.toml deleted file mode 100644 index f385ca8b..00000000 --- a/src/targets/x86_64-linux/Core.toml +++ /dev/null @@ -1 +0,0 @@ -name = "corearch" diff --git a/tests/selfhost/test_backend_bootstrap.py b/tests/selfhost/test_backend_bootstrap.py index bf4e3232..b3f433f0 100644 --- a/tests/selfhost/test_backend_bootstrap.py +++ b/tests/selfhost/test_backend_bootstrap.py @@ -11,7 +11,7 @@ COREC = BUILD / "corec" COREARCH = BUILD / "corearch" # 组合根(三轴组合的 target triple 命名)——project-mode 入口 = 该目录 main.cr -BACKEND_SOURCE = BASE / "src" / "targets" / "x86_64-linux" +BACKEND_SOURCE = BASE / "src" / "targets" / "x86_64-linux-elf" def run_checked(args: list[str], label: str, env: dict[str, str]) -> bool: diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index 8cf6aeca..b9b0020e 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -178,10 +178,10 @@ # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 canary_elf pa 826d74cf7f099f9758e11a64d443695ff8360327a88e7eb5922ddfd617e0dce5 53430 -pa_ccr pa.ccr e581daf5a8e30f91745d3c0b6fb6ae6421422248507ec401a67228ae7c6c2355 96985 -pa_static_ccr pa_st.bin.ccr a7d2433b0c78428ed93e434ccd617f1308362d7bb268764712d2972e243485a1 97128 -gt_ccr gt.ccr 21b1f39c96bd2242b963bb5a1726eb93b912739f77c31317da055d6b37d88103 143687 -gt_static_ccr gt_st.bin.ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662f19bbdc60271 143830 +pa_ccr pa.ccr 8b0575e0060df241a697b2e2d78e1a128eeff3515b8444f178cf127e03077f7f 96985 +pa_static_ccr pa_st.bin.ccr 667ca1f65421cc11cdc8935e070d663e2b4fa1b692e26e80dad75ace776103dd 97128 +gt_ccr gt.ccr 4df02acd1ea343601ae8a016cff093bf56d7a90f7b6b06c2811b1b3a3a2d3e41 143687 +gt_static_ccr gt_st.bin.ccr bacd9eddd956dce88df3de675a29d0857d832c5a843781707a2e84b3c864f8a1 143830 # ⚠ **本组为最终值(全部编译器源码改动落定后重测)**:重锁分量有两个,**都不是发射面泄漏**—— # ① **内容面**:`fmt.cr` 新增 `bool_str` ⇒ 凡 `import fmt` 的程序多一个函数体(ELF/`.ccr` 同增); # ② **编译器身份位移**(#123 同机制):编译器源码任何改动都会改变**其自身 intern 序** ⇒ 同语义下 @@ -281,3 +281,14 @@ gt_static_ccr gt_st.bin.ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662 # 四条 `.ccr` **逐字节不变**(`.ccr` 由 `corec` 前端生成,不含后端全局)。 # D1 确定性闸门 5/5 PASS(重锁前置已绿)。 # 旧值留痕(自本批起作废):canary_elf `dfae9d845362856fe3609ecd23fcb2deaaa7eae1d2ca39b7a242695513e9bb09` 53430。 + +# ── 【本批重锁 2026-10-23】前端机器边界清理(三轴 target + builtin/Mapping)── +# 四条 `.ccr`:size 全不变,sha 全变;canary ELF 不变。 +# 归因 = 编译器侧全局登记顺序改变:`g_rt_builtin_*` 从 checker.cr 移到 prelude.cr, +# 内建表按 arch/os/runtime 三轴重组;用户程序 .ccr 内嵌的编译器 intern/身份随之位移, +# 机码与语义未变。D1 5/5 PASS,ELF canary 逐字节不变。 +# 旧值留痕(自本批起作废): +# pa_ccr e581daf5a8e30f91745d3c0b6fb6ae6421422248507ec401a67228ae7c6c2355 96985 +# pa_static_ccr a7d2433b0c78428ed93e434ccd617f1308362d7bb268764712d2972e243485a1 97128 +# gt_ccr 21b1f39c96bd2242b963bb5a1726eb93b912739f77c31317da055d6b37d88103 143687 +# gt_static_ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662f19bbdc60271 143830 diff --git a/tools/baseline/parity_run.sh b/tools/baseline/parity_run.sh index d34e22dd..db17d66a 100644 --- a/tools/baseline/parity_run.sh +++ b/tools/baseline/parity_run.sh @@ -57,7 +57,7 @@ run_one() { # $1 tier tag, $2 file echo "=== Tier 3: backend/kernel units ===" run_one t3 src/compiler/ccr_io.cr - for f in src/format/elf/*.cr src/os/linux/*.cr src/arch/x86_64/*.cr src/lattice/*.cr src/targets/x86_64-linux/*.cr; do + for f in src/format/elf/*.cr src/os/linux/*.cr src/arch/x86_64/*.cr src/lattice/*.cr src/targets/x86_64-linux-elf/*.cr; do run_one t3 "$f" done diff --git a/tools/v4_migrate.py b/tools/v4_migrate.py index 7f8fdfc4..bea9e0e8 100644 --- a/tools/v4_migrate.py +++ b/tools/v4_migrate.py @@ -38,7 +38,7 @@ # 「七目录面 74 档」会漏掉四棵同样进编译单元的子树 —— `src/lattice`(1) · # `src/lsp`(7) · `src/kernel`(9) · `src/scheduler`(1);逐目录 = compiler 38 · # stdlib 19 · kernel 9 · lsp 7 · arch/x86_64 5 · os/linux 3 · format/elf 3 · -# targets/x86_64-linux 2 · runtime 2 · arch/hit 2 · scheduler 1 · lattice 1)。 +# targets/x86_64-linux-elf 2 · runtime 2 · arch/hit 2 · scheduler 1 · lattice 1)。 # `tests/`·`docs/`·`examples/`·`tools/`·`legacy_asm_backend/` 下的 `.cr` 属 T6,**不动**。 SRC_ROOT = 'src' From 6776e620fb59f748b4b0d3d1d375aa8caaa1e87a Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 14:15:05 +0800 Subject: [PATCH 32/58] target: split the combination root into three components Use src/targets/x86_64/linux/elf/ instead of one hyphenated target name. The root only composes the three axes; architecture, OS, and container implementations remain in their own trees. Update the build manifest, project-mode import root, tests, and parity script. --- build_selfhost_native.py | 20 ++++++------- .../2026-10-23-frontend-machine-boundary.md | 28 +++++++++---------- .../specs/2026-09-10-x86-instance-design.md | 2 +- src/arch/hit/lower_to_core.cr | 2 +- src/arch/x86_64/backend_flags.cr | 2 +- src/arch/x86_64/regalloc.cr | 2 +- src/compiler/ccr_io.cr | 2 +- .../linux/elf}/Core.toml | 0 .../linux/elf}/_import.cr | 0 .../linux/elf}/frontend_target.cr | 6 ++-- .../linux/elf}/main.cr | 6 ++-- .../linux/elf}/target_abi.cr | 4 +-- tests/selfhost/test_backend_bootstrap.py | 2 +- tools/baseline/parity_run.sh | 2 +- tools/v4_migrate.py | 2 +- 15 files changed, 40 insertions(+), 40 deletions(-) rename src/targets/{x86_64-linux-elf => x86_64/linux/elf}/Core.toml (100%) rename src/targets/{x86_64-linux-elf => x86_64/linux/elf}/_import.cr (100%) rename src/targets/{x86_64-linux-elf => x86_64/linux/elf}/frontend_target.cr (93%) rename src/targets/{x86_64-linux-elf => x86_64/linux/elf}/main.cr (97%) rename src/targets/{x86_64-linux-elf => x86_64/linux/elf}/target_abi.cr (79%) diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 05fe9592..3ac9292b 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -127,15 +127,15 @@ 'src/arch/x86_64/corearch.cr', ] -# 组合根(x86_64-linux-elf target triple):project-mode 入口链——`corec build -# src/targets/x86_64-linux-elf` 的入口 = 该目录 main.cr(load_project 直读)+ +# 组合根(x86_64/linux/elf target triple):project-mode 入口链——`corec build +# src/targets/x86_64/linux/elf` 的入口 = 该目录 main.cr(load_project 直读)+ # _import.cr(load_imports 直读)+ Core.toml(工程名)。**不入 concat**: # main.cr 的 fn main 与 concat 追加的 wrapper 冲突,且其 .cr 经 project-mode # 独立收编(stage0/stage 链即此入口)。仅纳入清单存在性守卫。 x86_linux_elf_target_files = [ - 'src/targets/x86_64-linux-elf/main.cr', - 'src/targets/x86_64-linux-elf/_import.cr', - 'src/targets/x86_64-linux-elf/Core.toml', + 'src/targets/x86_64/linux/elf/main.cr', + 'src/targets/x86_64/linux/elf/_import.cr', + 'src/targets/x86_64/linux/elf/Core.toml', ] @@ -337,8 +337,8 @@ def main(): 'src/arch/x86_64/x86_frontend_abi.cr', 'src/os/linux/linux_frontend_abi.cr', 'src/runtime/runtime_frontend_abi.cr', - 'src/targets/x86_64-linux-elf/target_abi.cr', - 'src/targets/x86_64-linux-elf/frontend_target.cr', + 'src/targets/x86_64/linux/elf/target_abi.cr', + 'src/targets/x86_64/linux/elf/frontend_target.cr', # R2 P0 类型项引擎(建层):类型项 DAG 表 + 构造去重 → 判定引擎(桩)→ 自测通道。 # 置于 checker 之后(计划 Step 9 指定位置);引擎尚无 checker 侧消费者, # 本批为纯增量(checker/ir_gen/后端零改动 = 产物零变化)。 @@ -409,7 +409,7 @@ def main(): arch_x86_64_files + format_elf_files + os_linux_files + backend_support_files) guard_manifest(corearch_files, 'corearch') - guard_manifest(x86_linux_elf_target_files, 'x86_64-linux-elf target') + guard_manifest(x86_linux_elf_target_files, 'x86_64/linux/elf target') compile_and_assemble( concat(corearch_files, wrapper_fn='corearch_main'), label='corearch', @@ -449,8 +449,8 @@ def main(): 'src/arch/x86_64/x86_frontend_abi.cr', 'src/os/linux/linux_frontend_abi.cr', 'src/runtime/runtime_frontend_abi.cr', - 'src/targets/x86_64-linux-elf/target_abi.cr', - 'src/targets/x86_64-linux-elf/frontend_target.cr', + 'src/targets/x86_64/linux/elf/target_abi.cr', + 'src/targets/x86_64/linux/elf/frontend_target.cr', # R2 P1:checker 的判定入口引用桥接层(sh_term_of_ti;影子挂点已随 P5 T5 下线)→ # corelsp 也必须链接 引擎 + 桥接层(相对顺序与 corec 一致;corearch 无 checker 仍不含)。 'src/compiler/type_terms.cr', diff --git a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md index 169a8058..42522e97 100644 --- a/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md +++ b/docs/superpowers/plans/2026-10-23-frontend-machine-boundary.md @@ -55,7 +55,7 @@ cmp /tmp/corec2 /tmp/corec3 - 新建 `src/arch/x86_64/backend_globals.cr` - 新建 `src/arch/x86_64/backend_arrays.cr` - `build_selfhost_native.py` -- `src/targets/x86_64-linux-elf/_import.cr` +- `src/targets/x86_64/linux/elf/_import.cr` ### 步骤 @@ -64,7 +64,7 @@ cmp /tmp/corec2 /tmp/corec3 3. 从 `src/compiler/globals.cr` 删除这些 `g_x86_*` 声明。保留 `g_opt_*`、`g_sgs`、`.so` 侧表和规格注记侧表。 4. 从 `src/compiler/dyn_arr.cr` 删除 x86 扩容函数。保留 `grow_so_side`、`grow_spec_side` 和其余前端扩容函数。 5. 在 `build_selfhost_native.py` 的 `arch_x86_64_files` 开头加入两个新文件,顺序为 `backend_globals.cr`、`backend_arrays.cr`、`regalloc.cr`。 -6. 在 `src/targets/x86_64-linux-elf/_import.cr` 中加入 `import backend_globals` 和 `import backend_arrays`,位置在 `import regalloc` 之前。 +6. 在 `src/targets/x86_64/linux/elf/_import.cr` 中加入 `import backend_globals` 和 `import backend_arrays`,位置在 `import regalloc` 之前。 ### 验收 @@ -82,16 +82,16 @@ cmp /tmp/corec2 /tmp/corec3 - `src/compiler/backend_flags.cr` 移到 `src/arch/x86_64/backend_flags.cr` - `src/compiler/regalloc-consistency.cr` 移到 `docs/maintainer/design/regalloc-consistency.md` - `build_selfhost_native.py` -- `src/targets/x86_64-linux-elf/_import.cr` +- `src/targets/x86_64/linux/elf/_import.cr` - 相关路径引用 ### 步骤 1. 移动文件,保留文件内部函数名和代码。 2. 更新 `backend_support_files` 中的路径。 -3. 更新 `src/targets/x86_64-linux-elf/_import.cr` 的导入名。 +3. 更新 `src/targets/x86_64/linux/elf/_import.cr` 的导入名。 4. 搜索 `src/compiler/corearch.cr`、`src/compiler/backend_flags.cr` 和 `regalloc-consistency.cr` 的路径引用,全部改为新位置。 -5. 不修改 `src/targets/x86_64-linux-elf/main.cr` 的行为。 +5. 不修改 `src/targets/x86_64/linux/elf/main.cr` 的行为。 ### 验收 @@ -196,7 +196,7 @@ fn mp_offset(index: int, ti: int) -> int - `src/compiler/ty_shadow.cr` - `src/compiler/ast.cr` - `build_selfhost_native.py` -- `src/targets/x86_64-linux-elf/_import.cr` +- `src/targets/x86_64/linux/elf/_import.cr` ### 步骤 @@ -258,16 +258,16 @@ fn mp_offset(index: int, ti: int) -> int arch = x86_64 system = linux container = elf -target = x86_64-linux-elf -root = src/targets/x86_64-linux-elf/ +target = x86_64/linux/elf +root = src/targets/x86_64/linux/elf/ ``` -`Core.toml` 记录 `arch`、`system`、`container`;`src/targets/x86_64-linux-elf/frontend_target.cr` 提供 target name、import roots、backend command 和 runtime source 路径。 +`Core.toml` 记录 `arch`、`system`、`container`;`src/targets/x86_64/linux/elf/frontend_target.cr` 提供 target name、import roots、backend command 和 runtime source 路径。 ### 文件 -- 新建 `src/targets/x86_64-linux-elf/frontend_target.cr` -- 新建 `src/targets/x86_64-linux-elf/target_abi.cr` +- 新建 `src/targets/x86_64/linux/elf/frontend_target.cr` +- 新建 `src/targets/x86_64/linux/elf/target_abi.cr` - 新建 `src/arch/x86_64/x86_frontend_abi.cr` - 新建 `src/os/linux/linux_frontend_abi.cr` - 新建 `src/runtime/runtime_frontend_abi.cr` @@ -319,14 +319,14 @@ root = src/targets/x86_64-linux-elf/ - `src/compiler/main.cr` - `src/compiler/module.cr` -- `src/targets/x86_64-linux-elf/frontend_target.cr` +- `src/targets/x86_64/linux/elf/frontend_target.cr` ### 结果 - `main.cr` 不再拼 `corearch` 命令、`--elf`、`--static`、`--link auto`;改由 `target_backend_command`。 - `read_static_runtime_source` 不再写死 `src/runtime/rt.cr`;改由 target 描述。 -- `module.cr` 的 import 搜索根、`.so` index 路径改由 target 描述提供;前端不再写死 `src/format/elf`、`src/arch/x86_64`、`src/os/linux`、`src/targets/x86_64-linux-elf`。 -- `src/targets/x86_64-linux` 旧目录改名为 `src/targets/x86_64-linux-elf`;`test_backend_bootstrap.py`、`parity_run.sh` 同步。 +- `module.cr` 的 import 搜索根、`.so` index 路径改由 target 描述提供;前端不再写死 `src/format/elf`、`src/arch/x86_64`、`src/os/linux`、`src/targets/x86_64/linux/elf`。 +- `src/targets/x86_64-linux` 旧目录改名为 `src/targets/x86_64/linux/elf`;`test_backend_bootstrap.py`、`parity_run.sh` 同步。 ### 本批验证(实测) diff --git a/docs/superpowers/specs/2026-09-10-x86-instance-design.md b/docs/superpowers/specs/2026-09-10-x86-instance-design.md index 7588c509..826d9d58 100644 --- a/docs/superpowers/specs/2026-09-10-x86-instance-design.md +++ b/docs/superpowers/specs/2026-09-10-x86-instance-design.md @@ -8,7 +8,7 @@ x86 实例是三个轴的组合: 架构 x86_64 × 格式 ELF × OS Linux ``` -组合根目录必须显式包含三部分:`src/targets/x86_64-linux-elf/`;`Core.toml` 同时记录 `arch = "x86_64"`、`system = "linux"`、`container = "elf"`。旧的两段名 `x86_64-linux` 只表示架构和系统,不能作为完整目标名。 +组合根目录必须显式包含三部分:`src/targets/x86_64/linux/elf/`;`Core.toml` 同时记录 `arch = "x86_64"`、`system = "linux"`、`container = "elf"`。旧的两段名 `x86_64-linux` 只表示架构和系统,不能作为完整目标名。 架构轴提供指令、寄存器、编码和序列。格式轴提供容器、段、重定位和链接。OS 轴提供系统调用、入口和 ABI 约定。 diff --git a/src/arch/hit/lower_to_core.cr b/src/arch/hit/lower_to_core.cr index 71e30167..002a57fb 100644 --- a/src/arch/hit/lower_to_core.cr +++ b/src/arch/hit/lower_to_core.cr @@ -34,7 +34,7 @@ // // This file is part of two builds: the corearch concat list, after common and // before the kernel and architecture files, and the project-mode unit -// src/targets/x86_64-linux-elf/_import.cr. The import lines also let standalone +// src/targets/x86_64/linux/elf/_import.cr. The import lines also let standalone // `corec check` and project mode resolve symbols; concat builds strip them. import io diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index ba7daeb3..68501449 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -1,6 +1,6 @@ // 后端 CLI 旗标注册单源。 // concat 入口(corearch.cr)与组合根 project-mode 入口 -// (src/targets/x86_64-linux-elf/main.cr)共用本函数,避免两处注册表漂移。 +// (src/targets/x86_64/linux/elf/main.cr)共用本函数,避免两处注册表漂移。 fn register_backend_flags(): cli_flag_bool("elf", "", "Output ELF binary (default)") cli_flag_bool("shared", "", "Output shared library (.so)") diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index 24423564..c6af6b6f 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -898,7 +898,7 @@ fn alloc_registers(): // nod_* 访问器读 g_v7_nod_sem 的 28 字节语义字段,nod_edge_first 和 nod_edge_count // 读邻接域。REG 展开和函数边界回填(root span 到 g_ir_func_instr_start 和 // g_ir_func_instr_count)留在 loader。 -// 调用方是 corearch.cr 和组合根 src/targets/x86_64-linux-elf/main.cr;load_ccr 成功后、 +// 调用方是 corearch.cr 和组合根 src/targets/x86_64/linux/elf/main.cr;load_ccr 成功后、 // 分派和发射前调用一次。 fn build_linear_schedule(): grow_ir_instrs(g_v7_nod_count) diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 3ac5c0a1..7a482c25 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -1048,7 +1048,7 @@ fn inject_var_shift() -> int: // segments in canonical order. TYPE and IFACE are required. // Loader output is semantic objects plus guards. The linear NOD-to-g_ir_instrs // rebuild is outside this file: build_linear_schedule in regalloc.cr is called by -// corearch.cr or src/targets/x86_64-linux-elf/main.cr after a successful load. +// corearch.cr or src/targets/x86_64/linux/elf/main.cr after a successful load. // Parse order: STR -> SYM (globals and funcs rebuild the var namespace) -> REG // (derive nstart/ncount and function instruction bounds) -> NOD (40B; 28B // semantic fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT (28B diff --git a/src/targets/x86_64-linux-elf/Core.toml b/src/targets/x86_64/linux/elf/Core.toml similarity index 100% rename from src/targets/x86_64-linux-elf/Core.toml rename to src/targets/x86_64/linux/elf/Core.toml diff --git a/src/targets/x86_64-linux-elf/_import.cr b/src/targets/x86_64/linux/elf/_import.cr similarity index 100% rename from src/targets/x86_64-linux-elf/_import.cr rename to src/targets/x86_64/linux/elf/_import.cr diff --git a/src/targets/x86_64-linux-elf/frontend_target.cr b/src/targets/x86_64/linux/elf/frontend_target.cr similarity index 93% rename from src/targets/x86_64-linux-elf/frontend_target.cr rename to src/targets/x86_64/linux/elf/frontend_target.cr index ab96b9ef..ec9685ab 100644 --- a/src/targets/x86_64-linux-elf/frontend_target.cr +++ b/src/targets/x86_64/linux/elf/frontend_target.cr @@ -1,4 +1,4 @@ -// === targets/x86_64-linux-elf/frontend_target.cr === +// === targets/x86_64/linux/elf/frontend_target.cr === // Frontend-visible target description for the x86_64 / Linux / ELF instance. // // The three axes are explicit. The frontend asks this file for import roots, @@ -6,7 +6,7 @@ // directory names or backend flags itself. fn target_name() -> string: - return "x86_64-linux-elf" + return "x86_64/linux/elf" fn target_arch_name() -> string: return "x86_64" @@ -42,7 +42,7 @@ fn target_import_root(i: int) -> string: if i == 8: return "src/lattice/" if i == 9: - return "src/targets/x86_64-linux-elf/" + return "src/targets/x86_64/linux/elf/" return "" fn target_so_index_path(home_dir: string, fs_path: string) -> string: diff --git a/src/targets/x86_64-linux-elf/main.cr b/src/targets/x86_64/linux/elf/main.cr similarity index 97% rename from src/targets/x86_64-linux-elf/main.cr rename to src/targets/x86_64/linux/elf/main.cr index 463670b6..a81299d8 100644 --- a/src/targets/x86_64-linux-elf/main.cr +++ b/src/targets/x86_64/linux/elf/main.cr @@ -1,9 +1,9 @@ -// === targets/x86_64-linux-elf/main.cr === +// === targets/x86_64/linux/elf/main.cr === // Backend project entry: .ccr to ELF, assembly, or shared object. // Self-hosted counterpart of src/arch/x86_64/corearch.cr. // -// This file is the live project-mode entry for the x86_64-linux-elf target. -// `corec build src/targets/x86_64-linux-elf` starts at main, calls corearch_main, +// This file is the live project-mode entry for the x86_64/linux/elf target. +// `corec build src/targets/x86_64/linux/elf` starts at main, calls corearch_main, // loads the .ccr, and rebuilds the linear schedule. The backend bootstrap stages // all pass through this entry. // There are two live entries: the corearch concat entry in diff --git a/src/targets/x86_64-linux-elf/target_abi.cr b/src/targets/x86_64/linux/elf/target_abi.cr similarity index 79% rename from src/targets/x86_64-linux-elf/target_abi.cr rename to src/targets/x86_64/linux/elf/target_abi.cr index 26a19132..c5f9a9e5 100644 --- a/src/targets/x86_64-linux-elf/target_abi.cr +++ b/src/targets/x86_64/linux/elf/target_abi.cr @@ -1,5 +1,5 @@ -// === targets/x86_64-linux-elf/target_abi.cr === -// Combination-root frontend ABI composition for x86_64-linux-elf. +// === targets/x86_64/linux/elf/target_abi.cr === +// Combination-root frontend ABI composition for x86_64/linux/elf. // // The root owns no target facts. It composes the architecture, OS, and runtime // axes; the container axis (ELF) is provided by src/format/elf and is not part diff --git a/tests/selfhost/test_backend_bootstrap.py b/tests/selfhost/test_backend_bootstrap.py index b3f433f0..bcba0ba7 100644 --- a/tests/selfhost/test_backend_bootstrap.py +++ b/tests/selfhost/test_backend_bootstrap.py @@ -11,7 +11,7 @@ COREC = BUILD / "corec" COREARCH = BUILD / "corearch" # 组合根(三轴组合的 target triple 命名)——project-mode 入口 = 该目录 main.cr -BACKEND_SOURCE = BASE / "src" / "targets" / "x86_64-linux-elf" +BACKEND_SOURCE = BASE / "src" / "targets" / "x86_64" / "linux" / "elf" def run_checked(args: list[str], label: str, env: dict[str, str]) -> bool: diff --git a/tools/baseline/parity_run.sh b/tools/baseline/parity_run.sh index db17d66a..97c1cac3 100644 --- a/tools/baseline/parity_run.sh +++ b/tools/baseline/parity_run.sh @@ -57,7 +57,7 @@ run_one() { # $1 tier tag, $2 file echo "=== Tier 3: backend/kernel units ===" run_one t3 src/compiler/ccr_io.cr - for f in src/format/elf/*.cr src/os/linux/*.cr src/arch/x86_64/*.cr src/lattice/*.cr src/targets/x86_64-linux-elf/*.cr; do + for f in src/format/elf/*.cr src/os/linux/*.cr src/arch/x86_64/*.cr src/lattice/*.cr src/targets/x86_64/linux/elf/*.cr; do run_one t3 "$f" done diff --git a/tools/v4_migrate.py b/tools/v4_migrate.py index bea9e0e8..a54205fe 100644 --- a/tools/v4_migrate.py +++ b/tools/v4_migrate.py @@ -38,7 +38,7 @@ # 「七目录面 74 档」会漏掉四棵同样进编译单元的子树 —— `src/lattice`(1) · # `src/lsp`(7) · `src/kernel`(9) · `src/scheduler`(1);逐目录 = compiler 38 · # stdlib 19 · kernel 9 · lsp 7 · arch/x86_64 5 · os/linux 3 · format/elf 3 · -# targets/x86_64-linux-elf 2 · runtime 2 · arch/hit 2 · scheduler 1 · lattice 1)。 +# targets/x86_64/linux/elf 2 · runtime 2 · arch/hit 2 · scheduler 1 · lattice 1)。 # `tests/`·`docs/`·`examples/`·`tools/`·`legacy_asm_backend/` 下的 `.cr` 属 T6,**不动**。 SRC_ROOT = 'src' From 756ca67b73f120f00f2dd3a7caf96508715682ae Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 14:33:42 +0800 Subject: [PATCH 33/58] dex: preserve optional payload declarations Keep the declared dex? row on call results, struct-field reads, and enum match payloads. The existing match lowering only recovered the payload form from irv_decl_ti; without propagation, @raw_int saw a raw scaled integer as a plain int and produced 3 instead of 7. test_opt_dex is now 30/30; BigInt, backend bootstrap, corec2/corec3, and canary remain green. --- src/compiler/ir_gen.cr | 67 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 04b41c09..fc7920cc 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -335,6 +335,23 @@ fn callee_ret_optional(func_ni: int) -> int: return 1 return 0 +// Declared `dex?` return row for a call result. The payload can be bare +// (scaled) or boxed; the declaration row is what later match/inner reads use +// to recover the payload form. -1 = not a dex-optional return. +fn callee_ret_dex_opt_decl(func_ni: int) -> int: + if func_ni < 0: + return -1 + cfi := find_func(func_ni) + if cfi < 0: + return -1 + cfn := fi_ast_node(cfi) + if cfn < 0 || ast_kind(cfn) != EXPR_FN: + return -1 + rtn := ast_type_val(cfn) + if rtn < 0 || dex_opt_type_node(rtn) == 0: + return -1 + return res_type_node(rtn) + // ── 容量批 T2:可选聚合槽的存储边界规范化(写点装箱)── // 法则:可选聚合槽(结构体字段 / 数组·切片元素 / 元组元素 / 枚举携带值 / 全局槽)任何时刻恒持 // 装箱值(Some 对象)或 None;裸 T 值在写点装箱。读侧零改动(既有装箱假定)。 @@ -489,7 +506,10 @@ fn agg_field_read_form(field_node: int, fi: int) -> int: si := find_struct(ast_c(field_node)) if si < 0: return TI_INT - if agg_read_form_is_dex(si_field_type_node(si, fi)) != 0: + ftn := si_field_type_node(si, fi) + if agg_read_form_is_dex(ftn) != 0: + return TI_DEX_S + if dex_opt_type_node(ftn) != 0: return TI_DEX_S return TI_INT @@ -523,6 +543,30 @@ fn agg_payload_read_form(pat_node: int, pos: int) -> int: vi = vi + 1 return TI_INT +// 枚举携带值的声明行(仅 `T?` 且内层 dex;-1 = 不登记)。用于外层 match 的 +// 载荷变量:内层再 match 时,`dex_opt_slot_ti(irv_decl_ti(...))` 才能识别它。 +fn agg_payload_decl_ti(pat_node: int, pos: int) -> int: + if pat_node < 0 || pos < 0: + return -1 + name_ni := ast_a(pat_node) + si := find_gsym(name_ni) + if si < 0: + return -1 + ei := find_enum_row_of(sym_type(si)) + if ei < 0: + return -1 + vi : ., mut = 0 + loop: + if vi >= ei_variant_count(ei): + return -1 + if ei_variant_name(ei, vi) == name_ni: + tn := ei_variant_type_node(ei, vi, pos) + if dex_opt_type_node(tn) != 0: + return res_type_node(tn) + return -1 + vi = vi + 1 + return -1 + // 元素表达式(元组/数组字面量的元素槽无声明信息)的可选性:保守 = 不可选 -> 不装箱 // 覆盖范围 = ident(声明侧表)/ 调用(被调返回类型可选);其余形态登记为未覆盖情形。 fn elem_node_optional(node: int) -> int: @@ -2422,6 +2466,13 @@ fn gen_expr(node: int) -> int: emit_rep_set(g_optrep_arg_cell0 + ai3, rep_enc_of_expr(an3, av3)) ai3 = ai3 + 1 emit(IR_CALL, dest, first_arg_var, ac, func_ni, call_ti) + // `dex?` returns carry a declared optional row, not just TI_INT. Keeping + // the row on the call result lets a later match recover the payload form + // (bare scaled vs boxed Some) without guessing from the IR value width. + if g_optrep_on != 0: + cdd := callee_ret_dex_opt_decl(func_ni) + if cdd >= 0: + irv_set_decl_ti(dest, cdd) // 可选表示:被调返回可选 -> 1) 读返回信道(读点必须紧跟真实 // 调用:调用点与返回点同进程同栈、其间无写入);2) 禁惰性 thunk,thunk 把 // 调用推迟到 force 点,读点即悬空(for example: `v := g()` 的 g 判纯 -> thunk -> 表示位 @@ -2749,8 +2800,16 @@ fn gen_expr(node: int) -> int: // 携带值入 dex 运算会被当 int 再 ×S(`dex_scale_int`)。 fv_ti : ., mut = agg_payload_read_form(arm_pat, fi) if fv_ti == TI_INT && match_val >= 0: - if dex_opt_slot_ti(irv_decl_ti(match_val)) != 0: + if irv_type(match_val) == TI_DEX_S: + fv_ti = TI_DEX_S + else if dex_opt_slot_ti(irv_decl_ti(match_val)) != 0: fv_ti = TI_DEX_S + else if dex_opt_slot_ti(irv_type(match_val)) != 0: + fv_ti = TI_DEX_S + if g_optrep_on != 0 && match_val >= 0: + pdt := agg_payload_decl_ti(arm_pat, fi) + if pdt >= 0: + irv_set_decl_ti(fv, pdt) irv_set_type(fv, fv_ti) if rep_v < 0: emit(IR_LOAD_FIELD, fv, match_val, 0, fi + 1, 0) // +1 for tag offset @@ -2980,6 +3039,10 @@ fn gen_expr(node: int) -> int: // 读点定类型取声明信息形式(dex 声明 -> TI_DEX_S),零 alloc 节点判, // 非 dex 形态恒 TI_INT -> 零足迹。必须在 fi 解出之后(字段位序)。 irv_set_type(v, agg_field_read_form(node, fi)) + if g_optrep_on != 0: + ft := field_ti_of_node(node, fi) + if ft >= 0 && dex_opt_slot_ti(ft) != 0: + irv_set_decl_ti(v, ft) emit(IR_LOAD_FIELD, v, obj_var, 0, fi, 0) return v From a580a42fada41729b54984b14b822d15f48b277f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:10:19 +0800 Subject: [PATCH 34/58] docs: specify target-neutral lattice ccr v10 Define .ccr v10 as a lattice/existence representation, not a linear slot instruction stream. NOD/EDG/ENT/REG are the semantic backbone; file order is a topological projection. Remove width/offset/stride/ABI/home from the authoritative format, keep TYPE as semantic domains, reserve CASE, and leave target layout to corearch/HIT while .coi remains optional optimization knowledge. --- .../2026-10-23-lattice-ccr-v10-design.md | 401 ++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md diff --git a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md new file mode 100644 index 00000000..42488411 --- /dev/null +++ b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md @@ -0,0 +1,401 @@ +# v10 目标无关格形态 `.ccr` 规格 + +## 状态 + +设计定稿,待实现。 + +本规格定义 `.ccr` v10 的语义模型和字节形状。它取代 v9.x 的槽语义,不提供自动迁移:v9.x 文件整类拒绝。 + +## 1. 定位 + +`.ccr` 是格形态的落盘表示,不是线性指令流。 + +```text +.cir 关系空间:图、生成方法、输入边 +.ccr 存在空间:节点、边、条目、区域 +编码 物理投影:HIT 事件、寄存器、栈、ABI、机器码 +``` + +`.ccr` 必须能重建: + +- 图事件和生成方法; +- 数据边和 state 边; +- 变量乘条目的存在版本和区间; +- 区域嵌套和共存关系。 + +文件序只是生产者选择的合法拓扑调度。它不是唯一执行顺序,不是语义主干。消费方可以按边约束重新调度。 + +## 2. 硬约束 + +1. `.ccr` 不包含目标机器事实:位宽、字节偏移、步长、对齐、字节序、寄存器、栈、ABI。 +2. 位置的语义是“条目标识 + 路径”,不是字节地址。 +3. 赋值语义是版本推进:`STORE` 表达 `ρ(target) := ρ(value)`,不是固定宽度内存写。 +4. `TYPE` 是语义域/约束,不是机器类型表。它可以被未来 target 擦除、动态实现或映射为原生域,但 `.ccr` 不假设硬件存在类型。 +5. `.ccr` 不依赖 `.coi`。删除全部 `.coi` 后,`.ccr` 仍可加载、验证、投影和执行。 +6. `.coi` 是非权威优化知识;它不能补语义、不能补布局、不能决定正确性。 +7. target model / DataLayout 是编码输入,不是 `.ccr` 的字段。 +8. 文件格式自身的固定字段宽度、段表偏移和 little-endian 编码属于 wire format,不是程序字宽。 + +## 3. 文件结构 + +```text +Header 16B + magic u32 = 0x31524343 // "CCR1" + version u32 = 0x000A0000 // 10.0 + seg_count u32 = 9 + flags u32 // 仅程序特性,见 3.1 + +Segment table 9 × 12B + tag u32 + offset u32 相对文件头 + size u32 + +Segments + 1 STR + 2 SYM + 3 NOD + 4 ENT + 5 REG + 6 EDG + 7 TYPE + 8 IFACE + 9 CASE +``` + +段表按 tag 升序,段体连续。加载器检查: + +- magic/version 精确匹配; +- `seg_count == 9`; +- 段表 offset/size 在文件内,段体不重叠; +- 段体没有未消费尾随字节; +- 未知、缺失或重复段拒绝。 + +### 3.1 Header flags + +```text +bit 0 PORTABLE 无 raw/FFI/外部 ABI 的程序 +bit 1 SYSTEM 含 unsafe/runtime/ABI 边界的程序 +bit 2 CONCURRENT 含并发/执行策略节点 +bit 3+ 保留,必须为 0 +``` + +flags 不携带 target 身份、字宽、字节序或 ABI。一个 `SYSTEM` 程序仍可以是格形态,但不承诺跨目标运行。 + +## 4. NOD:图事件 + +```text +nod_count u32 +nod_count × 40B + op u32 + dest i32 + src1 i64 + src2 i32 + src3 i32 + type_id u32 + flags u32 + first_edge u32 + edge_count u32 +``` + +规则: + +- `dest` 是值条目的产生变量,`-1` 表示无产生值。 +- `src1` 是源变量、条目、路径、符号或立即数;由 `op` 决定。 +- `src2`/`src3` 是操作辅助索引;**不得表示宽度、字节偏移、步长或对齐**。 +- `type_id` 是 TYPE 段的行索引,表示该节点的语义域;`-1` 表示无类型/不适用。TYPE 项表只服务 IFACE 签名,不进入 NOD 的常规域选择。 +- `flags` 是节点特性,不是目标事实。 +- 出边在 EDG 段连续;节点 i+1 的 `first_edge` 等于节点 i 的 `first_edge` 加 `edge_count`。 + +### 4.1 操作类别 + +操作名称是本规格的语义权威。数值 opcode 由实现侧的 v10 opcode 表分配;分配规则: + +- 每个名称唯一; +- 旧 v9 数值只有在字段语义完全一致时才可复用; +- 任何旧槽语义、宽度或编码选择 opcode 不得复用; +- 加载器按 v10 表解释,不按 v9 表猜测。 + +数值/计算: + +```text +CONST dest, src1=值或 STR 索引, type_id +UNARY dest, src1, src3=op, type_id +BINARY dest, src1, src2, src3=op, type_id +CALL dest, src1=首实参, src2=实参数, src3=符号, type_id=返回域 +RETURN src1=值 +``` + +存在/位置: + +```text +STORE dest=-1, src1=目标条目/路径, src2=值, type_id=值域 +LOAD dest, src1=源条目/路径, type_id=读取域 +ALLOC dest, src1=数量或 0, type_id=分配域 +ALLOC_ARRAY dest, src1=元素数量, type_id=元素域 +ALLOC_STRUCT dest, type_id=结构域 +FIELD dest, src1=基条目/路径, src3=字段号, type_id=字段域 +INDEX dest, src1=基条目/路径, src2=索引, type_id=元素域 +STORE_FIELD src1=基条目, src2=值, src3=字段号, type_id=值域 +STORE_INDEX src1=基条目, src2=索引, src3=值, type_id=值域 +DEREF dest, src1=指针/引用, type_id=指向域 +STORE_PTR src1=指针/引用, src2=值, type_id=值域 +SLICE dest, src1=基条目, src2=低, src3=高, type_id=切片域 +BOUNDS_CHECK src1=索引, src2=数量, type_id=元素域 +``` + +控制/关系: + +```text +BRANCH src1=条件, src2=真目标, src3=假目标 +JUMP src1=目标 +LABEL src1=标签号 +MATCH src1=scrutinee, src2=CASE 表索引, type_id=结果域 +``` + +枚举/动态: + +```text +MAKE_ENUM dest, src1=变体符号, src2=字段数, type_id=枚举域 +LOAD_ENUM_TAG dest, src1=枚举值, type_id=标签域 +DYN_TAG dest, src1=动态值 +DYN_VAL dest, src1=动态值, type_id=目标域 +DYN_PACK dest, src1=值, src2=域 +DYN_DISPATCH dest, src1=接收者, src2=方法符号, type_id=返回域 +``` + +执行/系统边界: + +```text +ARENA_NEW dest, type_id=unit // 容量/实现由 runtime 决定 +ARENA_RESET src1=arena +SPAWN dest, src1=首实参, src2=实参数, src3=函数符号, type_id=返回域 +YIELD src1=值 +AWAIT dest, src1=值, type_id=结果域 +HOTPATCH dest, src1=首实参, src2=实参数, src3=符号, type_id=返回域 +FNADDR dest, src1=函数符号, type_id=地址域 +CALL_EXTERN dest, src1=首实参, src2=实参数, src3=外部符号, type_id=返回域 +INLINE src1=函数符号 +PTR_OF dest, src1=字符串, type_id=地址域 +STR_OF dest, src1=地址, type_id=字符串域 +``` + +执行/系统边界节点要求文件的 `SYSTEM` 标志置 1;它们不承诺跨目标可运行。`PTR_OF`/`STR_OF`/`CALL_EXTERN`/`HOTPATCH`/`ARENA_*` 不得出现在纯 `PORTABLE` 程序中。 + +## 5. ENT:存在主干 + +```text +ent_count u32 +ent_count × 24B + var_id i32 + version u32 + def_nod i32 + live_start u32 + live_end u32 + flags u32 +``` + +- `var_id` 是变量/条目身份;同一变量的不同版本各占一条。 +- `version` 从 1 开始,每次 `STORE` 推进。 +- `def_nod` 是产生该版本的 NOD id;参数和全局为 `-1`。 +- `live_start` 包含,`live_end` 不包含,坐标是 NOD id。 +- `flags`: + ```text + bit 0 无生成方法 + bit 1 参数 + bit 2 全局 + bit 3 移除候选 + bit 4+ 保留,必须为 0 + ``` + +`home` 不再出现在 `.ccr`。寄存器、栈槽、home、分配结果、物理位置属于 `.coi` 或实例侧。需要无生成方法条目的持久绑定时,由 `.coi` 提供候选;`.ccr` 本身只保留存在事实。 + +`STORE` 是 ENT 版本推进的唯一规范点;`STORE_FIELD`、`STORE_INDEX`、`STORE_PTR` 是路径投影,不改变 `var_id` 的存在身份。具体版本选择由数据边和 ENT 区间决定。 + +## 6. REG:区域 + +```text +reg_count u32 +reg_count × 24B + kind i32 // func/loop/if/flow/unsafe 等语义种类 + parent i32 + enter_nod u32 + exit_nod u32 + first_ent u32 + last_ent u32 +``` + +区域坐标是 NOD id;`first_ent/last_ent` 是 ENT 半开索引。区域表达图绑定、存在范围和共存约束,不表达栈帧、字节范围或寄存器。 + +## 7. EDG:边 + +```text +edg_count u32 +edg_count × 8B + to_nod u32 + kind u32 +``` + +- `kind 0`:数据边,def-use 值流。 +- `kind 1`:state 边,副作用顺序。 +- `kind 2+`:保留;v10 不定义语义时拒绝或跳过策略必须由版本级规则固定。 + +`from` 由所属 NOD 的 `first_edge/edge_count` 隐含。数据边和 state 边都是前向边;循环由 REG 和 state 链表达,不用后向边。 + +加载器验证: + +- 边所属节点小于 `to_nod`; +- `edg_count` 等于所有 `edge_count` 之和; +- 文件序是合法拓扑调度;否则拒绝。 + +## 8. STR / SYM + +### 8.1 STR:字符串表 + +```text +str_count u32 +str_count × {len u32, data[len]} +``` + +字符串只保存源码标识、符号名和规范化字面量;不保存目标内存布局或指针宽度。 + +### 8.2 SYM:符号与声明 + +SYM 保存函数、全局、结构体、枚举和外部符号。函数保存参数域和返回域;结构体保存字段名和字段域;全局保存名称和域。SYM 不保存: + +- 参数栈序、寄存器分配、调用约定; +- 字段字节偏移、结构体 size/align; +- 全局地址、段选择、重定位。 + +## 9. TYPE:语义域 + +TYPE 保留现有两小节结构,但语义改为“域约束”,不存机器布局: + +```text +row_count u32 +row_count × 24B {kind, data, extra} + +term_count u32 +term_count × 40B {tag, a, b, c, d} +``` + +- 不存 size、align、offset、stride、endian。 +- `TYP_ARRAY` 的 `extra` 是元素数量,不是字节长度。 +- `TYP_PTR`/`TYP_REF` 只存指向域,不存指针宽度。 +- `TYP_OPTIONAL` 只存内层域,不存 tag 字节布局。 +- `TYP_NAMED` 的结构字段在 SYM 段;字段没有字节偏移。 +- `TYP_FN` 只有参数/返回域,不存 ABI。 + +未来 target 可以没有“类型”这个概念;它在验证后可按自己的原生域擦除或动态表示 TYPE 约束。`corearch` 不得把 TYPE 行直接当机器类型尺寸使用。 + +## 10. IFACE:接口 + +保持五小节结构,但只保存语义接口: + +- 原生条目、形状、接口、impl、方法表; +- `ret_term`/`param_terms` 是 TYPE 项引用; +- 不存调用约定、寄存器分配、参数栈序、返回寄存器。 + +## 11. CASE:match 关系 + +```text +case_count u32 +case_count × 24B + value_i64 i64 + kind u32 // 值/枚举/字符串/默认等语义种类 + str_index i32 + target_nod i32 + flags u32 +``` + +CASE 只表达 scrutinee 域到目标节点的关系。跳转表、比较指令、二分形态和 case 排序属于 corearch 投影,不进入 `.ccr`。 + +## 12. 文件序与投影 + +1. NOD 文件序是生产者选择的合法拓扑调度,沿 EDG 前向边约束。 +2. 加载器可以重建图语义;后端可以按不同 target 重新调度。 +3. `NOD id` 是图坐标。ENT 区间、REG 边界、CASE 目标都使用 NOD id。 +4. 文件序不是程序顺序,不是执行顺序,不是语义主干。 +5. 同源程序的不同合法调度必须产生等价的存在格;后端可以选择不同投影。 + +## 13. 消费与编码 + +```text +corec: HDFG / 关系图 -> 重建 ENT -> 拓扑投影 -> .ccr +corearch: 加载 .ccr -> 重建图/存在格 -> target model -> HIT 事件 -> 编码 +interp: 加载 .ccr -> 使用自己的运行时值表示执行 +.coi: 可选优化知识 -> 候选 -> 后端独立验证 +``` + +HIT 最小核仍是编码层契约: + +```text +sub / nand / load / store +``` + +它们没有位宽、寄存器、寻址。宽度、寄存器、寻址、ABI、字节序属于 HIT 投影、target model 或 runtime。`.ccr` 不复制 HIT 事件,也不把 HIT 事件当格语义。 + +## 14. `.coi` 边界 + +`.ccr` 的字段、段、加载检查和验证不读取 `.coi`。 + +`.coi` 可以保存: + +- 寄存器分配候选; +- 栈/home 候选; +- CSE、布局偏好、向量宽度、调度候选; +- target-specific 优化知识; +- 从 `.cir`/`.ccr` 重算的索引和代价。 + +`.coi` 不能保存: + +- 语义事实; +- 必需的布局; +- 程序正确性条件; +- 目标模型/硬件能力本身; +- 机器码。 + +删除 `.coi` 只允许增加编译成本或降低优化质量;不得改变 `.ccr` 的语义。 + +## 15. 验证规则 + +1. Header、段表、段体边界和版本必须精确。 +2. NOD 的 `type_id` 在 TYPE 范围内;`src*` 按 op 的字段约定验证。 +3. NOD 的 `src2/src3` 不得被解释为字节宽度;遇到旧版宽度语义按损坏拒绝。 +4. ENT 的 `var_id/def_nod/live_start/live_end/flags` 自洽;区间不越 NOD 界。 +5. REG 的 parent/enter/exit/entry 范围自洽;区域嵌套无环。 +6. EDG 前向、邻接连续、计数守恒。 +7. TYPE/IFACE 行表和项表自洽;跨段引用在界内。 +8. CASE 目标和值域引用自洽。 +9. `PORTABLE` 程序不得含 `SYSTEM` 节点或 raw/FFI/ABI 边界。 +10. `.ccr` 加载不读取 `.coi`;`.coi` 缺失不改变 `.ccr` 结构。 + +## 16. 版本与迁移 + +- `.ccr` v10.0;v9.x 整类拒收。 +- `.cir` 缓存版本随格语义/操作码迁移 bump。 +- 实现迁移顺序: + 1. 先落 v10 读写器和图重建检查; + 2. 再改 `ccr_lower`/`ir_gen`,去掉 `mp_*`; + 3. 再改 corearch 的 target layout/HIT 投影; + 4. 再删普通语言 API 的 `w64/r64`; + 5. 最后处理 `.coi` 候选和 canary 重锁。 +- 不允许在 v10 中同时保留 v9 槽语义作为隐式后备。 + +## 17. 判定问句 + +任何新增字段或操作先回答: + +1. 换目标机时,这个字段的语义会变吗?会变,属于 target model/`.coi`,不进入 `.ccr`。 +2. 这个字段是否让位置变成字节地址?是,拒绝。 +3. 这个操作是否依赖固定字宽、对齐、字节序或 ABI?是,放编码层或 `SYSTEM` 边界。 +4. 删除它是否会改变可观测语义?会,属于 `.ccr`;不会但会变慢,属于 `.coi`。 +5. 它是否参与存在/版本/区域/边?是,属于 ENT/REG/EDG/NOD 的格语义。 + +## 18. 关联 + +- 格形态语义:`docs/superpowers/specs/2026-09-09-lattice-ir-v7-carrier-design.md` +- 格层边界:`docs/superpowers/specs/2026-09-09-lattice-encoding-boundary-design.md` +- 字节格式前身:`docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md` +- IR schema:`docs/ir-schema/coreir-schema.md` +- HIT:`docs/superpowers/specs/2026-09-05-hardware-interface-table.md` +- `.coi`:`docs/maintainer/design/coi-optimization-knowledge-sidecar.md` From 0095bfd4fba57817d1aedd5688133f8bc41d1637 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:11:18 +0800 Subject: [PATCH 35/58] docs: describe ccr fields as wire roles Do not spell wire fields as u32/i32. The format needs fixed byte widths, but those are file-format contracts, not language types or target word sizes. Use semantic roles (count, id, imm, flags, ...) plus a separate wire-role table. --- .../2026-10-23-lattice-ccr-v10-design.md | 110 ++++++++++-------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md index 42488411..dcaf242b 100644 --- a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md +++ b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md @@ -38,17 +38,35 @@ ## 3. 文件结构 +`.ccr` 是 little-endian wire format。下面字段只描述 **wire 角色**;宽度是文件格式契约,不是语言类型、不是目标字宽。 + +Wire 角色表: + +```text +角色 宽度 编码 +count 4B 无符号 +kind 4B 无符号 +opcode 4B 无符号 +flags 4B 无符号 +offset 4B 无符号 +size 4B 无符号 +version 4B 无符号 +id 4B 有符号,-1 保留为无值/无效 +imm 8B 有符号 +word 8B 有符号 +``` + ```text Header 16B - magic u32 = 0x31524343 // "CCR1" - version u32 = 0x000A0000 // 10.0 - seg_count u32 = 9 - flags u32 // 仅程序特性,见 3.1 + magic 4B = 0x31524343 // "CCR1" + version version = 0x000A0000 + seg_count count = 9 + flags flags // 仅程序特性,见 3.1 Segment table 9 × 12B - tag u32 - offset u32 相对文件头 - size u32 + tag kind + offset offset 相对文件头 + size size Segments 1 STR @@ -84,17 +102,17 @@ flags 不携带 target 身份、字宽、字节序或 ABI。一个 `SYSTEM` 程 ## 4. NOD:图事件 ```text -nod_count u32 +nod_count count nod_count × 40B - op u32 - dest i32 - src1 i64 - src2 i32 - src3 i32 - type_id u32 - flags u32 - first_edge u32 - edge_count u32 + op opcode + dest id + src1 imm + src2 id + src3 id + type_id id + flags flags + first_edge count + edge_count count ``` 规则: @@ -184,14 +202,14 @@ STR_OF dest, src1=地址, type_id=字符串域 ## 5. ENT:存在主干 ```text -ent_count u32 +ent_count count ent_count × 24B - var_id i32 - version u32 - def_nod i32 - live_start u32 - live_end u32 - flags u32 + var_id id + version version + def_nod id + live_start count + live_end count + flags flags ``` - `var_id` 是变量/条目身份;同一变量的不同版本各占一条。 @@ -214,14 +232,14 @@ ent_count × 24B ## 6. REG:区域 ```text -reg_count u32 +reg_count count reg_count × 24B - kind i32 // func/loop/if/flow/unsafe 等语义种类 - parent i32 - enter_nod u32 - exit_nod u32 - first_ent u32 - last_ent u32 + kind kind // func/loop/if/flow/unsafe 等语义种类 + parent id + enter_nod count + exit_nod count + first_ent count + last_ent count ``` 区域坐标是 NOD id;`first_ent/last_ent` 是 ENT 半开索引。区域表达图绑定、存在范围和共存约束,不表达栈帧、字节范围或寄存器。 @@ -229,10 +247,10 @@ reg_count × 24B ## 7. EDG:边 ```text -edg_count u32 +edg_count count edg_count × 8B - to_nod u32 - kind u32 + to_nod count + kind kind ``` - `kind 0`:数据边,def-use 值流。 @@ -252,8 +270,8 @@ edg_count × 8B ### 8.1 STR:字符串表 ```text -str_count u32 -str_count × {len u32, data[len]} +str_count count +str_count × {len count, data[len]} ``` 字符串只保存源码标识、符号名和规范化字面量;不保存目标内存布局或指针宽度。 @@ -271,11 +289,11 @@ SYM 保存函数、全局、结构体、枚举和外部符号。函数保存参 TYPE 保留现有两小节结构,但语义改为“域约束”,不存机器布局: ```text -row_count u32 -row_count × 24B {kind, data, extra} +row_count count +row_count × 24B {kind word, data word, extra word} -term_count u32 -term_count × 40B {tag, a, b, c, d} +term_count count +term_count × 40B {tag kind, a word, b word, c word, d word} ``` - 不存 size、align、offset、stride、endian。 @@ -298,13 +316,13 @@ term_count × 40B {tag, a, b, c, d} ## 11. CASE:match 关系 ```text -case_count u32 +case_count count case_count × 24B - value_i64 i64 - kind u32 // 值/枚举/字符串/默认等语义种类 - str_index i32 - target_nod i32 - flags u32 + value imm + kind kind // 值/枚举/字符串/默认等语义种类 + str_index id + target_nod id + flags flags ``` CASE 只表达 scrutinee 域到目标节点的关系。跳转表、比较指令、二分形态和 case 排序属于 corearch 投影,不进入 `.ccr`。 From 9d4b85620a24e1f91c3d2b00761625343f95ab39 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:17:26 +0800 Subject: [PATCH 36/58] docs: make ccr v10 streaming-friendly Order the canonical segments as STR, TYPE, IFACE, SYM, NOD, ENT, REG, EDG, CASE so a reader can consume forward with no backward seeks. Add the streaming-reader contract and the zstd/tar .ccrb distribution bundle rules. Raw .ccr remains uncompressed and target-neutral. --- .../2026-10-23-lattice-ccr-v10-design.md | 67 ++++++++++++++++--- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md index dcaf242b..1c32bc39 100644 --- a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md +++ b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md @@ -68,15 +68,15 @@ Segment table 9 × 12B offset offset 相对文件头 size size -Segments +Segments(canonical file order) 1 STR - 2 SYM - 3 NOD - 4 ENT - 5 REG - 6 EDG - 7 TYPE - 8 IFACE + 2 TYPE + 3 IFACE + 4 SYM + 5 NOD + 6 ENT + 7 REG + 8 EDG 9 CASE ``` @@ -99,6 +99,57 @@ bit 3+ 保留,必须为 0 flags 不携带 target 身份、字宽、字节序或 ABI。一个 `SYSTEM` 程序仍可以是格形态,但不承诺跨目标运行。 +### 3.2 流式读取契约 + +v10 的 canonical file order 是为顺序读取设计的: + +```text +STR -> TYPE -> IFACE -> SYM -> NOD -> ENT -> REG -> EDG -> CASE +``` + +依赖方向: + +- STR 只被所有段引用; +- TYPE 在 IFACE/SYM/NOD 前,类型域先就绪; +- IFACE 在 SYM 前; +- SYM 在 NOD 前,函数/全局/结构声明先就绪; +- NOD 在 ENT/REG/EDG/CASE 前,图坐标先就绪; +- ENT 在 REG 前,区域条目范围可校验; +- EDG 在 NOD 后,边可校验; +- CASE 最后,目标 NOD 已全部可见。 + +读取器可以: + +1. 先读 16B header 和 9×12B 段表; +2. 校验 magic/version/seg_count、offset/size 连续、无重叠; +3. 按 canonical order 顺序消费段体; +4. 对固定记录段直接流式解析; +5. 对变长段按 count + length 前缀读取; +6. NOD 控制目标、CASE 目标等前向引用在 NOD/CASE 读完后再解析,不要求文件后向 seek; +7. 语义表可以继续驻留内存;流式指输入路径,不承诺零内存执行。 + +写入器可以为了填写段表先计算各段大小,或使用可 seek 输出;**读取器不得依赖后向 seek**。 + +随机访问模式仍可用段表 offset/size 直接定位;它和流式模式必须产生相同语义。 + +### 3.3 分发 bundle + +标准分发单位是 `.ccrb`: + +```text +.ccrb = Zstandard frame(deterministic tar) + manifest + program.ccr + coi/.coi (0..N,可选) +``` + +- zstd 流式解压;tar 条目按固定顺序、固定元数据; +- manifest 只含 bundle/版本/raw `.ccr` 身份/`.coi` 目标和哈希; +- `.ccr` 保持 raw、固定 wire、目标无关; +- `.coi` 可选、可删、可重算; +- 解压后的 raw `.ccr` 用 3.2 的流式读取契约; +- `corearch` 不要求实现 zstd;由分发/安装层解包。 + ## 4. NOD:图事件 ```text From d50901072802f56434751e7c4b8b0e172e7e8834 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:18:45 +0800 Subject: [PATCH 37/58] docs: use mermaid flowcharts in ccr v10 spec Replace ASCII pipeline arrows with Mermaid flowcharts for the lattice pipeline, canonical segment order, bundle layout, and consumer flow. --- .../2026-10-23-lattice-ccr-v10-design.md | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md index 1c32bc39..d0300df5 100644 --- a/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md +++ b/docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md @@ -25,6 +25,18 @@ 文件序只是生产者选择的合法拓扑调度。它不是唯一执行顺序,不是语义主干。消费方可以按边约束重新调度。 +```mermaid +flowchart LR + CIR[".cir 关系空间"] --> CCR[".ccr 存在空间"] + CIR -. "可选优化知识" .-> COI[".coi"] + CCR --> LOAD["流式加载 / 图重建"] + LOAD --> TARGET["target model / DataLayout"] + COI -. "候选" .-> TARGET + TARGET --> HIT["HIT 事件"] + HIT --> ENC["机器编码"] +``` + + ## 2. 硬约束 1. `.ccr` 不包含目标机器事实:位宽、字节偏移、步长、对齐、字节序、寄存器、栈、ABI。 @@ -103,8 +115,9 @@ flags 不携带 target 身份、字宽、字节序或 ABI。一个 `SYSTEM` 程 v10 的 canonical file order 是为顺序读取设计的: -```text -STR -> TYPE -> IFACE -> SYM -> NOD -> ENT -> REG -> EDG -> CASE +```mermaid +flowchart LR + STR["STR"] --> TYPE["TYPE"] --> IFACE["IFACE"] --> SYM["SYM"] --> NOD["NOD"] --> ENT["ENT"] --> REG["REG"] --> EDG["EDG"] --> CASE["CASE"] ``` 依赖方向: @@ -136,11 +149,12 @@ STR -> TYPE -> IFACE -> SYM -> NOD -> ENT -> REG -> EDG -> CASE 标准分发单位是 `.ccrb`: -```text -.ccrb = Zstandard frame(deterministic tar) - manifest - program.ccr - coi/.coi (0..N,可选) +```mermaid +flowchart TD + BUNDLE[".ccrb zstd frame"] --> TAR["deterministic tar"] + TAR --> MANIFEST["manifest"] + TAR --> CCR["program.ccr raw"] + TAR --> COI["coi/target.coi(可选)"] ``` - zstd 流式解压;tar 条目按固定顺序、固定元数据; @@ -388,11 +402,16 @@ CASE 只表达 scrutinee 域到目标节点的关系。跳转表、比较指令 ## 13. 消费与编码 -```text -corec: HDFG / 关系图 -> 重建 ENT -> 拓扑投影 -> .ccr -corearch: 加载 .ccr -> 重建图/存在格 -> target model -> HIT 事件 -> 编码 -interp: 加载 .ccr -> 使用自己的运行时值表示执行 -.coi: 可选优化知识 -> 候选 -> 后端独立验证 +```mermaid +flowchart TD + HDFG["HDFG / 关系图"] --> ENT["重建 ENT"] + ENT --> CCR[".ccr 存在空间"] + CCR --> LOAD["corearch 加载 / 图重建"] + LOAD --> TARGET["target model / DataLayout"] + COI[".coi 可选"] -. "候选" .-> TARGET + TARGET --> HIT["HIT 事件"] + HIT --> ENC["机器编码"] + CCR --> INTERP["interp 执行"] ``` HIT 最小核仍是编码层契约: From 52c5c1de68aaaa7fe6351fe42217e666821763e5 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:33:55 +0800 Subject: [PATCH 38/58] runtime: replace public w64/r64 with cell accessors Remove the w64 and r64 builtin names from the frontend ABI table and backend dispatch. Internal byte-buffer access now uses cell_store and cell_load, which the compiler and stdlib share as low-level runtime accessors. This is the first step toward removing fixed-width names from the ordinary surface; v10 typed/layout IR migration remains separate. --- src/arch/hit/lower_to_core.cr | 28 +- src/arch/x86_64/corearch.cr | 8 +- src/arch/x86_64/frame.cr | 2 +- src/arch/x86_64/instr.cr | 140 ++++----- src/arch/x86_64/int_rep.cr | 4 +- src/arch/x86_64/regalloc.cr | 222 +++++++------- src/arch/x86_64/x86_frontend_abi.cr | 4 +- src/compiler/globals.cr | 4 +- src/format/elf/elf.cr | 352 +++++++++++------------ src/format/elf/ld.cr | 264 ++++++++--------- src/format/elf/resolve.cr | 20 +- src/kernel/bytes.cr | 20 +- src/kernel/mctt.cr | 6 +- src/kernel/term_io.cr | 14 +- src/lattice/ent_kernel.cr | 140 ++++----- src/lsp/analysis.cr | 142 ++++----- src/lsp/lsp.cr | 14 +- src/os/linux/callseq.cr | 18 +- src/os/linux/entry.cr | 24 +- src/runtime/bigint.cr | 12 +- src/stdlib/arena.cr | 18 +- src/stdlib/chan.cr | 114 ++++---- src/stdlib/cli.cr | 102 +++---- src/stdlib/goroutine.cr | 24 +- src/stdlib/hotpatch.cr | 10 +- src/stdlib/sched.cr | 86 +++--- src/stdlib/scheduler.cr | 40 +-- src/stdlib/variadic.cr | 4 +- src/targets/x86_64/linux/elf/main.cr | 4 +- tests/selfhost/test_bigint_boundaries.py | 10 +- tools/baseline/canary_values.tsv | 18 +- 31 files changed, 936 insertions(+), 932 deletions(-) diff --git a/src/arch/hit/lower_to_core.cr b/src/arch/hit/lower_to_core.cr index 002a57fb..4673d498 100644 --- a/src/arch/hit/lower_to_core.cr +++ b/src/arch/hit/lower_to_core.cr @@ -167,14 +167,14 @@ fn hit_rel_add(kind: int, pos: int, target: int): hit_grow_rel(g_hit_rel_count + 1) slot : ., mut = g_hit_rel_count g_hit_rel_count = slot + 1 - w64(g_hit_rel, slot * 24, kind) - w64(g_hit_rel, slot * 24 + 8, pos) - w64(g_hit_rel, slot * 24 + 16, target) + cell_store(g_hit_rel, slot * 24, kind) + cell_store(g_hit_rel, slot * 24 + 8, pos) + cell_store(g_hit_rel, slot * 24 + 16, target) // Record the emission byte position of an event ordinal before its bytes are written. fn hit_ev_pos_set(ord: int, pos: int): hit_grow_ev_pos(ord + 1) - w64(g_hit_ev_pos, ord * 8, pos) + cell_store(g_hit_ev_pos, ord * 8, pos) if ord + 1 > g_hit_ev_pos_count: g_hit_ev_pos_count = ord + 1 @@ -284,7 +284,7 @@ fn hit_pool_intern(v: int) -> int: loop: if k >= g_hit_pool_count: break - if r64(g_hit_pool, k * 8) == v: + if cell_load(g_hit_pool, k * 8) == v: return k k = k + 1 hit_grow_pool(g_hit_pool_count + 1) @@ -296,15 +296,15 @@ fn hit_pool_intern(v: int) -> int: fn hit_pool_total() -> int: return g_hit_pool_count fn hit_pool_val(k: int) -> int: - return r64(g_hit_pool, k * 8) + return cell_load(g_hit_pool, k * 8) // Record a pool reference at a byte position. elf.cr patches it after rodata is final. fn hit_pool_patch_add(pos: int, k: int): hit_grow_pool_patch(g_hit_pool_patch_count + 1) slot : ., mut = g_hit_pool_patch_count g_hit_pool_patch_count = slot + 1 - w64(g_hit_pool_patch, slot * 16, pos) - w64(g_hit_pool_patch, slot * 16 + 8, k) + cell_store(g_hit_pool_patch, slot * 16, pos) + cell_store(g_hit_pool_patch, slot * 16 + 8, k) // ------------------------------------------------------------------ // Lowering rules: straight-line IR to event stream @@ -398,15 +398,15 @@ fn hit_lower_program() -> int: loop: if fi >= g_ir_func_count: break - if str_eq(istr_get(r64(g_ir_func_name_idx, fi * 8)), "main") != 0: + if str_eq(istr_get(cell_load(g_ir_func_name_idx, fi * 8)), "main") != 0: entry = fi break fi = fi + 1 if entry < 0: return 0 // No main means there is nothing to lower; use the normal path. - ist := r64(g_ir_func_instr_start, entry * 8) - ic := r64(g_ir_func_instr_count, entry * 8) + ist := cell_load(g_ir_func_instr_start, entry * 8) + ic := cell_load(g_ir_func_instr_count, entry * 8) ii : ., mut = 0 loop: if ii >= ic: @@ -703,7 +703,7 @@ fn hit_inject_events(path: string) -> int: loop: if fi >= g_ir_func_count: break - if str_eq(istr_get(r64(g_ir_func_name_idx, fi * 8)), "main") != 0: + if str_eq(istr_get(cell_load(g_ir_func_name_idx, fi * 8)), "main") != 0: entry = fi break fi = fi + 1 @@ -717,8 +717,8 @@ fn hit_inject_events(path: string) -> int: break hit_ev_map_set(mi, 0, 0) mi = mi + 1 - ist := r64(g_ir_func_instr_start, entry * 8) - ic := r64(g_ir_func_instr_count, entry * 8) + ist := cell_load(g_ir_func_instr_start, entry * 8) + ic := cell_load(g_ir_func_instr_count, entry * 8) cur : ., mut = 0 // next unattached line ii : ., mut = 0 loop: diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index 7814c63f..97e78efa 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -484,7 +484,7 @@ fn corearch_main() -> int: if hpi >= g_hit_pool_count: break print(" ") - print_i(r64(g_hit_pool, hpi * 8)) + print_i(cell_load(g_hit_pool, hpi * 8)) hpi = hpi + 1 println("") @@ -569,7 +569,7 @@ fn corearch_main() -> int: loop: if rpi >= g_x86_rip_patch_count: break - ppos := r64(g_x86_rip_patch_pos, rpi * 8) + ppos := cell_load(g_x86_rip_patch_pos, rpi * 8) if ppos >= 176 && ppos - 176 + 4 <= cs: w32(cd, ppos - 176, 0) w8(cd, ppos + 4 - 176, 144) @@ -594,7 +594,7 @@ fn corearch_main() -> int: loop: if ri2 >= g_x86_ext_rel_count: break - println(" " + istr_get(r64(g_x86_ext_rel_name, ri2 * 8))) + println(" " + istr_get(cell_load(g_x86_ext_rel_name, ri2 * 8))) ri2 = ri2 + 1 return 1 // Pure static: write directly; the frontend prepends rt.cr. ELF output @@ -612,7 +612,7 @@ fn corearch_main() -> int: loop: if ri >= g_x86_ext_rel_count: break - fn_name := istr_get(r64(g_x86_ext_rel_name, ri * 8)) + fn_name := istr_get(cell_load(g_x86_ext_rel_name, ri * 8)) ctx_add_plt(fn_name, 0) ri = ri + 1 ctx_set_user_code(cd, cs) diff --git a/src/arch/x86_64/frame.cr b/src/arch/x86_64/frame.cr index 8ea521f3..a80a15d8 100644 --- a/src/arch/x86_64/frame.cr +++ b/src/arch/x86_64/frame.cr @@ -218,7 +218,7 @@ fn pf_epilogue(ss: int, buf: string, pos: int) -> int: loop: if rpi >= g_x86_ret_patch_count: break - jmp_pos := r64(g_x86_ret_patch_pos, rpi * 8) + jmp_pos := cell_load(g_x86_ret_patch_pos, rpi * 8) rel := epi_pos - (jmp_pos + 5) w32(buf, jmp_pos + 1, rel) rpi = rpi + 1 diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 5b3c28a4..917d70dc 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -56,7 +56,7 @@ fn g2_slot(v: int) -> int: return E2_REG_SLOT_BASE + rn // Stack sharing: if this var maps to another, use that var's slot if v >= 0 && str_len(g_stack_map) > v * 8: - mapped := r64(g_stack_map, v * 8) + mapped := cell_load(g_stack_map, v * 8) if mapped >= 0 && mapped != v: v = mapped // Function-relative slot: offset within current function's stack frame @@ -71,15 +71,15 @@ fn g2_str_off(si: int) -> int: loop: if i >= g_x86_str_count: break - if r64(g_x86_str_offs, i * 8) == si: + if cell_load(g_x86_str_offs, i * 8) == si: return o + 8 // +8 for this string's header - o = o + 8 + istr_len(r64(g_x86_str_offs, i * 8)) + 1 + o = o + 8 + istr_len(cell_load(g_x86_str_offs, i * 8)) + 1 if o % 8 != 0: o = o + 8 - (o % 8) i = i + 1 grow_str_offs(g_x86_str_count + 1) - w64(g_x86_str_offs, g_x86_str_count * 8, si) + cell_store(g_x86_str_offs, g_x86_str_count * 8, si) g_x86_str_count = g_x86_str_count + 1 return o + 8 // +8 for this string's header @@ -89,7 +89,7 @@ fn g2_rodata_sz() -> int: loop: if i >= g_x86_str_count: break - o = o + 8 + istr_len(r64(g_x86_str_offs, i * 8)) + 1 + o = o + 8 + istr_len(cell_load(g_x86_str_offs, i * 8)) + 1 if o % 8 != 0: o = o + 8 - (o % 8) i = i + 1 @@ -107,7 +107,7 @@ fn e2_w32(buf: string, pos: int, val: int) -> int: w32(buf, pos, val) return 4 fn e2_w64(buf: string, pos: int, val: int) -> int: - w64(buf, pos, val) + cell_store(buf, pos, val) return 8 // ── Encoding primitives (computed, no magic numbers) ── @@ -468,10 +468,10 @@ fn e2_sd_store(b: string, p: int, o: int) -> int: // ── emit_instr: write one instruction to buffer, return bytes written ── fn e2_load_var(buf: string, pos: int, reg: int, var_idx: int) -> int: - if var_idx >= 0 && r64(g_x86_is_global, var_idx * 8) != 0: + if var_idx >= 0 && cell_load(g_x86_is_global, var_idx * 8) != 0: grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, var_idx) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, var_idx) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 sz := e2_lrb(buf, pos, 0) // mov reg, [r11] — memory load (NOT register copy; e2_ld + e2_rslot would misinterpret) @@ -496,15 +496,15 @@ fn e2_load_var(buf: string, pos: int, reg: int, var_idx: int) -> int: // needed. fn e2_lea_glob(buf: string, pos: int, var_idx: int) -> int: grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, var_idx) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, var_idx) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 return e2_lrb(buf, pos, 0) fn e2_is_glob(var_idx: int) -> int: if var_idx < 0: return 0 - return r64(g_x86_is_global, var_idx * 8) + return cell_load(g_x86_is_global, var_idx * 8) // 读侧 dex(movsd xmm{n}, [槽]):全局 -> RIP 相对(lea r11 + movsd [r11]); // 局部 -> 现 `e2_sd_load`/`e2_sd_load1`/`e2_sd_load_x` 原字节(按 xmmn 分派,n=0/1 与 @@ -607,8 +607,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if g_x86_big_enabled != 0: ro := g2_str_off(s1) grow_rodataref(g_x86_rodataref_count + 1) - w64(g_x86_rodataref_pos, g_x86_rodataref_count * 8, pos + cp) - w64(g_x86_rodataref_ro, g_x86_rodataref_count * 8, ro) + cell_store(g_x86_rodataref_pos, g_x86_rodataref_count * 8, pos + cp) + cell_store(g_x86_rodataref_ro, g_x86_rodataref_count * 8, ro) g_x86_rodataref_count = g_x86_rodataref_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) // r10 = decimal string cp = cp + e2_mov(buf, pos+cp, 7, 10) // rdi = string @@ -634,12 +634,12 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: ro := g2_str_off(s1) // Record for post-emission patching (rodata position from Phase 3) grow_rodataref(g_x86_rodataref_count + 1) - w64(g_x86_rodataref_pos, g_x86_rodataref_count * 8, pos + cp) - w64(g_x86_rodataref_ro, g_x86_rodataref_count * 8, ro) + cell_store(g_x86_rodataref_pos, g_x86_rodataref_count * 8, pos + cp) + cell_store(g_x86_rodataref_ro, g_x86_rodataref_count * 8, ro) g_x86_rodataref_count = g_x86_rodataref_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) // placeholder, patched later cp = cp + e2_st(buf, pos+cp, 10, do2) - else if r64(g_x86_is_global, d * 8) != 0: + else if cell_load(g_x86_is_global, d * 8) != 0: // Global row direct write. g2_slot would return an offset outside the // current frame, so e2_li would write stack garbage. The interpreter // writes g_ir_vals directly for the same IR. Use the same dispatch as @@ -652,8 +652,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w64(buf, pos+cp+2, s1) cp = cp + 10 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, d) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, d) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lrb(buf, pos+cp, 0) // mov [r11], r10 — REX.WRB + 0x89 @@ -1020,7 +1020,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 1 cp = cp + emit_modrm(buf, pos+cp, 0, 2, 4) cp = cp + emit_sib(buf, pos+cp, 0, 6, 7) - else if s3 == g_ni_load64 || s3 == g_ni_load_str_ptr || s3 == g_ni_r64: + else if s3 == g_ni_load64 || s3 == g_ni_load_str_ptr || s3 == g_ni_cell_load: // mov rax, [rdi + rsi] if g_x86_big_enabled != 0: cp = cp + e2_big_sub_rsp(buf, pos+cp, 16) @@ -1067,8 +1067,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // would be registered as a rip_patch with gvi=-1 and SKIPPED by the // patch loop, leaving displacement=0 and causing GPF on dereference. grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) // lea r10, [rip+0] placeholder // mov r10, [r10] — REX.WRB + 0x8B @@ -1095,7 +1095,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w32(buf, pos+cp+1, 1) cp = cp + 5 // mov edi, 1 grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1139,7 +1139,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w8(buf, pos+cp+1, 82) cp = cp + 2 // push r10 grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1178,8 +1178,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 2 // jne copy_loop if d >= 0: cp = cp + e2_store_ret(buf, pos+cp, d) - else if s3 == g_ni_w64: - // w64(buf, pos, val) -> mov [rdi+rsi], rdx + else if s3 == g_ni_cell_store: + // cell_store(buf, pos, val) -> mov [rdi+rsi], rdx // In --big-int pos/val are tagged ints; convert them at the boundary. if g_x86_big_enabled != 0: cp = cp + e2_big_sub_rsp(buf, pos+cp, 32) @@ -1239,8 +1239,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // movabs r10, imm64 + store; imm64 patched in elf.cr Phase 3. do2 := g2_slot(d) grow_fnaddr_patch(g_x86_fnaddr_patch_count + 1) - w64(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_count * 8, pos + cp) - w64(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_count * 8, str_intern("goroutine_entry_wrapper")) + cell_store(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_count * 8, pos + cp) + cell_store(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_count * 8, str_intern("goroutine_entry_wrapper")) g_x86_fnaddr_patch_count = g_x86_fnaddr_patch_count + 1 // movabs r10, imm64: REX.W+B = 0x49, opcode 0xBA (0xB8 + reg 2) e2_w8(buf, pos+cp, 73) @@ -1305,8 +1305,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + e2_big_add_rsp(buf, pos+cp, 48) // Record external relocation grow_ext_rel(g_x86_ext_rel_count + 1) - w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp) - w64(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, name_ni) + cell_store(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp) + cell_store(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, name_ni) g_x86_ext_rel_count = g_x86_ext_rel_count + 1 // Emit call placeholder (E8 + rel32 = 0, patched later) e2_w8(buf, pos+cp, 232) @@ -1325,8 +1325,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: do2 := g2_slot(d) name_ni := s1 grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_ni) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_ni) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1352,8 +1352,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cs_stack_args(buf, pos, cp, fa, ac) // Emit call to function + store result (single-threaded approximation) grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_ni) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_ni) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1373,8 +1373,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: do2 := g2_slot(d) name_ni := s1 grow_fnaddr_patch(g_x86_fnaddr_patch_count + 1) - w64(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_count * 8, pos + cp) - w64(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_count * 8, name_ni) + cell_store(g_x86_fnaddr_patch_pos, g_x86_fnaddr_patch_count * 8, pos + cp) + cell_store(g_x86_fnaddr_patch_name, g_x86_fnaddr_patch_count * 8, name_ni) g_x86_fnaddr_patch_count = g_x86_fnaddr_patch_count + 1 // movabs r10, imm64: REX.W+B = 0x49, opcode 0xBA (0xB8 + reg 2, // REX.B extends to 10 = r10). 0x49 0xBB would encode r11 — the @@ -1418,7 +1418,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if fc > 0: cp = cp + e2_movabs_rdi(buf, pos+cp, fc * 8) grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1432,7 +1432,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if sz > 0: cp = cp + e2_movabs_rdi(buf, pos+cp, sz) grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1443,7 +1443,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // 按 OOM 处理(alloc(-1) -> null),后续访问由 null 陷阱捕获。 cp = cp + e2_movabs_rdi(buf, pos+cp, -1) grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1457,8 +1457,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // Scope actually allocates: create a fresh arena via arena_new(). ni_arena_new := str_intern("arena_new") grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, ni_arena_new) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, ni_arena_new) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1494,8 +1494,8 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 2 ni_arena_reset := str_intern("arena_reset") grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, ni_arena_reset) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, ni_arena_reset) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1505,11 +1505,11 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_LOAD && d >= 0: do2 := g2_slot(d) if s1 >= 0: - isg : ., mut = r64(g_x86_is_global, s1 * 8) + isg : ., mut = cell_load(g_x86_is_global, s1 * 8) if isg != 0: grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) // mov r10, [r10] — REX.WRB + 0x8B @@ -1533,15 +1533,15 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // 立即数(例如 7_000_000 被读成 3_500_000)。 needs_big_box := g_x86_big_enabled != 0 && irv_type(s1) == TI_INT && irv_type(s2) == TI_DEX_S if s1 >= 0: - if r64(g_x86_is_global, s1 * 8) != 0: + if cell_load(g_x86_is_global, s1 * 8) != 0: cp = cp + e2_load_var(buf, pos+cp, 10, s2) if needs_big_box != 0: cp = cp + e2_mov(buf, pos+cp, 7, 10) cp = cp + e2_big_call_name(buf, pos+cp, str_intern("bi_from_word")) cp = cp + e2_mov(buf, pos+cp, 10, 0) grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lrb(buf, pos+cp, 0) // mov [r11], r10 — REX.WRB + 0x89 @@ -1596,13 +1596,13 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_REF && d >= 0: do2 := g2_slot(d) - if s1 >= 0 && r64(g_x86_is_global, s1 * 8) != 0: + if s1 >= 0 && cell_load(g_x86_is_global, s1 * 8) != 0: // Global address: lea r10, [rip+rel32] with a RIP patch, the same // rule as the IR_STORE global branch and e2_load_var. A local row uses // e2_lb with its frame slot. grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) else: @@ -1684,36 +1684,36 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: // Single-pass backpatching: known labels emit immediately, unknown record pending je_rel_pos := pos+cp + 2 cp = cp + e2_je(buf, pos+cp, 0) - if s3 >= 0 && r64(g_label_poses, s3 * 8) >= 0: - target := r64(g_label_poses, s3 * 8) + if s3 >= 0 && cell_load(g_label_poses, s3 * 8) >= 0: + target := cell_load(g_label_poses, s3 * 8) e2_w32(buf, je_rel_pos, target - (je_rel_pos + 4)) else if s3 >= 0: grow_pending(g_pending_count + 1) - w64(g_pending_pos, g_pending_count*8, je_rel_pos) - w64(g_pending_label, g_pending_count*8, s3) + cell_store(g_pending_pos, g_pending_count*8, je_rel_pos) + cell_store(g_pending_label, g_pending_count*8, s3) g_pending_count = g_pending_count + 1 jmp_rel_pos := pos+cp + 1 cp = cp + e2_jmp(buf, pos+cp, 0) - if s2 >= 0 && r64(g_label_poses, s2 * 8) >= 0: - target := r64(g_label_poses, s2 * 8) + if s2 >= 0 && cell_load(g_label_poses, s2 * 8) >= 0: + target := cell_load(g_label_poses, s2 * 8) e2_w32(buf, jmp_rel_pos, target - (jmp_rel_pos + 4)) else if s2 >= 0: grow_pending(g_pending_count + 1) - w64(g_pending_pos, g_pending_count*8, jmp_rel_pos) - w64(g_pending_label, g_pending_count*8, s2) + cell_store(g_pending_pos, g_pending_count*8, jmp_rel_pos) + cell_store(g_pending_label, g_pending_count*8, s2) g_pending_count = g_pending_count + 1 return cp if op == IR_JUMP: jmp_rel_pos := pos+cp + 1 cp = cp + e2_jmp(buf, pos+cp, 0) - if s1 >= 0 && r64(g_label_poses, s1 * 8) >= 0: - target := r64(g_label_poses, s1 * 8) + if s1 >= 0 && cell_load(g_label_poses, s1 * 8) >= 0: + target := cell_load(g_label_poses, s1 * 8) e2_w32(buf, jmp_rel_pos, target - (jmp_rel_pos + 4)) else if s1 >= 0: grow_pending(g_pending_count + 1) - w64(g_pending_pos, g_pending_count*8, jmp_rel_pos) - w64(g_pending_label, g_pending_count*8, s1) + cell_store(g_pending_pos, g_pending_count*8, jmp_rel_pos) + cell_store(g_pending_label, g_pending_count*8, s1) g_pending_count = g_pending_count + 1 return cp @@ -1721,7 +1721,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: li := iri_s1(instr_idx) if li >= 0: grow_label_poses(li + 1) - w64(g_label_poses, li * 8, pos) + cell_store(g_label_poses, li * 8, pos) if li + 1 > g_label_count: g_label_count = li + 1 // Patch all pending forward jumps targeting this label @@ -1729,10 +1729,10 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: loop: if pi >= g_pending_count: break - if r64(g_pending_label, pi * 8) == li: - rp := r64(g_pending_pos, pi * 8) + if cell_load(g_pending_label, pi * 8) == li: + rp := cell_load(g_pending_pos, pi * 8) e2_w32(buf, rp, pos - (rp + 4)) - w64(g_pending_label, pi * 8, -1) + cell_store(g_pending_label, pi * 8, -1) pi = pi + 1 return 0 @@ -1881,7 +1881,7 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: e2_w32(buf, pos+cp+1, alloc_size) cp = cp + 5 // mov edi, size grow_alloc_patch(g_x86_alloc_patch_count + 1) - w64(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) + cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) g_x86_alloc_patch_count = g_x86_alloc_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -2190,7 +2190,7 @@ fn hit_ev_slot_addr_ok(v: int) -> int: return 0 if v >= g_ir_var_count: return 0 - if r64(g_x86_is_global, v * 8) != 0: + if cell_load(g_x86_is_global, v * 8) != 0: return 0 if v < g_current_func_var_start: return 0 diff --git a/src/arch/x86_64/int_rep.cr b/src/arch/x86_64/int_rep.cr index cb48c635..9251a34f 100644 --- a/src/arch/x86_64/int_rep.cr +++ b/src/arch/x86_64/int_rep.cr @@ -99,8 +99,8 @@ fn e2_big_patch_jmp(buf: string, jump_pos: int, target_pos: int): fn e2_big_call_name(buf: string, p: int, name_idx: int) -> int: // E8 rel32 placeholder; resolved by the existing call-patch table in elf.cr. grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, p) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_idx) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, p) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, name_idx) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, p, 232) e2_w32(buf, p + 1, 0) diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index c6af6b6f..b15dba11 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -173,7 +173,7 @@ fn meta_remove_var(var_idx: int): loop: if kk >= OPT_META_STRIDE / 8: break - w64(g_opt_meta, mj * OPT_META_STRIDE + kk * 8, r64(g_opt_meta, so + kk * 8)) + cell_store(g_opt_meta, mj * OPT_META_STRIDE + kk * 8, cell_load(g_opt_meta, so + kk * 8)) kk = kk + 1 mj = mj + 1 g_opt_meta_count = g_opt_meta_count - 1 @@ -276,8 +276,8 @@ fn inject_reg_conflict() -> int: // Otherwise force register 3 and first remove coexisting allocated vars to avoid // a rule 1 side effect. Removed vars have no location and do not enter the sweep. fn try_inject_read_gap(func_i: int) -> int: - vc := r64(g_ir_func_var_count, func_i * 8) - vs := r64(g_ir_func_var_start, func_i * 8) + vc := cell_load(g_ir_func_var_count, func_i * 8) + vs := cell_load(g_ir_func_var_start, func_i * 8) es := entry_start(func_i) ec := entry_count(func_i) lv : ., mut = 0 @@ -403,8 +403,8 @@ fn label_pos_of(id: int, lab_id: string, lab_ps: string, n: int) -> int: loop: if i >= n: break - if r64(lab_id, i * 8) == id: - return r64(lab_ps, i * 8) + if cell_load(lab_id, i * 8) == id: + return cell_load(lab_ps, i * 8) i = i + 1 return -1 @@ -416,20 +416,20 @@ fn alloc_registers(): MAX_REGS : int = 5 // callee-saved 物理寄存器(rbx, r12-r15;x86 枚举号,e2_mov/e2_ld REX 编码) reg_phys : string, mut = alloc(5 * 8) - w64(reg_phys, 0, 3) - w64(reg_phys, 8, 12) - w64(reg_phys, 16, 13) - w64(reg_phys, 24, 14) - w64(reg_phys, 32, 15) + cell_store(reg_phys, 0, 3) + cell_store(reg_phys, 8, 12) + cell_store(reg_phys, 16, 13) + cell_store(reg_phys, 24, 14) + cell_store(reg_phys, 32, 15) fi : ., mut = 0 loop: if fi >= g_ir_func_count: break - ic := r64(g_ir_func_instr_count, fi * 8) - ist := r64(g_ir_func_instr_start, fi * 8) - vc := r64(g_ir_func_var_count, fi * 8) - vs := r64(g_ir_func_var_start, fi * 8) - pc := r64(g_ir_func_param_count, fi * 8) + ic := cell_load(g_ir_func_instr_count, fi * 8) + ist := cell_load(g_ir_func_instr_start, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) + vs := cell_load(g_ir_func_var_start, fi * 8) + pc := cell_load(g_ir_func_param_count, fi * 8) if vc <= 0 || ic <= 0: fi = fi + 1 continue @@ -444,11 +444,11 @@ fn alloc_registers(): loop: if iz >= vc: break - w64(haz_const, iz * 8, 0) - w64(haz_ref, iz * 8, 0) - w64(haz_i2f, iz * 8, 0) - w64(haz_dyn, iz * 8, 0) - w64(haz_dex, iz * 8, 0) + cell_store(haz_const, iz * 8, 0) + cell_store(haz_ref, iz * 8, 0) + cell_store(haz_i2f, iz * 8, 0) + cell_store(haz_dyn, iz * 8, 0) + cell_store(haz_dex, iz * 8, 0) iz = iz + 1 ii : ., mut = 0 loop: @@ -462,26 +462,26 @@ fn alloc_registers(): if d >= vs && d < vs + vc: dl := d - vs if op == IR_CONST: - w64(haz_const, dl * 8, 1) + cell_store(haz_const, dl * 8, 1) if op == IR_DYN_PACK || op == IR_DYN_TAG || op == IR_DYN_VAL || op == IR_DYN_DISPATCH: - w64(haz_dyn, dl * 8, 1) + cell_store(haz_dyn, dl * 8, 1) if op == IR_REF && s1 >= vs && s1 < vs + vc: - w64(haz_ref, (s1 - vs) * 8, 1) + cell_store(haz_ref, (s1 - vs) * 8, 1) if op == IR_I2F && s1 >= vs && s1 < vs + vc: - w64(haz_i2f, (s1 - vs) * 8, 1) + cell_store(haz_i2f, (s1 - vs) * 8, 1) if op == IR_DYN_PACK || op == IR_DYN_TAG || op == IR_DYN_VAL || op == IR_DYN_DISPATCH: if s1 >= vs && s1 < vs + vc: - w64(haz_dyn, (s1 - vs) * 8, 1) + cell_store(haz_dyn, (s1 - vs) * 8, 1) if s2 >= vs && s2 < vs + vc: - w64(haz_dyn, (s2 - vs) * 8, 1) + cell_store(haz_dyn, (s2 - vs) * 8, 1) tk := iri_tk(inst) if tk == TI_DEX || tk == TI_DEX_S: if d >= vs && d < vs + vc: - w64(haz_dex, (d - vs) * 8, 1) + cell_store(haz_dex, (d - vs) * 8, 1) if s1 >= vs && s1 < vs + vc: - w64(haz_dex, (s1 - vs) * 8, 1) + cell_store(haz_dex, (s1 - vs) * 8, 1) if s2 >= vs && s2 < vs + vc: - w64(haz_dex, (s2 - vs) * 8, 1) + cell_store(haz_dex, (s2 - vs) * 8, 1) ii = ii + 1 // ── 阶段 2:回边探测 + 携带值标记(region 生命周期上下文)── @@ -502,8 +502,8 @@ fn alloc_registers(): if ii >= ic: break if iri_op(ist + ii) == IR_LABEL && iri_s1(ist + ii) >= 0: - w64(lab_id, li * 8, iri_s1(ist + ii)) - w64(lab_ps, li * 8, ii) + cell_store(lab_id, li * 8, iri_s1(ist + ii)) + cell_store(lab_ps, li * 8, ii) li = li + 1 ii = ii + 1 // 回边 span 收集(16B {label_pos, branch_pos};上限 = 指令数) @@ -519,21 +519,21 @@ fn alloc_registers(): if op == IR_JUMP: tp := label_pos_of(iri_s1(inst), lab_id, lab_ps, ln) if tp >= 0 && tp < ii: - w64(bt, bn * 16, tp) - w64(bt, bn * 16 + 8, ii) + cell_store(bt, bn * 16, tp) + cell_store(bt, bn * 16 + 8, ii) bn = bn + 1 has_loop = 1 else if op == IR_BRANCH: tp := label_pos_of(iri_s2(inst), lab_id, lab_ps, ln) if tp >= 0 && tp < ii: - w64(bt, bn * 16, tp) - w64(bt, bn * 16 + 8, ii) + cell_store(bt, bn * 16, tp) + cell_store(bt, bn * 16 + 8, ii) bn = bn + 1 has_loop = 1 tp = label_pos_of(iri_s3(inst), lab_id, lab_ps, ln) if tp >= 0 && tp < ii: - w64(bt, bn * 16, tp) - w64(bt, bn * 16 + 8, ii) + cell_store(bt, bn * 16, tp) + cell_store(bt, bn * 16 + 8, ii) bn = bn + 1 has_loop = 1 ii = ii + 1 @@ -542,7 +542,7 @@ fn alloc_registers(): loop: if iz >= vc: break - w64(car, iz * 8, 0) + cell_store(car, iz * 8, 0) iz = iz + 1 if has_loop != 0: // 扫描 span 内的指令。var 的首个引用是读时,值跨回边存活,标记为携带。 @@ -557,14 +557,14 @@ fn alloc_registers(): loop: if bi >= bn: break - t0 := r64(bt, bi * 16) - b0 := r64(bt, bi * 16 + 8) + t0 := cell_load(bt, bi * 16) + b0 := cell_load(bt, bi * 16 + 8) iz = 0 loop: if iz >= vc: break - w64(st, iz * 8, 0) - w64(st_def, iz * 8, -1) + cell_store(st, iz * 8, 0) + cell_store(st_def, iz * 8, -1) iz = iz + 1 pp : ., mut = t0 loop: @@ -577,34 +577,34 @@ fn alloc_registers(): s2 := iri_s2(inst) if d >= vs && d < vs + vc: lvd := d - vs - if r64(st, lvd * 8) == 0: + if cell_load(st, lvd * 8) == 0: // dest 定值同指令自读(s1/s2 == d):读先于写,该读 // 可能取上一轮值,按读首引用(携带)处理 if s1 == d || s2 == d: - w64(st, lvd * 8, 2) - w64(car, lvd * 8, 1) + cell_store(st, lvd * 8, 2) + cell_store(car, lvd * 8, 1) else: - w64(st, lvd * 8, 1) - w64(st_def, lvd * 8, pp) + cell_store(st, lvd * 8, 1) + cell_store(st_def, lvd * 8, pp) if op == IR_STORE && s1 >= vs && s1 < vs + vc: lvs := s1 - vs - if r64(st, lvs * 8) == 0: + if cell_load(st, lvs * 8) == 0: if s2 == s1: - w64(st, lvs * 8, 2) - w64(car, lvs * 8, 1) + cell_store(st, lvs * 8, 2) + cell_store(car, lvs * 8, 1) else: - w64(st, lvs * 8, 1) - w64(st_def, lvs * 8, pp) + cell_store(st, lvs * 8, 1) + cell_store(st_def, lvs * 8, pp) if s1 >= vs && s1 < vs + vc && op != IR_STORE: lv1 := s1 - vs - if r64(st, lv1 * 8) == 0: - w64(st, lv1 * 8, 2) - w64(car, lv1 * 8, 1) + if cell_load(st, lv1 * 8) == 0: + cell_store(st, lv1 * 8, 2) + cell_store(car, lv1 * 8, 1) if s2 >= vs && s2 < vs + vc: lv2 := s2 - vs - if r64(st, lv2 * 8) == 0: - w64(st, lv2 * 8, 2) - w64(car, lv2 * 8, 1) + if cell_load(st, lv2 * 8) == 0: + cell_store(st, lv2 * 8, 2) + cell_store(car, lv2 * 8, 1) pp = pp + 1 // 条件定值差分标记:BRANCH 的前向目标 t 在 (pp, b0] 时,位置 (pp, t) // 可被跳过且循环仍继续。区间 [pp+1, t-1] 加 1,终点 t 减 1;前缀累计 @@ -622,7 +622,7 @@ fn alloc_registers(): loop: if zz >= spn: break - w64(skp, zz * 8, 0) + cell_store(skp, zz * 8, 0) zz = zz + 1 pp = t0 loop: @@ -632,16 +632,16 @@ fn alloc_registers(): t2 := label_pos_of(iri_s2(ist + pp), lab_id, lab_ps, ln) t3 := label_pos_of(iri_s3(ist + pp), lab_id, lab_ps, ln) if t2 > pp && t2 <= b0: - w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1) - w64(skp, (t2 - t0) * 8, r64(skp, (t2 - t0) * 8) - 1) + cell_store(skp, (pp + 1 - t0) * 8, cell_load(skp, (pp + 1 - t0) * 8) + 1) + cell_store(skp, (t2 - t0) * 8, cell_load(skp, (t2 - t0) * 8) - 1) if t3 > pp && t3 <= b0: - w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1) - w64(skp, (t3 - t0) * 8, r64(skp, (t3 - t0) * 8) - 1) + cell_store(skp, (pp + 1 - t0) * 8, cell_load(skp, (pp + 1 - t0) * 8) + 1) + cell_store(skp, (t3 - t0) * 8, cell_load(skp, (t3 - t0) * 8) - 1) else if iri_op(ist + pp) == IR_JUMP: t1 := label_pos_of(iri_s1(ist + pp), lab_id, lab_ps, ln) if t1 > pp && t1 <= b0: - w64(skp, (pp + 1 - t0) * 8, r64(skp, (pp + 1 - t0) * 8) + 1) - w64(skp, (t1 - t0) * 8, r64(skp, (t1 - t0) * 8) - 1) + cell_store(skp, (pp + 1 - t0) * 8, cell_load(skp, (pp + 1 - t0) * 8) + 1) + cell_store(skp, (t1 - t0) * 8, cell_load(skp, (t1 - t0) * 8) - 1) pp = pp + 1 // 前缀累计:skp[p−t0] > 0 ⟺ 位置 p 可被某条件分支跳过 acc : ., mut = 0 @@ -649,11 +649,11 @@ fn alloc_registers(): loop: if pp > b0: break - acc = acc + r64(skp, (pp - t0) * 8) + acc = acc + cell_load(skp, (pp - t0) * 8) if acc != 0: - w64(skp, (pp - t0) * 8, 1) + cell_store(skp, (pp - t0) * 8, 1) else: - w64(skp, (pp - t0) * 8, 0) + cell_store(skp, (pp - t0) * 8, 0) pp = pp + 1 // state 1 表示首引用是定值。若定值点可被跳过,则是条件定值,每轮 // 未必刷新,旧值跨回边存活,标记为携带,窗口扩至整函数。 @@ -661,10 +661,10 @@ fn alloc_registers(): loop: if iz >= vc: break - if r64(st, iz * 8) == 1: - dd := r64(st_def, iz * 8) - if dd >= t0 && r64(skp, (dd - t0) * 8) != 0: - w64(car, iz * 8, 1) + if cell_load(st, iz * 8) == 1: + dd := cell_load(st_def, iz * 8) + if dd >= t0 && cell_load(skp, (dd - t0) * 8) != 0: + cell_store(car, iz * 8, 1) iz = iz + 1 bi = bi + 1 @@ -681,15 +681,15 @@ fn alloc_registers(): loop: if lv >= vc: break - w64(var_reg, lv * 8, -1) - w64(var_end, lv * 8, 0) - w64(var_asg, lv * 8, -1) + cell_store(var_reg, lv * 8, -1) + cell_store(var_end, lv * 8, 0) + cell_store(var_asg, lv * 8, -1) lv = lv + 1 lv = 0 loop: if lv >= MAX_REGS: break - w64(reg_occ, lv * 8, -1) + cell_store(reg_occ, lv * 8, -1) lv = lv + 1 cc : ., mut = 0 lv = 0 @@ -707,7 +707,7 @@ fn alloc_registers(): if ty == TI_DEX || ty == TI_DEX_S || ty == TI_DYN || ty == TI_UNIT: lv = lv + 1 continue - if r64(haz_const, lv * 8) != 0 || r64(haz_ref, lv * 8) != 0 || r64(haz_i2f, lv * 8) != 0 || r64(haz_dyn, lv * 8) != 0 || r64(haz_dex, lv * 8) != 0: + if cell_load(haz_const, lv * 8) != 0 || cell_load(haz_ref, lv * 8) != 0 || cell_load(haz_i2f, lv * 8) != 0 || cell_load(haz_dyn, lv * 8) != 0 || cell_load(haz_dex, lv * 8) != 0: lv = lv + 1 continue st : ., mut = f @@ -715,13 +715,13 @@ fn alloc_registers(): if lv < pc: st = 0 // 参数入口装载,窗口自函数头 - if r64(car, lv * 8) != 0: + if cell_load(car, lv * 8) != 0: st = 0 en = ic - 1 // 携带值,扩至整函数 - w64(cand_st, cc * 8, st) - w64(cand_en, cc * 8, en) - w64(cand_lv, cc * 8, lv) + cell_store(cand_st, cc * 8, st) + cell_store(cand_en, cc * 8, en) + cell_store(cand_lv, cc * 8, lv) cc = cc + 1 lv = lv + 1 // 候选按 (start, lv) 稳定升序(插入排序;起点 = 指令位置,C ≤ vc 小集) @@ -729,25 +729,25 @@ fn alloc_registers(): loop: if ci2 >= cc: break - ks := r64(cand_st, ci2 * 8) - kl := r64(cand_lv, ci2 * 8) - ke := r64(cand_en, ci2 * 8) + ks := cell_load(cand_st, ci2 * 8) + kl := cell_load(cand_lv, ci2 * 8) + ke := cell_load(cand_en, ci2 * 8) jj : ., mut = ci2 loop: if jj <= 0: break pj := jj - 1 - ps := r64(cand_st, pj * 8) - pl := r64(cand_lv, pj * 8) + ps := cell_load(cand_st, pj * 8) + pl := cell_load(cand_lv, pj * 8) if ps < ks || (ps == ks && pl < kl): break - w64(cand_st, jj * 8, ps) - w64(cand_en, jj * 8, r64(cand_en, pj * 8)) - w64(cand_lv, jj * 8, pl) + cell_store(cand_st, jj * 8, ps) + cell_store(cand_en, jj * 8, cell_load(cand_en, pj * 8)) + cell_store(cand_lv, jj * 8, pl) jj = jj - 1 - w64(cand_st, jj * 8, ks) - w64(cand_en, jj * 8, ke) - w64(cand_lv, jj * 8, kl) + cell_store(cand_st, jj * 8, ks) + cell_store(cand_en, jj * 8, ke) + cell_store(cand_lv, jj * 8, kl) ci2 = ci2 + 1 // ── 阶段 4:位置扫描 First Fit(同位置先 free 后分配)── @@ -763,10 +763,10 @@ fn alloc_registers(): loop: if lv2 >= vc: break - rr := r64(var_reg, lv2 * 8) - if rr >= 0 && r64(var_end, lv2 * 8) < p: - w64(var_reg, lv2 * 8, -1) - w64(reg_occ, rr * 8, -1) + rr := cell_load(var_reg, lv2 * 8) + if rr >= 0 && cell_load(var_end, lv2 * 8) < p: + cell_store(var_reg, lv2 * 8, -1) + cell_store(reg_occ, rr * 8, -1) lv2 = lv2 + 1 // 分配起点等于 p 的候选。窗口终点为 p-1 的 var 已先释放,可以复用同一 // 寄存器;终点恰为 p 的 var 仍活跃于 p,不能释放。同指令的共存条目不得 @@ -774,25 +774,25 @@ fn alloc_registers(): loop: if ci >= cc: break - if r64(cand_st, ci * 8) != p: + if cell_load(cand_st, ci * 8) != p: break - lvv := r64(cand_lv, ci * 8) - eend := r64(cand_en, ci * 8) + lvv := cell_load(cand_lv, ci * 8) + eend := cell_load(cand_en, ci * 8) ci = ci + 1 rj : ., mut = 0 tk : ., mut = -1 loop: if rj >= MAX_REGS: break - if r64(reg_occ, rj * 8) < 0: + if cell_load(reg_occ, rj * 8) < 0: tk = rj break rj = rj + 1 if tk >= 0: - w64(var_reg, lvv * 8, tk) - w64(var_end, lvv * 8, eend) - w64(reg_occ, tk * 8, lvv) - w64(var_asg, lvv * 8, r64(reg_phys, tk * 8)) + cell_store(var_reg, lvv * 8, tk) + cell_store(var_end, lvv * 8, eend) + cell_store(reg_occ, tk * 8, lvv) + cell_store(var_asg, lvv * 8, cell_load(reg_phys, tk * 8)) p = p + 1 // ── 阶段 5:终分配写 g_opt_meta ── @@ -805,7 +805,7 @@ fn alloc_registers(): loop: if lv >= vc: break - if r64(var_asg, lv * 8) >= 0: + if cell_load(var_asg, lv * 8) >= 0: rc = rc + 1 lv = lv + 1 if rc > 0: @@ -816,8 +816,8 @@ fn alloc_registers(): loop: if lv >= vc: break - if r64(var_asg, lv * 8) >= 0: - w64(asg_lv, ai * 8, lv) + if cell_load(var_asg, lv * 8) >= 0: + cell_store(asg_lv, ai * 8, lv) ai = ai + 1 lv = lv + 1 pb : ., mut = 0 @@ -851,9 +851,9 @@ fn alloc_registers(): loop: if pi >= n_in: break - lvp := r64(asg_lv, (pb + pi) * 8) + lvp := cell_load(asg_lv, (pb + pi) * 8) vw := vs + lvp - rn := r64(var_asg, lvp * 8) + rn := cell_load(var_asg, lvp * 8) store8(g_opt_meta, eo+di, vw%256) store8(g_opt_meta, eo+di+1, (vw/256)%256) store8(g_opt_meta, eo+di+2, (vw/65536)%256) @@ -879,9 +879,9 @@ fn alloc_registers(): loop: if ai >= rc: break - lvp := r64(asg_lv, ai * 8) + lvp := cell_load(asg_lv, ai * 8) vw := vs + lvp - rn := r64(var_asg, lvp * 8) + rn := cell_load(var_asg, lvp * 8) ej : ., mut = 0 loop: if ej >= ec: diff --git a/src/arch/x86_64/x86_frontend_abi.cr b/src/arch/x86_64/x86_frontend_abi.cr index 2fec6b8a..008b1a4f 100644 --- a/src/arch/x86_64/x86_frontend_abi.cr +++ b/src/arch/x86_64/x86_frontend_abi.cr @@ -9,9 +9,9 @@ fn x86_frontend_builtin_register(): prelude_builtin_add("load8", TI_INT) prelude_builtin_add("store8", TI_INT) prelude_builtin_add("load64", TI_INT) - prelude_builtin_add("r64", TI_INT) + prelude_builtin_add("cell_load", TI_INT) prelude_builtin_add("w32", TI_UNIT) - prelude_builtin_add("w64", TI_UNIT) + prelude_builtin_add("cell_store", TI_UNIT) prelude_builtin_add("_dyncpy", TI_UNIT) prelude_builtin_add("load_str_ptr", TI_STR) prelude_builtin_add("store_str_ptr", TI_INT) diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index ef0cbe07..fe8f2f55 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -417,9 +417,9 @@ g_ni_load64 : int, mut g_ni_load_str_ptr : int, mut g_ni_store_str_ptr : int, mut g_ni_get_arg : int, mut -g_ni_w64 : int, mut +g_ni_cell_store : int, mut g_ni_dyncpy : int, mut -g_ni_r64 : int, mut +g_ni_cell_load : int, mut g_ni_goroutine_wrapper_addr : int, mut // Single-pass backpatching: pending forward jumps g_pending_pos : string, mut // rel32 buffer positions to patch diff --git a/src/format/elf/elf.cr b/src/format/elf/elf.cr index 00b03be5..d9f2adb4 100644 --- a/src/format/elf/elf.cr +++ b/src/format/elf/elf.cr @@ -68,8 +68,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_ca) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_ca) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov r10, [r10]: 4D 8B 12 (modrm 0x12: reg=010+R -> r10, rm=010+B -> [r10]; // old 0x10 had rm=100 which consumed a SIB byte and desynced the stream) @@ -96,8 +96,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int rip_pos3 := pos + cp + 3 cp = cp + e2_lrb(buf, pos + cp, 0) grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_pool_data) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_pool_data) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov r11, [r11]: 4D 8B 1B w8(buf, pos+cp, 77) @@ -112,8 +112,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos4) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_max_size) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos4) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_max_size) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov rax, [rax]: 48 8B 00 (load g_arena_max_size value) w8(buf, pos+cp, 72) @@ -142,8 +142,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos1) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_cursors) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos1) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_cursors) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov rdx, [rdx]: 48 8B 12 w8(buf, pos+cp, 72) @@ -164,8 +164,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_sizes) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_sizes) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov rsi, [rsi]: 48 8B 36 w8(buf, pos+cp, 72) @@ -292,8 +292,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos5) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos5) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov qword [r8], -1: 49 C7 00 FF FF FF FF (set g_current_arena = -1; // REX.W+B: a 32-bit store would leave the slot = 0x00000000ffffffff, @@ -315,8 +315,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_sizes) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_sizes) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_sizes) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_arena_sizes) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov rsi, [rsi]: 48 8B 36 (deref to get sizes array pointer) w8(buf, pos+cp, 72) @@ -374,8 +374,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp0) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp0) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel0 := bss_va - (fva + cp + 7) @@ -411,8 +411,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s1) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s1) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel2 := bss_va - (fva + cp + 7) @@ -447,8 +447,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_he) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_end) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_he) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_end) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: heap_end_va : ., mut = bss_va + 8 @@ -481,8 +481,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_rl) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_rl) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel_hr := bss_va - (fva + cp + 7) @@ -510,8 +510,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s2) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s2) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel3 := bss_va - (fva + cp + 7) @@ -575,8 +575,8 @@ fn emit_alloc_body(buf: string, pos: int, bss_va: int, globals_size: int) -> int e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos6) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos6) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov [r8], r10d: 45 89 10 (restore arena index: r8 = g_current_arena addr) // objdump 验证:45 89 10 = mov %r10d,(%r8) ✓;44 89 00 = mov %r8d,(%rax) ✗ @@ -640,8 +640,8 @@ fn emit_alloc_body_simple(buf: string, pos: int, bss_va: int, globals_size: int) e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_l) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_l) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel0 := bss_va - (fva + cp + 7) @@ -676,8 +676,8 @@ fn emit_alloc_body_simple(buf: string, pos: int, bss_va: int, globals_size: int) e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s1) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s1) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel2 := bss_va - (fva + cp + 7) @@ -704,8 +704,8 @@ fn emit_alloc_body_simple(buf: string, pos: int, bss_va: int, globals_size: int) e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s2) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_s2) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel3 := bss_va - (fva + cp + 7) @@ -822,8 +822,8 @@ fn emit_heap_expand(buf: string, pos: int, bss_va: int) -> int: e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_ex) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_hp_ex) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_ptr) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel_hp_s := bss_va - (fva + cp + 7) @@ -847,8 +847,8 @@ fn emit_heap_expand(buf: string, pos: int, bss_va: int) -> int: e2_w32(buf, pos+cp+3, 0) cp = cp + 7 grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_he_ex) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_end) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_he_ex) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_heap_end) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 else: rel_hp_e := (bss_va + 8) - (fva + cp + 7) @@ -1073,8 +1073,8 @@ fn emit_goroutine_stubs(buf: string, pos: int) -> int: // ret (fiber dead if no work) call_pos0 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos0) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos0) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1096,8 +1096,8 @@ fn emit_goroutine_stubs(buf: string, pos: int) -> int: cp = cp + 1 // push rax call_pos1 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos1) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos1) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1111,8 +1111,8 @@ fn emit_goroutine_stubs(buf: string, pos: int) -> int: cp = cp + 1 // pop rsi call_pos2 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos2) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("chan_send")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos2) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("chan_send")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1120,8 +1120,8 @@ fn emit_goroutine_stubs(buf: string, pos: int) -> int: // exit: call g_get_curg; mov rdi, rax; call g_free; call sched_schedule; ret call_pos3 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos3) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos3) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_get_curg")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1132,16 +1132,16 @@ fn emit_goroutine_stubs(buf: string, pos: int) -> int: cp = cp + 3 // mov rdi, rax call_pos4 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos4) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_free")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos4) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("g_free")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) cp = cp + 5 // call g_free call_pos5 := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos5) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("sched_schedule")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos5) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("sched_schedule")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1174,8 +1174,8 @@ fn emit_m_start_workers(buf: string, pos: int) -> int: cp = cp + 1 // pop rdi call_pos_we := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos_we) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("sched_worker_run")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos_we) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("sched_worker_run")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1239,8 +1239,8 @@ fn emit_m_start_workers(buf: string, pos: int) -> int: cp = cp + 4 call_pos_al := pos + cp grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos_al) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("alloc")) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, call_pos_al) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern("alloc")) g_x86_call_patch_count = g_x86_call_patch_count + 1 e2_w8(buf, pos+cp, 232) e2_w32(buf, pos+cp+1, 0) @@ -1357,8 +1357,8 @@ fn emit_m_start_workers(buf: string, pos: int) -> int: fn sched_reg_one(name: string, offset: int, cp: int): grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern(name)) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern(name)) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) g_x86_func_off_count = g_x86_func_off_count + 1 fn w16(buf: string, off: int, val: int): @@ -1413,12 +1413,12 @@ fn write_phdr(buf: string, idx: int, p_type: int, p_flags: int, base : ., mut = EHDR_SIZE + idx * PHDR_SIZE w32(buf, base + P_TYPE, p_type) w32(buf, base + P_FLAGS, p_flags) - w64(buf, base + P_OFFSET, p_offset) - w64(buf, base + P_VADDR, p_vaddr) - w64(buf, base + P_PADDR, p_paddr) - w64(buf, base + P_FILESZ, p_filesz) - w64(buf, base + P_MEMSZ, p_memsz) - w64(buf, base + P_ALIGN, p_align) + cell_store(buf, base + P_OFFSET, p_offset) + cell_store(buf, base + P_VADDR, p_vaddr) + cell_store(buf, base + P_PADDR, p_paddr) + cell_store(buf, base + P_FILESZ, p_filesz) + cell_store(buf, base + P_MEMSZ, p_memsz) + cell_store(buf, base + P_ALIGN, p_align) // ── ELF header writer (x86-64) ── fn elf2_hdr(buf: string, code_end: int, total_sz: int): @@ -1441,8 +1441,8 @@ fn elf2_hdr(buf: string, code_end: int, total_sz: int): w16(buf, E_TYPE, 2) // ET_EXEC w16(buf, E_MACH, 62) // EM_X86_64 w32(buf, E_VERSION, 1) // EV_CURRENT - w64(buf, E_ENTRY, 0x400000 + EHDR_SIZE + 2 * PHDR_SIZE) // entry = text_base + 176 - w64(buf, E_PHOFF, EHDR_SIZE) // phdrs start right after ehdr + cell_store(buf, E_ENTRY, 0x400000 + EHDR_SIZE + 2 * PHDR_SIZE) // entry = text_base + 176 + cell_store(buf, E_PHOFF, EHDR_SIZE) // phdrs start right after ehdr w16(buf, E_EHSIZE, EHDR_SIZE) // sizeof(Elf64_Ehdr) = 64 w16(buf, E_PHENTSIZE, PHDR_SIZE) // sizeof(Elf64_Phdr) = 56 w16(buf, E_PHNUM, 2) // 2 program headers @@ -1496,10 +1496,10 @@ fn elf_gen(buf: string) -> int: loop: if gi >= g_ir_global_count: break - gv := r64(g_ir_globals, gi * 24 + 8) + gv := cell_load(g_ir_globals, gi * 24 + 8) if gv >= 0: grow_is_global(gv + 1) - w64(g_x86_is_global, gv * 8, 1) + cell_store(g_x86_is_global, gv * 8, 1) gi = gi + 1 grow_is_global(g_ir_var_count) @@ -1519,9 +1519,9 @@ fn elf_gen(buf: string) -> int: g_ni_load_str_ptr = -1 g_ni_store_str_ptr = -1 g_ni_get_arg = -1 - g_ni_w64 = -1 + g_ni_cell_store = -1 g_ni_dyncpy = -1 - g_ni_r64 = -1 + g_ni_cell_load = -1 g_ni_goroutine_wrapper_addr = -1 ni_i : ., mut = 0 loop: @@ -1538,16 +1538,16 @@ fn elf_gen(buf: string) -> int: g_ni_store8 = ni_i if str_eq(ns, "load64") != 0: g_ni_load64 = ni_i - if str_eq(ns, "r64") != 0: - g_ni_r64 = ni_i + if str_eq(ns, "cell_load") != 0: + g_ni_cell_load = ni_i if str_eq(ns, "load_str_ptr") != 0: g_ni_load_str_ptr = ni_i if str_eq(ns, "store_str_ptr") != 0: g_ni_store_str_ptr = ni_i if str_eq(ns, "get_arg") != 0: g_ni_get_arg = ni_i - if str_eq(ns, "w64") != 0: - g_ni_w64 = ni_i + if str_eq(ns, "cell_store") != 0: + g_ni_cell_store = ni_i if str_eq(ns, "_dyncpy") != 0: g_ni_dyncpy = ni_i if str_eq(ns, "goroutine_wrapper_addr") != 0: @@ -1559,7 +1559,7 @@ fn elf_gen(buf: string) -> int: print(" load8=") print(int_str(g_ni_load8)) print(" w64=") - print(int_str(g_ni_w64)) + print(int_str(g_ni_cell_store)) print(" dyncpy=") println(int_str(g_ni_dyncpy)) @@ -1576,7 +1576,7 @@ fn elf_gen(buf: string) -> int: loop: if si >= g_ir_str_const_count: break - g2_str_off(r64(g_ir_str_consts, si * 8)) + g2_str_off(cell_load(g_ir_str_consts, si * 8)) si = si + 1 // Find g_rt_argc/g_rt_argv_ptr via name_idx (no str_eq loop) @@ -1603,29 +1603,29 @@ fn elf_gen(buf: string) -> int: loop: if gvsi >= g_ir_global_count: break - ni := r64(g_ir_globals, gvsi * 24) + ni := cell_load(g_ir_globals, gvsi * 24) if ni == argc_ni: - gv_argc = r64(g_ir_globals, gvsi * 24 + 8) + gv_argc = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == argv_ni: - gv_argv = r64(g_ir_globals, gvsi * 24 + 8) + gv_argv = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni: - gv_current_arena = r64(g_ir_globals, gvsi * 24 + 8) + gv_current_arena = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni2: - gv_arena_cursors = r64(g_ir_globals, gvsi * 24 + 8) + gv_arena_cursors = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni3: - gv_arena_sizes = r64(g_ir_globals, gvsi * 24 + 8) + gv_arena_sizes = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni4: - gv_arena_pool_data = r64(g_ir_globals, gvsi * 24 + 8) + gv_arena_pool_data = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni5: - gv_arena_max_size = r64(g_ir_globals, gvsi * 24 + 8) + gv_arena_max_size = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni6: - gv_heap_ptr = r64(g_ir_globals, gvsi * 24 + 8) + gv_heap_ptr = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni7: - gv_heap_end = r64(g_ir_globals, gvsi * 24 + 8) + gv_heap_end = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni8: - gv_hp_config = r64(g_ir_globals, gvsi * 24 + 8) + gv_hp_config = cell_load(g_ir_globals, gvsi * 24 + 8) if ni == cur_ni9: - gv_hp_inflight = r64(g_ir_globals, gvsi * 24 + 8) + gv_hp_inflight = cell_load(g_ir_globals, gvsi * 24 + 8) gvsi = gvsi + 1 // Phase 2: compute sizes for all functions @@ -1637,8 +1637,8 @@ fn elf_gen(buf: string) -> int: loop: if sfi >= g_ir_func_count: break - ist2 := r64(g_ir_func_instr_start, sfi * 8) - ic2 := r64(g_ir_func_instr_count, sfi * 8) + ist2 := cell_load(g_ir_func_instr_start, sfi * 8) + ic2 := cell_load(g_ir_func_instr_count, sfi * 8) // Count label indices for g_label_poses allocation cur_labels : ., mut = 0 ii2 : ., mut = 0 @@ -1665,7 +1665,7 @@ fn elf_gen(buf: string) -> int: if cur_labels > max_labels: max_labels = cur_labels grow_func_code_sz(sfi + 1) - w64(g_x86_func_code_sz, sfi * 8, ic2 * 5) + cell_store(g_x86_func_code_sz, sfi * 8, ic2 * 5) sfi = sfi + 1 g_label_count = max_labels @@ -1680,17 +1680,17 @@ fn elf_gen(buf: string) -> int: print(int_str(fi)) print("/") println(int_str(g_ir_func_count)) - ni := r64(g_ir_func_name_idx, fi * 8) + ni := cell_load(g_ir_func_name_idx, fi * 8) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, ni) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, ni) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 - vc2 := r64(g_ir_func_var_count, fi * 8) - vs2 := r64(g_ir_func_var_start, fi * 8) - pc2 := r64(g_ir_func_param_count, fi * 8) + vc2 := cell_load(g_ir_func_var_count, fi * 8) + vs2 := cell_load(g_ir_func_var_start, fi * 8) + pc2 := cell_load(g_ir_func_param_count, fi * 8) - fsz := r64(g_x86_func_code_sz, fi * 8) + fsz := cell_load(g_x86_func_code_sz, fi * 8) // Frame size comes from frame.cr. SysV requires 16-byte alignment: after // a call rsp is 8 mod 16. With opt >= 1 there are 6 pushes (rbx, r12-r15, // rbp), so the frame size must be 8 mod 16. With opt < 1 there is 1 push @@ -1729,52 +1729,52 @@ fn elf_gen(buf: string) -> int: if alloc_ni < 0: alloc_ni = str_intern("alloc") grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, alloc_ni) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, alloc_ni) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + 330 // arena-aware dual-path alloc body (~323 bytes with OOM check + zero-init + chain-expand + current-arena check) // heap_expand grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("heap_expand")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("heap_expand")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + 86 // heap_expand with allocator-state save/restore // sched_call trampolines (0..4) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_0")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_0")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + sched_tramp_sz(0) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_1")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_1")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + sched_tramp_sz(1) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_2")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_2")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + sched_tramp_sz(2) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_3")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_3")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + sched_tramp_sz(3) grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_4")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("sched_call_4")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + sched_tramp_sz(4) if g_x86_emit_rt_stubs != 0: // g_set_curg / g_get_curg bridges (11 bytes each): pure-static self-containment grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("g_set_curg")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("g_set_curg")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + 11 grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("g_get_curg")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("g_get_curg")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, total_code) g_x86_func_off_count = g_x86_func_off_count + 1 total_code = total_code + 11 @@ -1807,23 +1807,23 @@ fn elf_gen(buf: string) -> int: print(int_str(fi)) print("/") println(int_str(g_ir_func_count)) - ni := r64(g_ir_func_name_idx, fi * 8) + ni := cell_load(g_ir_func_name_idx, fi * 8) grow_func_cp(fi + 1) - w64(g_x86_func_cp, fi * 8, cp) + cell_store(g_x86_func_cp, fi * 8, cp) // Override with actual position for backward calls fi3 := 0 loop: if fi3 >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, fi3*16)), istr_get(ni)) != 0: - w64(g_x86_func_offsets, fi3*16+8, cp - 176) + if str_eq(istr_get(cell_load(g_x86_func_offsets, fi3*16)), istr_get(ni)) != 0: + cell_store(g_x86_func_offsets, fi3*16+8, cp - 176) break fi3 = fi3 + 1 - ist := r64(g_ir_func_instr_start, fi * 8) - ic := r64(g_ir_func_instr_count, fi * 8) - vc := r64(g_ir_func_var_count, fi * 8) - vs := r64(g_ir_func_var_start, fi * 8) - pc := r64(g_ir_func_param_count, fi * 8) + ist := cell_load(g_ir_func_instr_start, fi * 8) + ic := cell_load(g_ir_func_instr_count, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) + vs := cell_load(g_ir_func_var_start, fi * 8) + pc := cell_load(g_ir_func_param_count, fi * 8) g2_init() g_current_func_var_start = vs @@ -1843,7 +1843,7 @@ fn elf_gen(buf: string) -> int: if li2 >= g_label_count: break grow_label_poses(li2 + 1) - w64(g_label_poses, li2*8, -1) + cell_store(g_label_poses, li2*8, -1) li2 = li2 + 1 g_pending_count = 0 @@ -1893,12 +1893,12 @@ fn elf_gen(buf: string) -> int: loop: if hri >= hri2: break - rk := r64(g_hit_rel, hri * 24) - rp := r64(g_hit_rel, hri * 24 + 8) - rt := r64(g_hit_rel, hri * 24 + 16) + rk := cell_load(g_hit_rel, hri * 24) + rp := cell_load(g_hit_rel, hri * 24 + 8) + rt := cell_load(g_hit_rel, hri * 24 + 16) if rk == 0: if rt >= 0 && rt < g_hit_ev_pos_count: - tp := r64(g_hit_ev_pos, rt * 8) + tp := cell_load(g_hit_ev_pos, rt * 8) w32(buf, rp, tp - (rp + 4)) else: print(" MISSING event position for rel target ordinal ") @@ -1931,7 +1931,7 @@ fn elf_gen(buf: string) -> int: loop: if gsi >= g_ir_global_count: break - gvv := r64(g_ir_globals, gsi * 24 + 8) + gvv := cell_load(g_ir_globals, gsi * 24 + 8) if gvv >= 0 && gvv > max_gv: max_gv = gvv gsi = gsi + 1 @@ -1955,8 +1955,8 @@ fn elf_gen(buf: string) -> int: loop: if hefi >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, hefi*16)), "heap_expand") != 0: - w64(g_x86_func_offsets, hefi*16+8, heap_expand_start - 176) + if str_eq(istr_get(cell_load(g_x86_func_offsets, hefi*16)), "heap_expand") != 0: + cell_store(g_x86_func_offsets, hefi*16+8, heap_expand_start - 176) break hefi = hefi + 1 @@ -1965,8 +1965,8 @@ fn elf_gen(buf: string) -> int: loop: if afi2 >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, afi2*16)), istr_get(alloc_ni)) != 0: - w64(g_x86_func_offsets, afi2*16+8, alloc_start - 176) + if str_eq(istr_get(cell_load(g_x86_func_offsets, afi2*16)), istr_get(alloc_ni)) != 0: + cell_store(g_x86_func_offsets, afi2*16+8, alloc_start - 176) break afi2 = afi2 + 1 @@ -1978,7 +1978,7 @@ fn elf_gen(buf: string) -> int: loop: if api >= g_x86_alloc_patch_count: break - call_pos := r64(g_x86_alloc_patch_pos, api * 8) + call_pos := cell_load(g_x86_alloc_patch_pos, api * 8) rel := (176 + alloc_code_off) - (call_pos + 5) w32(buf, call_pos + 1, rel) api = api + 1 @@ -1988,15 +1988,15 @@ fn elf_gen(buf: string) -> int: // These are only used when the functions aren't compiled from user code. // If the user imports arena, the compiled versions in g_ir_func_name_idx take priority. grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("arena_new")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("arena_new")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) g_x86_func_off_count = g_x86_func_off_count + 1 w8(buf, cp, 195) cp = cp + 1 // ret grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("arena_reset")) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern("arena_reset")) + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, cp - 176) g_x86_func_off_count = g_x86_func_off_count + 1 w8(buf, cp, 195) cp = cp + 1 // ret @@ -2023,11 +2023,11 @@ fn elf_gen(buf: string) -> int: loop: if gsi2 >= g_x86_func_off_count: break - nm2 := istr_get(r64(g_x86_func_offsets, gsi2*16)) + nm2 := istr_get(cell_load(g_x86_func_offsets, gsi2*16)) if str_eq(nm2, "g_set_curg") != 0: - w64(g_x86_func_offsets, gsi2*16+8, g_curg_stub_start - 176) + cell_store(g_x86_func_offsets, gsi2*16+8, g_curg_stub_start - 176) if str_eq(nm2, "g_get_curg") != 0: - w64(g_x86_func_offsets, gsi2*16+8, g_curg_stub_start - 176 + 11) + cell_store(g_x86_func_offsets, gsi2*16+8, g_curg_stub_start - 176 + 11) gsi2 = gsi2 + 1 // ── Goroutine runtime stubs (fiber_init / fiber_switch / goroutine_entry_wrapper) ── @@ -2074,9 +2074,9 @@ fn elf_gen(buf: string) -> int: sym_off := bigint_blob_symbol(sym_name) if sym_off >= 0: grow_func_offsets(g_x86_func_off_count * 2 + 2) - w64(g_x86_func_offsets, g_x86_func_off_count * 16, + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16, str_intern(sym_name)) - w64(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, + cell_store(g_x86_func_offsets, g_x86_func_off_count * 16 + 8, blob_start + sym_off - 176) g_x86_func_off_count = g_x86_func_off_count + 1 sci = sci + 1 @@ -2100,9 +2100,9 @@ fn elf_gen(buf: string) -> int: loop: if exi >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, exi * 16)), + if str_eq(istr_get(cell_load(g_x86_func_offsets, exi * 16)), "bi_to_word_checked") != 0: - ex_off := r64(g_x86_func_offsets, exi * 16 + 8) + ex_off := cell_load(g_x86_func_offsets, exi * 16 + 8) ex_target := 176 + ex_off w32(buf, g_x86_big_exit_call_pos + 1, ex_target - (g_x86_big_exit_call_pos + 5)) @@ -2116,15 +2116,15 @@ fn elf_gen(buf: string) -> int: loop: if cpi >= g_x86_call_patch_count: break - call_pos := r64(g_x86_call_patch_pos, cpi * 8) - fn_ni := r64(g_x86_call_patch_name, cpi * 8) + call_pos := cell_load(g_x86_call_patch_pos, cpi * 8) + fn_ni := cell_load(g_x86_call_patch_name, cpi * 8) cfi2 : ., mut = 0 loop: if cfi2 >= g_ir_func_count: break - name_at := r64(g_ir_func_name_idx, cfi2 * 8) + name_at := cell_load(g_ir_func_name_idx, cfi2 * 8) if str_eq(istr_get(name_at), istr_get(fn_ni)) != 0: - func_cp := r64(g_x86_func_cp, cfi2 * 8) + func_cp := cell_load(g_x86_func_cp, cfi2 * 8) if func_cp > 0: rel := func_cp - (call_pos + 5) w32(buf, call_pos + 1, rel) @@ -2136,8 +2136,8 @@ fn elf_gen(buf: string) -> int: loop: if bfi2 >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, bfi2*16)), istr_get(fn_ni)) != 0: - func_off := r64(g_x86_func_offsets, bfi2*16+8) + if str_eq(istr_get(cell_load(g_x86_func_offsets, bfi2*16)), istr_get(fn_ni)) != 0: + func_off := cell_load(g_x86_func_offsets, bfi2*16+8) // Safety check: target must be within emitted code target_pos := 176 + func_off if target_pos > 0 && target_pos < cp: @@ -2156,16 +2156,16 @@ fn elf_gen(buf: string) -> int: loop: if fpi >= g_x86_fnaddr_patch_count: break - fp_pos := r64(g_x86_fnaddr_patch_pos, fpi * 8) - fn_ni := r64(g_x86_fnaddr_patch_name, fpi * 8) + fp_pos := cell_load(g_x86_fnaddr_patch_pos, fpi * 8) + fn_ni := cell_load(g_x86_fnaddr_patch_name, fpi * 8) fn_va : ., mut = 0 fcfi : ., mut = 0 loop: if fcfi >= g_ir_func_count: break - name_at := r64(g_ir_func_name_idx, fcfi * 8) + name_at := cell_load(g_ir_func_name_idx, fcfi * 8) if str_eq(istr_get(name_at), istr_get(fn_ni)) != 0: - func_cp := r64(g_x86_func_cp, fcfi * 8) + func_cp := cell_load(g_x86_func_cp, fcfi * 8) if func_cp > 0: fn_va = TEXT_BASE + func_cp break @@ -2176,15 +2176,15 @@ fn elf_gen(buf: string) -> int: loop: if bfi3 >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, bfi3*16)), istr_get(fn_ni)) != 0: - func_off := r64(g_x86_func_offsets, bfi3*16+8) + if str_eq(istr_get(cell_load(g_x86_func_offsets, bfi3*16)), istr_get(fn_ni)) != 0: + func_off := cell_load(g_x86_func_offsets, bfi3*16+8) target_pos := 176 + func_off if target_pos > 0 && target_pos < cp: fn_va = TEXT_BASE + target_pos break bfi3 = bfi3 + 1 if fn_va > 0: - w64(buf, fp_pos + 2, fn_va) + cell_store(buf, fp_pos + 2, fn_va) // imm64 placeholder starts after the 49 BB opcode fpi = fpi + 1 g_x86_fnaddr_patch_count = 0 @@ -2206,8 +2206,8 @@ fn elf_gen(buf: string) -> int: loop: if rri >= g_x86_rodataref_count: break - lea_pos := r64(g_x86_rodataref_pos, rri * 8) - ro_off := r64(g_x86_rodataref_ro, rri * 8) + lea_pos := cell_load(g_x86_rodataref_pos, rri * 8) + ro_off := cell_load(g_x86_rodataref_ro, rri * 8) rel := g_x86_rodata_base + ro_off - (lea_pos + 7) w32(buf, lea_pos + 3, rel) rri = rri + 1 @@ -2218,10 +2218,10 @@ fn elf_gen(buf: string) -> int: loop: if si >= g_x86_str_count: break - s := istr_get(r64(g_x86_str_offs, si * 8)) + s := istr_get(cell_load(g_x86_str_offs, si * 8)) sl := str_len(s) // Write 8-byte length header (sl + null) so str_len via load64(s,-8) works - w64(buf, cp, sl + 1) + cell_store(buf, cp, sl + 1) cp = cp + 8 ci := 0 loop: @@ -2246,8 +2246,8 @@ fn elf_gen(buf: string) -> int: loop: if hpr >= g_hit_pool_patch_count: break - pp := r64(g_hit_pool_patch, hpr * 16) - pk := r64(g_hit_pool_patch, hpr * 16 + 8) + pp := cell_load(g_hit_pool_patch, hpr * 16) + pk := cell_load(g_hit_pool_patch, hpr * 16 + 8) rel := cp + pk * 8 - (pp + 7) w32(buf, pp + 3, rel) hpr = hpr + 1 @@ -2256,7 +2256,7 @@ fn elf_gen(buf: string) -> int: loop: if hpk >= g_hit_pool_count: break - w64(buf, cp + hpk * 8, r64(g_hit_pool, hpk * 8)) + cell_store(buf, cp + hpk * 8, cell_load(g_hit_pool, hpk * 8)) hpk = hpk + 1 cp = cp + g_hit_pool_count * 8 @@ -2290,10 +2290,10 @@ fn elf_gen(buf: string) -> int: loop: if gi2 >= g_ir_global_count: break - gv2 := r64(g_ir_globals, gi2 * 24 + 8) + gv2 := cell_load(g_ir_globals, gi2 * 24 + 8) if gv2 >= 0: grow_global_off(gv2 + 1) - w64(g_x86_global_off, gv2 * 8, goff) + cell_store(g_x86_global_off, gv2 * 8, goff) goff = goff + 8 gi2 = gi2 + 1 @@ -2302,8 +2302,8 @@ fn elf_gen(buf: string) -> int: loop: if rpi2 >= g_x86_rip_patch_count: break - ppos := r64(g_x86_rip_patch_pos, rpi2 * 8) - gvi := r64(g_x86_rip_patch_globals, rpi2 * 8) + ppos := cell_load(g_x86_rip_patch_pos, rpi2 * 8) + gvi := cell_load(g_x86_rip_patch_globals, rpi2 * 8) // Verify: buffer at ppos-3 should contain LEA prefix (0x4C or 0x4D for REX.WR/WRB) lea_check := bu8(buf, ppos - 3) if lea_check != 72 && lea_check != 73 && lea_check != 76 && lea_check != 77 && lea_check != 74 && lea_check != 78 && lea_check != 79 && lea_check != 75: @@ -2317,7 +2317,7 @@ fn elf_gen(buf: string) -> int: println(int_str(lea_check)) if gvi >= 0: lea_end_va := TEXT_BASE + ppos + 4 - off := r64(g_x86_global_off, gvi * 8) + off := cell_load(g_x86_global_off, gvi * 8) target_va := bss_va + 16 + off rel := target_va - lea_end_va w32(buf, ppos, rel) @@ -2348,8 +2348,8 @@ fn elf_gen(buf: string) -> int: loop: if bfi3 >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, bfi3*16)), "main") != 0: - mo = r64(g_x86_func_offsets, bfi3*16+8) + if str_eq(istr_get(cell_load(g_x86_func_offsets, bfi3*16)), "main") != 0: + mo = cell_load(g_x86_func_offsets, bfi3*16+8) break bfi3 = bfi3 + 1 // Debug: find ALL entries named "main" @@ -2357,9 +2357,9 @@ fn elf_gen(buf: string) -> int: loop: if bfi4 >= g_x86_func_off_count: break - nm := istr_get(r64(g_x86_func_offsets, bfi4*16)) + nm := istr_get(cell_load(g_x86_func_offsets, bfi4*16)) if str_eq(nm, "main") != 0: - off := r64(g_x86_func_offsets, bfi4*16+8) + off := cell_load(g_x86_func_offsets, bfi4*16+8) print(" main at [") print(int_str(bfi4)) print("] off=") @@ -2418,11 +2418,11 @@ fn elf_gen(buf: string) -> int: // user code itself remains the only executable content. w32(buf, EHDR_SIZE + 4, 7) // Override entry point to bss_init (which zeroes heap then jumps to _start) - w64(buf, E_ENTRY, TEXT_BASE + bss_init_cp) + cell_store(buf, E_ENTRY, TEXT_BASE + bss_init_cp) // Patch data segment VA to match bss_va (page after code+rodata) data_phdr_base := EHDR_SIZE + PHDR_SIZE - w64(buf, data_phdr_base + P_OFFSET, bss_va - TEXT_BASE) - w64(buf, data_phdr_base + P_VADDR, bss_va) - w64(buf, data_phdr_base + P_PADDR, bss_va) + cell_store(buf, data_phdr_base + P_OFFSET, bss_va - TEXT_BASE) + cell_store(buf, data_phdr_base + P_VADDR, bss_va) + cell_store(buf, data_phdr_base + P_PADDR, bss_va) g_asm_code_size = total_sz return total_sz diff --git a/src/format/elf/ld.cr b/src/format/elf/ld.cr index 5fc66e41..fb8cc27b 100644 --- a/src/format/elf/ld.cr +++ b/src/format/elf/ld.cr @@ -152,10 +152,10 @@ fn grow_chunks(needed: int): fn cnew(k: int): grow_chunks(g_ch_count + 1) - w64(g_ch_kind, g_ch_count * 8, k) - w64(g_ch_vaddr, g_ch_count * 8, 0) - w64(g_ch_foff, g_ch_count * 8, 0) - w64(g_ch_size, g_ch_count * 8, 0) + cell_store(g_ch_kind, g_ch_count * 8, k) + cell_store(g_ch_vaddr, g_ch_count * 8, 0) + cell_store(g_ch_foff, g_ch_count * 8, 0) + cell_store(g_ch_size, g_ch_count * 8, 0) g_ch_count = g_ch_count + 1 fn cby(k: int) -> int: @@ -163,7 +163,7 @@ fn cby(k: int) -> int: loop: if i >= g_ch_count: break - if r64(g_ch_kind, i * 8) == k: + if cell_load(g_ch_kind, i * 8) == k: return i i = i + 1 return -1 @@ -178,7 +178,7 @@ fn so_find(buf: string, name: string) -> int: return -1 if read_u16(buf, 16) != 3: return -1 - e_shoff := r64(buf, 40) + e_shoff := cell_load(buf, 40) e_shnum := read_u16(buf, 60) do : ., mut = 0 ds : ., mut = 0 @@ -189,9 +189,9 @@ fn so_find(buf: string, name: string) -> int: if i >= e_shnum: break t := r32(buf, e_shoff+i*64+4) - a := r64(buf, e_shoff+i*64+16) - o := r64(buf, e_shoff+i*64+24) - s := r64(buf, e_shoff+i*64+32) + a := cell_load(buf, e_shoff+i*64+16) + o := cell_load(buf, e_shoff+i*64+24) + s := cell_load(buf, e_shoff+i*64+32) if t == 11: do = o ds = s @@ -207,7 +207,7 @@ fn so_find(buf: string, name: string) -> int: if i >= sc: break sn := r32(buf, do+i*24) - sv := r64(buf, do+i*24+8) + sv := cell_load(buf, do+i*24+8) si := bu8(buf, do+i*24+4) sx := read_u16(buf, do+i*24+6) if si/16 == 1 && si%16 == 2 && sx != 0: @@ -233,11 +233,11 @@ fn so_find_any(name: string) -> int: loop: if si >= g_so_count: break - if istr_len(r64(g_so_paths, si * 8)) > 0: + if istr_len(cell_load(g_so_paths, si * 8)) > 0: // Raw word to string view: the table stores a string pointer (the @ptr_of write side of ctx_add_so), // 读回时必须显式还原视图(2(a) 范式;`@str_of` 必须在 `unsafe` 内)。 so_path_str := unsafe: - @str_of(r64(g_so_paths, si * 8)) + @str_of(cell_load(g_so_paths, si * 8)) b := read_file(so_path_str) if str_len(b) > 0: a := so_find(b, name) @@ -257,7 +257,7 @@ fn layout(): if i >= g_ch_count: break sz : ., mut = 0 - k := r64(g_ch_kind, i * 8) + k := cell_load(g_ch_kind, i * 8) if k == 0: sz = 344 if k == 1: @@ -278,13 +278,13 @@ fn layout(): loop: if j >= g_plt_count: break - sz = sz + istr_len(r64(g_plts, j * 16)) + 1 + sz = sz + istr_len(cell_load(g_plts, j * 16)) + 1 j = j + 1 if k == 8: sz = g_plt_count * 24 - w64(g_ch_vaddr, i * 8, va) - w64(g_ch_foff, i * 8, va - g_text_base) - w64(g_ch_size, i * 8, sz) + cell_store(g_ch_vaddr, i * 8, va) + cell_store(g_ch_foff, i * 8, va - g_text_base) + cell_store(g_ch_size, i * 8, sz) va = va + sz i = i + 1 @@ -308,8 +308,8 @@ fn emit(buf: string, total: int, is_so: int): loop: if i >= g_ch_count: break - p : ., mut = r64(g_ch_foff, i * 8) - k := r64(g_ch_kind, i * 8) + p : ., mut = cell_load(g_ch_foff, i * 8) + k := cell_load(g_ch_kind, i * 8) // Ehdr: ELF header + 4 program headers @@ -328,63 +328,63 @@ fn emit(buf: string, total: int, is_so: int): w16(buf,18,62) w32(buf,20,1) uv := cby(2) - ev := r64(g_ch_vaddr, uv * 8) - w64(buf,24,ev) - w64(buf,32,64) - w64(buf,40,0) + ev := cell_load(g_ch_vaddr, uv * 8) + cell_store(buf,24,ev) + cell_store(buf,32,64) + cell_store(buf,40,0) w16(buf,52,64) w16(buf,54,56) w16(buf,56,5) w16(buf,58,64) - pe := r64(g_ch_size, i * 8) // size of this chunk (EHDR+PHDRs) + pe := cell_load(g_ch_size, i * 8) // size of this chunk (EHDR+PHDRs) // PT_PHDR w32(buf,64,6) w32(buf,68,4) - w64(buf,72,64) - w64(buf,80,tb+64) - w64(buf,88,tb+64) - w64(buf,96,pe-64) - w64(buf,104,pe-64) - w64(buf,112,8) + cell_store(buf,72,64) + cell_store(buf,80,tb+64) + cell_store(buf,88,tb+64) + cell_store(buf,96,pe-64) + cell_store(buf,104,pe-64) + cell_store(buf,112,8) // PT_INTERP ic := cby(1) w32(buf,120,3) w32(buf,124,4) - w64(buf,128,r64(g_ch_foff, ic * 8)) - w64(buf,136,r64(g_ch_vaddr, ic * 8)) - w64(buf,144,r64(g_ch_vaddr, ic * 8)) - w64(buf,152,r64(g_ch_size, ic * 8)) - w64(buf,160,r64(g_ch_size, ic * 8)) - w64(buf,168,1) + cell_store(buf,128,cell_load(g_ch_foff, ic * 8)) + cell_store(buf,136,cell_load(g_ch_vaddr, ic * 8)) + cell_store(buf,144,cell_load(g_ch_vaddr, ic * 8)) + cell_store(buf,152,cell_load(g_ch_size, ic * 8)) + cell_store(buf,160,cell_load(g_ch_size, ic * 8)) + cell_store(buf,168,1) // PT_LOAD (RX) - fs := r64(g_ch_vaddr, (g_ch_count - 1) * 8) + r64(g_ch_size, (g_ch_count - 1) * 8) - tb + fs := cell_load(g_ch_vaddr, (g_ch_count - 1) * 8) + cell_load(g_ch_size, (g_ch_count - 1) * 8) - tb w32(buf,176,1) w32(buf,180,7) - w64(buf,184,0) - w64(buf,192,tb) - w64(buf,200,tb) - w64(buf,208,fs) - w64(buf,216,fs) - w64(buf,224,4096) + cell_store(buf,184,0) + cell_store(buf,192,tb) + cell_store(buf,200,tb) + cell_store(buf,208,fs) + cell_store(buf,216,fs) + cell_store(buf,224,4096) // PT_GNU_STACK w32(buf,232,1685382481) w32(buf,236,6) - w64(buf,240,0) - w64(buf,248,0) - w64(buf,256,0) - w64(buf,264,0) - w64(buf,272,0) - w64(buf,280,8) + cell_store(buf,240,0) + cell_store(buf,248,0) + cell_store(buf,256,0) + cell_store(buf,264,0) + cell_store(buf,272,0) + cell_store(buf,280,8) // PT_DYNAMIC dc := cby(5) w32(buf,288,2) w32(buf,292,4) - w64(buf,296,r64(g_ch_foff, dc * 8)) - w64(buf,304,r64(g_ch_vaddr, dc * 8)) - w64(buf,312,r64(g_ch_vaddr, dc * 8)) - w64(buf,320,r64(g_ch_size, dc * 8)) - w64(buf,328,r64(g_ch_size, dc * 8)) - w64(buf,336,8) + cell_store(buf,296,cell_load(g_ch_foff, dc * 8)) + cell_store(buf,304,cell_load(g_ch_vaddr, dc * 8)) + cell_store(buf,312,cell_load(g_ch_vaddr, dc * 8)) + cell_store(buf,320,cell_load(g_ch_size, dc * 8)) + cell_store(buf,328,cell_load(g_ch_size, dc * 8)) + cell_store(buf,336,8) // Interp if k == 1: @@ -408,8 +408,8 @@ fn emit(buf: string, total: int, is_so: int): // PLT if k == 3: - gv := r64(g_ch_vaddr, cby(4) * 8) - pv := r64(g_ch_vaddr, i * 8) + gv := cell_load(g_ch_vaddr, cby(4) * 8) + pv := cell_load(g_ch_vaddr, i * 8) r1 := (gv+8)-(pv+6) w8(buf,p,255) w8(buf,p+1,53) @@ -445,16 +445,16 @@ fn emit(buf: string, total: int, is_so: int): // GOT.PLT if k == 4: - dv := r64(g_ch_vaddr, cby(5) * 8) - w64(buf,p,dv) - w64(buf,p+8,0) - w64(buf,p+16,0) + dv := cell_load(g_ch_vaddr, cby(5) * 8) + cell_store(buf,p,dv) + cell_store(buf,p+8,0) + cell_store(buf,p+16,0) pi : ., mut = 0 loop: if pi >= g_plt_count: break - pv2 := r64(g_ch_vaddr, cby(3) * 8) - w64(buf,p+24+pi*8,pv2+16+pi*16+6) + pv2 := cell_load(g_ch_vaddr, cby(3) * 8) + cell_store(buf,p+24+pi*8,pv2+16+pi*16+6) pi=pi+1 // .dynamic (also writes .dynstr) @@ -465,7 +465,7 @@ fn emit(buf: string, total: int, is_so: int): loop: if dnp >= g_plt_count: break - dn = dn + istr_len(r64(g_plts, dnp * 16)) + 1 + dn = dn + istr_len(cell_load(g_plts, dnp * 16)) + 1 dnp = dnp + 1 // Build dynstr db : ., mut = alloc(dn) @@ -485,12 +485,12 @@ fn emit(buf: string, total: int, is_so: int): break j=0 loop: - if j >= istr_len(r64(g_plts, pi * 16)): + if j >= istr_len(cell_load(g_plts, pi * 16)): break - w8(db,nxt+j, load8(r64(g_plts, pi * 16),j)) + w8(db,nxt+j, load8(cell_load(g_plts, pi * 16),j)) j=j+1 - w8(db,nxt+istr_len(r64(g_plts, pi * 16)),0) - nxt=nxt+istr_len(r64(g_plts, pi * 16))+1 + w8(db,nxt+istr_len(cell_load(g_plts, pi * 16)),0) + nxt=nxt+istr_len(cell_load(g_plts, pi * 16))+1 pi=pi+1 // Write dynstr to its chunk dsc := cby(7) @@ -498,44 +498,44 @@ fn emit(buf: string, total: int, is_so: int): loop: if di2 >= nxt: break - w8(buf,r64(g_ch_foff, dsc * 8)+di2,bu8(db,di2)) + w8(buf,cell_load(g_ch_foff, dsc * 8)+di2,bu8(db,di2)) di2=di2+1 // Write dynamic entries - sv := r64(g_ch_vaddr, cby(6) * 8) - dv2 := r64(g_ch_vaddr, dsc * 8) - gv2 := r64(g_ch_vaddr, cby(4) * 8) - rv := r64(g_ch_vaddr, cby(8) * 8) - rs := r64(g_ch_size, cby(8) * 8) + sv := cell_load(g_ch_vaddr, cby(6) * 8) + dv2 := cell_load(g_ch_vaddr, dsc * 8) + gv2 := cell_load(g_ch_vaddr, cby(4) * 8) + rv := cell_load(g_ch_vaddr, cby(8) * 8) + rs := cell_load(g_ch_size, cby(8) * 8) de : ., mut = 0 - w64(buf,p+de*16,5) - w64(buf,p+de*16+8,dv2) + cell_store(buf,p+de*16,5) + cell_store(buf,p+de*16+8,dv2) de=de+1 // DT_STRTAB - w64(buf,p+de*16,11) - w64(buf,p+de*16+8,24) + cell_store(buf,p+de*16,11) + cell_store(buf,p+de*16+8,24) de=de+1 - w64(buf,p+de*16,6) - w64(buf,p+de*16+8,sv) + cell_store(buf,p+de*16,6) + cell_store(buf,p+de*16+8,sv) de=de+1 // DT_SYMTAB - w64(buf,p+de*16,10) - w64(buf,p+de*16+8,r64(g_ch_size, dsc * 8)) + cell_store(buf,p+de*16,10) + cell_store(buf,p+de*16+8,cell_load(g_ch_size, dsc * 8)) de=de+1 - w64(buf,p+de*16,3) - w64(buf,p+de*16+8,gv2) + cell_store(buf,p+de*16,3) + cell_store(buf,p+de*16+8,gv2) de=de+1 // DT_PLTGOT - w64(buf,p+de*16,2) - w64(buf,p+de*16+8,rs) + cell_store(buf,p+de*16,2) + cell_store(buf,p+de*16+8,rs) de=de+1 - w64(buf,p+de*16,20) - w64(buf,p+de*16+8,7) + cell_store(buf,p+de*16,20) + cell_store(buf,p+de*16+8,7) de=de+1 // DT_PLTREL=RELA - w64(buf,p+de*16,23) - w64(buf,p+de*16+8,rv) + cell_store(buf,p+de*16,23) + cell_store(buf,p+de*16+8,rv) de=de+1 // DT_JMPREL - w64(buf,p+de*16,1) - w64(buf,p+de*16+8,so_off) + cell_store(buf,p+de*16,1) + cell_store(buf,p+de*16+8,so_off) de=de+1 // DT_NEEDED - w64(buf,p+de*16,0) - w64(buf,p+de*16+8,0) + cell_store(buf,p+de*16,0) + cell_store(buf,p+de*16+8,0) // .dynsym if k == 6: @@ -543,8 +543,8 @@ fn emit(buf: string, total: int, is_so: int): w8(buf,p+4,0) w8(buf,p+5,0) w16(buf,p+6,0) - w64(buf,p+8,0) - w64(buf,p+16,0) + cell_store(buf,p+8,0) + cell_store(buf,p+16,0) so_off2 : ., mut = 1 nxt2 : ., mut = 12 pi : ., mut = 0 @@ -556,25 +556,25 @@ fn emit(buf: string, total: int, is_so: int): w8(buf,so2+4,18) w8(buf,so2+5,0) w16(buf,so2+6,0) - w64(buf,so2+8,0) - w64(buf,so2+16,0) - nxt2 = nxt2 + istr_len(r64(g_plts, pi * 16)) + 1 + cell_store(buf,so2+8,0) + cell_store(buf,so2+16,0) + nxt2 = nxt2 + istr_len(cell_load(g_plts, pi * 16)) + 1 pi=pi+1 // .rela.plt if k == 8: - gv3 := r64(g_ch_vaddr, cby(4) * 8) + gv3 := cell_load(g_ch_vaddr, cby(4) * 8) pi : ., mut = 0 loop: if pi >= g_plt_count: break ro := p+pi*24 - w64(buf,ro,gv3+24+pi*8) + cell_store(buf,ro,gv3+24+pi*8) rinfo := pi + 1 rinfo = rinfo * 65536 rinfo = rinfo * 65536 + 7 - w64(buf,ro+8,rinfo) - w64(buf,ro+16,0) + cell_store(buf,ro+8,rinfo) + cell_store(buf,ro+16,0) pi=pi+1 i = i + 1 @@ -586,18 +586,18 @@ fn patch_relocs(): if g_x86_ext_rel_count == 0: return uc := cby(2) - uv := r64(g_ch_vaddr, uc * 8) - pv := r64(g_ch_vaddr, cby(3) * 8) + uv := cell_load(g_ch_vaddr, uc * 8) + pv := cell_load(g_ch_vaddr, cby(3) * 8) ri : ., mut = 0 loop: if ri >= g_x86_ext_rel_count: break - abs_pos := r64(g_x86_ext_rel_pos, ri * 8) + abs_pos := cell_load(g_x86_ext_rel_pos, ri * 8) code_off : ., mut = abs_pos - 176 if code_off < 0 || code_off >= g_user_size: ri=ri+1 continue - fn_name_ni := r64(g_x86_ext_rel_name, ri * 8) + fn_name_ni := cell_load(g_x86_ext_rel_name, ri * 8) fn_name : ., mut = "" if fn_name_ni >= 0: fn_name = istr_get(fn_name_ni) @@ -609,7 +609,7 @@ fn patch_relocs(): // Same as above: column 0 of g_plts stores the PLT name string pointer (the // @ptr_of write side of ctx_add_plt). plt_name_str := unsafe: - @str_of(r64(g_plts, si * 16)) + @str_of(cell_load(g_plts, si * 16)) if str_eq(plt_name_str, fn_name) != 0: plt_idx = si break @@ -644,14 +644,14 @@ fn ctx_add_so(path: string): dyn_grow_so(g_so_count + 1) path_ptr := unsafe: @ptr_of(path) - w64(g_so_paths, g_so_count * 8, path_ptr) + cell_store(g_so_paths, g_so_count * 8, path_ptr) g_so_count = g_so_count + 1 fn ctx_add_plt(name: string, so_idx: int): dyn_grow_plts(g_plt_count + 1) name_ptr := unsafe: @ptr_of(name) - w64(g_plts, g_plt_count * 16, name_ptr) - w64(g_plts, g_plt_count * 16 + 8, so_idx) + cell_store(g_plts, g_plt_count * 16, name_ptr) + cell_store(g_plts, g_plt_count * 16 + 8, so_idx) g_plt_count = g_plt_count + 1 // Produce dynamically-linked ELF executable @@ -667,7 +667,7 @@ fn ctx_emit_dyn(buf: string, path: string) -> int: cnew(8) layout() patch_relocs() - total := r64(g_ch_vaddr, (g_ch_count - 1) * 8) + r64(g_ch_size, (g_ch_count - 1) * 8) - g_text_base + total := cell_load(g_ch_vaddr, (g_ch_count - 1) * 8) + cell_load(g_ch_size, (g_ch_count - 1) * 8) - g_text_base println(int_str(total)) emit(buf, total, 0) fd := syscall3(2, path, 577, 493) // 0755: ELF output must be executable @@ -683,7 +683,7 @@ fn ctx_emit_so(buf: string, path: string) -> int: cnew(2) g_ch_count = 2 layout() - total := r64(g_ch_vaddr, (g_ch_count - 1) * 8) + r64(g_ch_size, (g_ch_count - 1) * 8) - g_text_base + total := cell_load(g_ch_vaddr, (g_ch_count - 1) * 8) + cell_load(g_ch_size, (g_ch_count - 1) * 8) - g_text_base emit(buf, total, 1) fd := syscall3(2, path, 577, 493) // 0755: ELF output must be executable if fd < 0: @@ -707,7 +707,7 @@ fn so_parse_text(buf: string) -> int: // Find the executable PROGBITS section (.text) in the .so if str_len(buf) < 64: return -1 - e_shoff := r64(buf, 40) + e_shoff := cell_load(buf, 40) e_shnum := read_u16(buf, 60) // A normal linker may place .init/.plt before .text. The old first-match // scan copied one of those stubs and then resolved symbols against the @@ -717,8 +717,8 @@ fn so_parse_text(buf: string) -> int: shstr_sz : ., mut = 0 if e_shstrndx < e_shnum: sh := e_shoff + e_shstrndx * 64 - shstr_off = r64(buf, sh + 24) - shstr_sz = r64(buf, sh + 32) + shstr_off = cell_load(buf, sh + 24) + shstr_sz = cell_load(buf, sh + 32) i : ., mut = 0 fallback_off : ., mut = -1 fallback_sz : ., mut = 0 @@ -727,10 +727,10 @@ fn so_parse_text(buf: string) -> int: if i >= e_shnum: break t := r32(buf, e_shoff+i*64+4) - fl := r64(buf, e_shoff+i*64+8) - a := r64(buf, e_shoff+i*64+16) - o := r64(buf, e_shoff+i*64+24) - s := r64(buf, e_shoff+i*64+32) + fl := cell_load(buf, e_shoff+i*64+8) + a := cell_load(buf, e_shoff+i*64+16) + o := cell_load(buf, e_shoff+i*64+24) + s := cell_load(buf, e_shoff+i*64+32) // SHT_PROGBITS(1) + SHF_ALLOC|SHF_EXECINSTR(6) = .text if t == 1 && fl == 6 && s > 0: if fallback_off < 0: @@ -772,7 +772,7 @@ fn ctx_emit_static(buf: string, path: string) -> int: loop: if si >= g_so_count: break - sp := r64(g_so_paths, si * 8) + sp := cell_load(g_so_paths, si * 8) // sp is a raw-word string pointer. Restore the view explicitly before use (2(a) pattern; each use restores it inside unsafe). sp_str := unsafe: @str_of(sp) @@ -826,8 +826,8 @@ fn ctx_emit_static(buf: string, path: string) -> int: loop: if rpi >= g_x86_ext_rel_count: break - abs_pos := r64(g_x86_ext_rel_pos, rpi * 8) - fn_ni := r64(g_x86_ext_rel_name, rpi * 8) + abs_pos := cell_load(g_x86_ext_rel_pos, rpi * 8) + fn_ni := cell_load(g_x86_ext_rel_name, rpi * 8) fn_name := istr_get(fn_ni) if str_len(fn_name) > 0: sym_addr := so_find(so_buf, fn_name) @@ -870,9 +870,9 @@ fn ctx_emit_static(buf: string, path: string) -> int: w16(buf,16,2) w16(buf,18,62) w32(buf,20,1) - w64(buf,24,0x400000 + user_out) // entry = user code's _start - w64(buf,32,64) - w64(buf,40,0) + cell_store(buf,24,0x400000 + user_out) // entry = user code's _start + cell_store(buf,32,64) + cell_store(buf,40,0) w16(buf,52,64) w16(buf,54,56) w16(buf,56,2) @@ -885,12 +885,12 @@ fn ctx_emit_static(buf: string, path: string) -> int: w32(buf,68,7) else: w32(buf,68,5) - w64(buf,72,0) - w64(buf,80,0x400000) - w64(buf,88,0x400000) - w64(buf,96,total) - w64(buf,104,total) - w64(buf,112,4096) + cell_store(buf,72,0) + cell_store(buf,80,0x400000) + cell_store(buf,88,0x400000) + cell_store(buf,96,total) + cell_store(buf,104,total) + cell_store(buf,112,4096) // The embedded user code still contains RIP-relative references to the // original frontend BSS page, shifted by the copied .so text. Map a // writable anonymous load at the next page so allocator/global slots do diff --git a/src/format/elf/resolve.cr b/src/format/elf/resolve.cr index a7303b4f..fa65427d 100644 --- a/src/format/elf/resolve.cr +++ b/src/format/elf/resolve.cr @@ -25,11 +25,11 @@ fn res_labels(): if fi >= g_ir_func_count: break - ist := r64(g_ir_func_instr_start, fi * 8) - ic := r64(g_ir_func_instr_count, fi * 8) - vs_r := r64(g_ir_func_var_start, fi * 8) - vc_r := r64(g_ir_func_var_count, fi * 8) - pc_r := r64(g_ir_func_param_count, fi * 8) + ist := cell_load(g_ir_func_instr_start, fi * 8) + ic := cell_load(g_ir_func_instr_count, fi * 8) + vs_r := cell_load(g_ir_func_var_start, fi * 8) + vc_r := cell_load(g_ir_func_var_count, fi * 8) + pc_r := cell_load(g_ir_func_param_count, fi * 8) // Init slot state for accurate sizing g2_init() @@ -60,7 +60,7 @@ fn res_labels(): ln := iri_s1(inst_idx) if ln >= 0: grow_label_poses(ln + 1) - w64(g_label_poses, ln * 8, off) + cell_store(g_label_poses, ln * 8, off) if ln + 1 > g_label_count: g_label_count = ln + 1 else: @@ -68,7 +68,7 @@ fn res_labels(): ii = ii + 1 // Save per-function code size for Phase 2 reuse grow_func_code_sz(fi + 1) - w64(g_x86_func_code_sz, fi * 8, off) + cell_store(g_x86_func_code_sz, fi * 8, off) // ── Pass 2: patch BRANCH/JUMP with resolved offsets ── ii = 0 @@ -77,14 +77,14 @@ fn res_labels(): break inst_idx := ist + ii if iri_op(inst_idx) == IR_BRANCH: - true_off := r64(g_label_poses, iri_s2(inst_idx) * 8) - false_off := r64(g_label_poses, iri_s3(inst_idx) * 8) + true_off := cell_load(g_label_poses, iri_s2(inst_idx) * 8) + false_off := cell_load(g_label_poses, iri_s3(inst_idx) * 8) iri_set_op(inst_idx, IR_BRANCH) iri_set_s2(inst_idx, true_off) iri_set_s3(inst_idx, false_off) iri_set_tk(inst_idx, IR_RESOLVED) if iri_op(inst_idx) == IR_JUMP: - tgt_off := r64(g_label_poses, iri_s1(inst_idx) * 8) + tgt_off := cell_load(g_label_poses, iri_s1(inst_idx) * 8) iri_set_op(inst_idx, IR_JUMP) iri_set_s1(inst_idx, tgt_off) iri_set_tk(inst_idx, IR_RESOLVED) diff --git a/src/kernel/bytes.cr b/src/kernel/bytes.cr index 061791ad..8a1f11b9 100644 --- a/src/kernel/bytes.cr +++ b/src/kernel/bytes.cr @@ -1,8 +1,8 @@ // src/kernel/bytes.cr: byte helpers. The kernel is self-contained so its trust // base can be audited independently. -// The byte layer (w8, bu8, w32, r32, w64, r64, and _dyncpy) is copied from -// src/compiler/dyn_arr.cr without compiler globals, which the kernel does not -// need. Semantics match dyn_arr.cr lines 5-46 and 144-146 byte for byte. +// The byte layer (w8, bu8, w32, r32, cell_store/cell_load, and _dyncpy) is +// shared by the compiler and the target/runtime axes. It is not language +// semantics; semantic passes use typed operations instead. fn w8(buf: string, pos: int, val: int): store8(buf, pos, val % 256) @@ -43,7 +43,7 @@ fn r32(buf: string, pos: int) -> int: else: v = v + b3 * 16777216 return v -fn w64(buf: string, pos: int, val: int): +fn cell_store(buf: string, pos: int, val: int): cur : ., mut = val i : ., mut = 0 loop: @@ -55,7 +55,7 @@ fn w64(buf: string, pos: int, val: int): store8(buf, pos + i, byte) cur = (cur - byte) / 256 i = i + 1 -fn r64(buf: string, pos: int) -> int: +fn cell_load(buf: string, pos: int) -> int: // The low dword is unsigned. Reading it through r32 double-subtracts // 2^32 for negative values, e.g. -1 becomes -1 + (-1 * 2^32). lo := bu8(buf,pos) + bu8(buf,pos+1)*256 + bu8(buf,pos+2)*65536 + bu8(buf,pos+3)*16777216 @@ -74,14 +74,8 @@ fn _dyncpy(src: string, nbytes: int, dst: string): // ── Compiler cell API ── // The frontend uses these names for compiler-owned table slots. The cell width -// and raw machine access stay in this low-level module; semantic passes do not -// read or write w64/r64/r32/load8 directly. - -fn cell_store(buf: string, pos: int, val: int): - w64(buf, pos, val) - -fn cell_load(buf: string, pos: int) -> int: - return r64(buf, pos) +// and raw byte access stay in this low-level module; semantic passes do not +// issue fixed-width memory operations. fn cell_store32(buf: string, pos: int, val: int): w32(buf, pos, val) diff --git a/src/kernel/mctt.cr b/src/kernel/mctt.cr index 7b83165f..dd288ae5 100644 --- a/src/kernel/mctt.cr +++ b/src/kernel/mctt.cr @@ -95,14 +95,14 @@ fn ctx_extend(a : int): loop: if i >= g_ctx_len: break - w64(nb , i * 8 , r64(g_ctx_buf , i * 8)) + cell_store(nb , i * 8 , cell_load(g_ctx_buf , i * 8)) i = i + 1 g_ctx_buf = nb g_ctx_cap = nc - w64(g_ctx_buf , g_ctx_len * 8 , a) + cell_store(g_ctx_buf , g_ctx_len * 8 , a) g_ctx_len = g_ctx_len + 1 fn ctx_get(x : int) -> int: if x < 0 || x >= g_ctx_len: return -1 - return r64(g_ctx_buf , (g_ctx_len - 1 -x) * 8) + return cell_load(g_ctx_buf , (g_ctx_len - 1 -x) * 8) diff --git a/src/kernel/term_io.cr b/src/kernel/term_io.cr index 6a1959c0..7b3799a7 100644 --- a/src/kernel/term_io.cr +++ b/src/kernel/term_io.cr @@ -70,11 +70,11 @@ fn kern_new(k: int) -> int: if g_kernel_count * 5 + 5 > KERNEL_SLOTS: return -1 i := g_kernel_count * 5 - w64(g_kernel_nodes, i * 8, k) - w64(g_kernel_nodes, (i + 1) * 8, 0) - w64(g_kernel_nodes, (i + 2) * 8, 0) - w64(g_kernel_nodes, (i + 3) * 8, 0) - w64(g_kernel_nodes, (i + 4) * 8, 0) + cell_store(g_kernel_nodes, i * 8, k) + cell_store(g_kernel_nodes, (i + 1) * 8, 0) + cell_store(g_kernel_nodes, (i + 2) * 8, 0) + cell_store(g_kernel_nodes, (i + 3) * 8, 0) + cell_store(g_kernel_nodes, (i + 4) * 8, 0) g_kernel_count = g_kernel_count + 1 return i @@ -82,12 +82,12 @@ fn kern_set(n: int, slot: int, v: int): if n < 0: return // Return early for a failed allocation; writing would overwrite node 0. - w64(g_kernel_nodes, (n + slot) * 8, v) + cell_store(g_kernel_nodes, (n + slot) * 8, v) fn kern_get(n: int, slot: int) -> int: if n < 0: return -1 - return r64(g_kernel_nodes, (n + slot) * 8) + return cell_load(g_kernel_nodes, (n + slot) * 8) // Parse state. g_kernel_parse_pos : int, mut = 0 diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index 5f627c12..25ab1219 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -58,19 +58,19 @@ fn live_range_slot(func_i: int, var_i: int) -> int: loop: if fi >= func_i: break - off = off + r64(g_ir_func_var_count, fi * 8) + off = off + cell_load(g_ir_func_var_count, fi * 8) fi = fi + 1 - return (off + (var_i - r64(g_ir_func_var_start, func_i * 8))) * 16 + return (off + (var_i - cell_load(g_ir_func_var_start, func_i * 8))) * 16 fn live_first(func_i: int, var_i: int) -> int: if func_i < 0 || var_i < 0: return -1 - return r64(g_ir_live_ranges, live_range_slot(func_i, var_i)) + return cell_load(g_ir_live_ranges, live_range_slot(func_i, var_i)) fn live_last(func_i: int, var_i: int) -> int: if func_i < 0 || var_i < 0: return -1 - return r64(g_ir_live_ranges, live_range_slot(func_i, var_i) + 8) + return cell_load(g_ir_live_ranges, live_range_slot(func_i, var_i) + 8) // Build g_ir_live_ranges. Scan each function's nodes through nod_*. If dest, s1, // or s2 falls in [vs, vs+vc), extend [first_ref, last_ref]: first sight sets @@ -81,7 +81,7 @@ fn compute_live_ranges(): loop: if fi >= g_ir_func_count: break - total = total + r64(g_ir_func_var_count, fi * 8) + total = total + cell_load(g_ir_func_var_count, fi * 8) fi = fi + 1 grow_live_ranges(total) g_live_range_count = total @@ -90,8 +90,8 @@ fn compute_live_ranges(): loop: if zi >= total: break - w64(g_ir_live_ranges, zi * 16, -1) - w64(g_ir_live_ranges, zi * 16 + 8, -1) + cell_store(g_ir_live_ranges, zi * 16, -1) + cell_store(g_ir_live_ranges, zi * 16 + 8, -1) zi = zi + 1 // Scan each function's node window. The segment offset is the running prefix, // matching live_range_slot. g_ir_func_instr_start/count hold NOD node @@ -102,10 +102,10 @@ fn compute_live_ranges(): loop: if fi >= g_ir_func_count: break - ic := r64(g_ir_func_instr_count, fi * 8) - ist := r64(g_ir_func_instr_start, fi * 8) - vc := r64(g_ir_func_var_count, fi * 8) - vs := r64(g_ir_func_var_start, fi * 8) + ic := cell_load(g_ir_func_instr_count, fi * 8) + ist := cell_load(g_ir_func_instr_start, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) + vs := cell_load(g_ir_func_var_start, fi * 8) if vc > 0: ii : ., mut = 0 loop: @@ -118,21 +118,21 @@ fn compute_live_ranges(): if d >= vs && d < vs + vc: lv := d - vs st := (seg + lv) * 16 - if r64(g_ir_live_ranges, st) < 0: - w64(g_ir_live_ranges, st, ii) - w64(g_ir_live_ranges, st + 8, ii) + if cell_load(g_ir_live_ranges, st) < 0: + cell_store(g_ir_live_ranges, st, ii) + cell_store(g_ir_live_ranges, st + 8, ii) if s1 >= vs && s1 < vs + vc: lv := s1 - vs st := (seg + lv) * 16 - if r64(g_ir_live_ranges, st) < 0: - w64(g_ir_live_ranges, st, ii) - w64(g_ir_live_ranges, st + 8, ii) + if cell_load(g_ir_live_ranges, st) < 0: + cell_store(g_ir_live_ranges, st, ii) + cell_store(g_ir_live_ranges, st + 8, ii) if s2 >= vs && s2 < vs + vc: lv := s2 - vs st := (seg + lv) * 16 - if r64(g_ir_live_ranges, st) < 0: - w64(g_ir_live_ranges, st, ii) - w64(g_ir_live_ranges, st + 8, ii) + if cell_load(g_ir_live_ranges, st) < 0: + cell_store(g_ir_live_ranges, st, ii) + cell_store(g_ir_live_ranges, st + 8, ii) ii = ii + 1 seg = seg + vc fi = fi + 1 @@ -221,12 +221,12 @@ fn ent_flags(e: int) -> int: fn entry_start(func_i: int) -> int: if func_i < 0 || func_i >= g_ir_func_count: return 0 - return r64(g_ir_func_entry_start, func_i * 8) + return cell_load(g_ir_func_entry_start, func_i * 8) fn entry_count(func_i: int) -> int: if func_i < 0 || func_i >= g_ir_func_count: return 0 - return r64(g_ir_func_entry_count, func_i * 8) + return cell_load(g_ir_func_entry_count, func_i * 8) fn compute_entries(func_i: int) -> int: if func_i == 0: @@ -237,10 +237,10 @@ fn compute_entries(func_i: int) -> int: loc_registry_reset() // The function window uses the same g_ir_func_instr_start/count values as // compute_live_ranges: NOD node coordinates, read through nod_*. - ic := r64(g_ir_func_instr_count, func_i * 8) - ist := r64(g_ir_func_instr_start, func_i * 8) - vc := r64(g_ir_func_var_count, func_i * 8) - vs := r64(g_ir_func_var_start, func_i * 8) + ic := cell_load(g_ir_func_instr_count, func_i * 8) + ist := cell_load(g_ir_func_instr_start, func_i * 8) + vc := cell_load(g_ir_func_var_count, func_i * 8) + vs := cell_load(g_ir_func_var_start, func_i * 8) cnt : ., mut = 0 if ic > 0 && vc > 0: // One open-entry slot per function-local variable, initialized to -1. @@ -250,7 +250,7 @@ fn compute_entries(func_i: int) -> int: loop: if pz >= vc: break - w64(prev, pz * 8, -1) + cell_store(prev, pz * 8, -1) pz = pz + 1 // Single scan for definitions: IR_STORE uses s1 as the target; every other // definition uses dest >= 0. STORE_INDEX_VAR, STORE_PTR, and DYN_DISPATCH @@ -277,7 +277,7 @@ fn compute_entries(func_i: int) -> int: if ll >= 0: last_global = ist + ll // Close the previous version at min(def-1, last_ref). - pe := r64(prev, lv * 8) + pe := cell_load(prev, lv * 8) if pe >= 0: pend : ., mut = inst - 1 if last_global >= 0 && last_global < pend: @@ -292,7 +292,7 @@ fn compute_entries(func_i: int) -> int: w32(g_ir_entries, eo + OFF_ENTRY_LE, last_global) w32(g_ir_entries, eo + OFF_ENTRY_HOME, -1) w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0) - w64(prev, lv * 8, g_entry_count) + cell_store(prev, lv * 8, g_entry_count) g_entry_count = g_entry_count + 1 cnt = cnt + 1 ii = ii + 1 @@ -301,7 +301,7 @@ fn compute_entries(func_i: int) -> int: loop: if lv2 >= vc: break - if r64(prev, lv2 * 8) < 0: + if cell_load(prev, lv2 * 8) < 0: gv := vs + lv2 ff := live_first(func_i, gv) ll := live_last(func_i, gv) @@ -318,8 +318,8 @@ fn compute_entries(func_i: int) -> int: cnt = cnt + 1 lv2 = lv2 + 1 grow_func_entry_meta(func_i + 1) - w64(g_ir_func_entry_start, func_i * 8, g_entry_count - cnt) - w64(g_ir_func_entry_count, func_i * 8, cnt) + cell_store(g_ir_func_entry_start, func_i * 8, g_entry_count - cnt) + cell_store(g_ir_func_entry_count, func_i * 8, cnt) return cnt // Name of a definition opcode for the --dump-entries kind= field. The list covers @@ -407,7 +407,7 @@ fn dump_entries_summary(): print("== entries func ") print(int_str(fi)) print(" (") - print(istr_get(r64(g_ir_func_name_idx, fi * 8))) + print(istr_get(cell_load(g_ir_func_name_idx, fi * 8))) print("): ") println(int_str(ec)) ei : ., mut = 0 @@ -542,7 +542,7 @@ fn dump_coexist_summary(): print("== coexist func ") print(int_str(fi)) print(" (") - print(istr_get(r64(g_ir_func_name_idx, fi * 8))) + print(istr_get(cell_load(g_ir_func_name_idx, fi * 8))) print("):") print(" ver_conf ") print(int_str(coexist_version_conflicts(fi))) @@ -620,8 +620,8 @@ fn meta_reg_for_var(var_idx: int) -> int: loop: if fi >= g_ir_func_count: break - vs := r64(g_ir_func_var_start, fi * 8) - vc := r64(g_ir_func_var_count, fi * 8) + vs := cell_load(g_ir_func_var_start, fi * 8) + vc := cell_load(g_ir_func_var_count, fi * 8) if var_idx >= vs && var_idx < vs + vc: es := entry_start(fi) ec := entry_count(fi) @@ -641,14 +641,14 @@ fn meta_reg_for_var(var_idx: int) -> int: // Compare two 16-byte records for sorting by (loc, live_start). fn rl_rec_lt(buf: string, a: int, b: int) -> int: - la := r64(buf, a * 16) - lb := r64(buf, b * 16) + la := cell_load(buf, a * 16) + lb := cell_load(buf, b * 16) if la != lb: if la < lb: return 1 return 0 - ea := r64(buf, a * 16 + 8) - eb := r64(buf, b * 16 + 8) + ea := cell_load(buf, a * 16 + 8) + eb := cell_load(buf, b * 16 + 8) if ent_live_start(ea) < ent_live_start(eb): return 1 return 0 @@ -680,26 +680,26 @@ fn rl_merge_sort(buf: string, n: int): if i >= mid || j >= right: break if rl_rec_lt(buf, i, j) != 0: - w64(tmp, k * 16, r64(buf, i * 16)) - w64(tmp, k * 16 + 8, r64(buf, i * 16 + 8)) + cell_store(tmp, k * 16, cell_load(buf, i * 16)) + cell_store(tmp, k * 16 + 8, cell_load(buf, i * 16 + 8)) i = i + 1 else: - w64(tmp, k * 16, r64(buf, j * 16)) - w64(tmp, k * 16 + 8, r64(buf, j * 16 + 8)) + cell_store(tmp, k * 16, cell_load(buf, j * 16)) + cell_store(tmp, k * 16 + 8, cell_load(buf, j * 16 + 8)) j = j + 1 k = k + 1 loop: if i >= mid: break - w64(tmp, k * 16, r64(buf, i * 16)) - w64(tmp, k * 16 + 8, r64(buf, i * 16 + 8)) + cell_store(tmp, k * 16, cell_load(buf, i * 16)) + cell_store(tmp, k * 16 + 8, cell_load(buf, i * 16 + 8)) i = i + 1 k = k + 1 loop: if j >= right: break - w64(tmp, k * 16, r64(buf, j * 16)) - w64(tmp, k * 16 + 8, r64(buf, j * 16 + 8)) + cell_store(tmp, k * 16, cell_load(buf, j * 16)) + cell_store(tmp, k * 16 + 8, cell_load(buf, j * 16 + 8)) j = j + 1 k = k + 1 left = right @@ -707,8 +707,8 @@ fn rl_merge_sort(buf: string, n: int): loop: if zi >= n: break - w64(buf, zi * 16, r64(tmp, zi * 16)) - w64(buf, zi * 16 + 8, r64(tmp, zi * 16 + 8)) + cell_store(buf, zi * 16, cell_load(tmp, zi * 16)) + cell_store(buf, zi * 16 + 8, cell_load(tmp, zi * 16 + 8)) zi = zi + 1 width = width * 2 @@ -723,7 +723,7 @@ fn rl_func_name(fi: int): print("func ") print(int_str(fi)) print(" (") - print(istr_get(r64(g_ir_func_name_idx, fi * 8))) + print(istr_get(cell_load(g_ir_func_name_idx, fi * 8))) print(")") // Rule 1 violation report. Printing is capped by RPT_MAX; the count is not @@ -768,7 +768,7 @@ fn rl_rule1_sweep(func_i: int, rec: string, cnt: int, cap_base: int) -> int: loop: if run_end >= cnt: break - if r64(rec, run_end * 16) != r64(rec, run_start * 16): + if cell_load(rec, run_end * 16) != cell_load(rec, run_start * 16): break run_end = run_end + 1 maxj : ., mut = -1 @@ -776,14 +776,14 @@ fn rl_rule1_sweep(func_i: int, rec: string, cnt: int, cap_base: int) -> int: loop: if zi >= run_end: break - e := r64(rec, zi * 16 + 8) + e := cell_load(rec, zi * 16 + 8) if maxj >= 0: - mej := r64(rec, maxj * 16 + 8) + mej := cell_load(rec, maxj * 16 + 8) if ent_live_end(mej) >= ent_live_start(e): if cap_base + viol < RPT_MAX: - rl_report_rule1(func_i, r64(rec, zi * 16), mej, e) + rl_report_rule1(func_i, cell_load(rec, zi * 16), mej, e) viol = viol + 1 - if maxj < 0 || ent_live_end(e) > ent_live_end(r64(rec, maxj * 16 + 8)): + if maxj < 0 || ent_live_end(e) > ent_live_end(cell_load(rec, maxj * 16 + 8)): maxj = zi zi = zi + 1 run_start = run_end @@ -845,14 +845,14 @@ fn rl_rule2_func(func_i: int, vs: int, vc: int, nst: int, nc: int, es: int, ec: break e := es + ei if ent_var(e) == gv && ent_def(e) >= 0: - w64(vers, vj * 8, e) + cell_store(vers, vj * 8, e) vj = vj + 1 ei = ei + 1 // Scan reads in ascending node order through nod_op/nod_s1/nod_s2. The // version cursor advances monotonically. ent_def and ent_live_end store // global node coordinates, so comparisons must use inst, not the local ii. cursor : ., mut = 0 - first_def := r64(vers, 0 * 8) + first_def := cell_load(vers, 0 * 8) ii : ., mut = 0 loop: if ii >= nc: @@ -871,11 +871,11 @@ fn rl_rule2_func(func_i: int, vs: int, vc: int, nst: int, nc: int, es: int, ec: loop: if cursor + 1 >= m: break - nx := r64(vers, (cursor + 1) * 8) + nx := cell_load(vers, (cursor + 1) * 8) if ent_def(nx) > inst: break cursor = cursor + 1 - cv := r64(vers, cursor * 8) + cv := cell_load(vers, cursor * 8) if ent_live_end(cv) < inst: if violations < RPT_MAX: rl_report_rule2(func_i, gv, inst) @@ -896,10 +896,10 @@ fn verify_regalloc_consistency(func_i: int) -> int: return 0 // The node window comes from g_ir_func_instr_start/count and is passed to // rl_rule2_func as nst/nc. Node fields are read through nod_*. - ic := r64(g_ir_func_instr_count, func_i * 8) - ist := r64(g_ir_func_instr_start, func_i * 8) - vc := r64(g_ir_func_var_count, func_i * 8) - vs := r64(g_ir_func_var_start, func_i * 8) + ic := cell_load(g_ir_func_instr_count, func_i * 8) + ist := cell_load(g_ir_func_instr_start, func_i * 8) + vc := cell_load(g_ir_func_var_count, func_i * 8) + vs := cell_load(g_ir_func_var_start, func_i * 8) // Rule 1 has two passes: // 1. Registry pass: entries with kern_loc_of(e) >= 0 grouped by location. @@ -933,8 +933,8 @@ fn verify_regalloc_consistency(func_i: int) -> int: if ent_live_start(e) >= 0 && ent_live_end(e) >= 0: loc := kern_loc_of(e) if loc >= 0: - w64(rec, ci * 16, loc) - w64(rec, ci * 16 + 8, e) + cell_store(rec, ci * 16, loc) + cell_store(rec, ci * 16 + 8, e) ci = ci + 1 ei = ei + 1 rule1_viol = rl_rule1_sweep(func_i, rec, reg_cnt, 0) @@ -951,8 +951,8 @@ fn verify_regalloc_consistency(func_i: int) -> int: if ent_live_start(e) >= 0 && ent_live_end(e) >= 0: h := ent_home(e) if h >= 0: - w64(rec, ci * 16, h) - w64(rec, ci * 16 + 8, e) + cell_store(rec, ci * 16, h) + cell_store(rec, ci * 16 + 8, e) ci = ci + 1 ei = ei + 1 rule1_viol = rule1_viol + rl_rule1_sweep(func_i, rec, home_cnt, rule1_viol) @@ -1036,9 +1036,9 @@ fn nod_tk(n: int) -> int: fn nod_item(n: int) -> int: return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_ITEM) fn nod_edge_first(n: int) -> int: - return r64(g_v7_nod_meta, n * ESZ_NOD_META) + return cell_load(g_v7_nod_meta, n * ESZ_NOD_META) fn nod_edge_count(n: int) -> int: - return r64(g_v7_nod_meta, n * ESZ_NOD_META + 8) + return cell_load(g_v7_nod_meta, n * ESZ_NOD_META + 8) // Edge accessors. e is a global edge index in [0, g_v7_edge_count). Each 8-byte // record packs to_nod in the low 32 bits and kind in the high 32 bits. diff --git a/src/lsp/analysis.cr b/src/lsp/analysis.cr index a86688a9..2271c692 100644 --- a/src/lsp/analysis.cr +++ b/src/lsp/analysis.cr @@ -83,7 +83,7 @@ fn analysis_type_name_ti(ti: int) -> string: break if e > 0: out = out + ", " - out = out + analysis_type_name_ti(r64(g_gen_apply_data, (get_type_extra(ti) + e) * 8)) + out = out + analysis_type_name_ti(cell_load(g_gen_apply_data, (get_type_extra(ti) + e) * 8)) e = e + 1 out = out + ")" return out @@ -155,11 +155,11 @@ fn analysis_token_at(line: int, col: int) -> int: loop: if i >= g_token_count: return -1 - if r64(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) == T_EOF: + if cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) == T_EOF: return -1 - if r64(g_tokens, i * ESZ_TOKEN + OFF_TK_LINE) == tl: - cl := r64(g_tokens, i * ESZ_TOKEN + OFF_TK_COL) - lex := r64(g_tokens, i * ESZ_TOKEN + OFF_TK_LEXEME) + if cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_LINE) == tl: + cl := cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_COL) + lex := cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_LEXEME) w : ., mut = 1 if lex >= 0: w = str_len(istr_get(lex)) @@ -422,9 +422,9 @@ fn analysis_hover(line: int, col: int) -> string: ti := analysis_token_at(line, col) if ti < 0: return "" - if r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) != T_IDENT: + if cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) != T_IDENT: return "" - name_ni := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) + name_ni := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) // Locals and parameters shadow functions and type names. vd := analysis_var_decl(name_ni, line, col) if vd >= 0: @@ -455,8 +455,8 @@ fn analysis_decl_tok(kw: int, name_ni: int) -> int: loop: if i + 1 >= g_token_count: return -1 - if r64(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) == kw: - if r64(g_tokens, (i + 1) * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT && r64(g_tokens, (i + 1) * ESZ_TOKEN + OFF_TK_LEXEME) == name_ni: + if cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_KIND) == kw: + if cell_load(g_tokens, (i + 1) * ESZ_TOKEN + OFF_TK_KIND) == T_IDENT && cell_load(g_tokens, (i + 1) * ESZ_TOKEN + OFF_TK_LEXEME) == name_ni: return i + 1 i = i + 1 @@ -464,14 +464,14 @@ fn analysis_decl_tok(kw: int, name_ni: int) -> int: fn analysis_path_for_line(ln1: int) -> string: fid : ., mut = -1 if ln1 >= 1 && ln1 - 1 < g_line_count: - fid = r64(g_line_fileid, (ln1 - 1) * 8) + fid = cell_load(g_line_fileid, (ln1 - 1) * 8) if fid < 0 && g_file_count > 0: - fid = r64(g_files, 0) + fid = cell_load(g_files, 0) i : ., mut = 0 loop: if i >= g_file_count: break - if r64(g_files, i * 16) == fid: + if cell_load(g_files, i * 16) == fid: p := load_str_ptr(g_files, i * 16 + 8) if str_len(p) > 0: return p @@ -482,15 +482,15 @@ fn analysis_path_for_line(ln1: int) -> string: fn analysis_line_offset(ln1: int) -> int: fid : ., mut = -1 if ln1 >= 1 && ln1 - 1 < g_line_count: - fid = r64(g_line_fileid, (ln1 - 1) * 8) + fid = cell_load(g_line_fileid, (ln1 - 1) * 8) if fid < 0: return 0 i : ., mut = 1 loop: if i >= g_seg_count: break - if r64(g_seg_fileids, i * 8) == fid: - limit := r64(g_seg_starts, i * 8) + if cell_load(g_seg_fileids, i * 8) == fid: + limit := cell_load(g_seg_starts, i * 8) off_lines : ., mut = 0 pos : ., mut = 0 loop: @@ -533,9 +533,9 @@ fn analysis_definition(line: int, col: int) -> string: ti := analysis_token_at(line, col) if ti < 0: return "" - if r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) != T_IDENT: + if cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) != T_IDENT: return "" - name_ni := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) + name_ni := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) // 局部变量/参数 vd := analysis_var_decl(name_ni, line, col) if vd >= 0: @@ -545,20 +545,20 @@ fn analysis_definition(line: int, col: int) -> string: if fi >= 0: dfn := analysis_decl_tok(T_FN, name_ni) if dfn >= 0: - return analysis_def_json(r64(g_tokens, dfn * ESZ_TOKEN + OFF_TK_LINE), - r64(g_tokens, dfn * ESZ_TOKEN + OFF_TK_COL)) + return analysis_def_json(cell_load(g_tokens, dfn * ESZ_TOKEN + OFF_TK_LINE), + cell_load(g_tokens, dfn * ESZ_TOKEN + OFF_TK_COL)) return "" // 结构体 / 枚举(令牌反查声明) if find_struct_by_name(name_ni) >= 0: d := analysis_decl_tok(T_STRUCT, name_ni) if d >= 0: - return analysis_def_json(r64(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE), - r64(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) + return analysis_def_json(cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE), + cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) if find_enum(name_ni) >= 0: d := analysis_decl_tok(T_ENUM, name_ni) if d >= 0: - return analysis_def_json(r64(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE), - r64(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) + return analysis_def_json(cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE), + cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) return "" // Completion: position to prefix, then filter candidates and emit JSON. @@ -939,11 +939,11 @@ fn analysis_cand_add(cand: string, count: int, ni: int, kind: int) -> int: loop: if i >= count: break - if r64(cand, i * 16) == ni: + if cell_load(cand, i * 16) == ni: return 0 i = i + 1 - w64(cand, count * 16, ni) - w64(cand, count * 16 + 8, kind) + cell_store(cand, count * 16, ni) + cell_store(cand, count * 16 + 8, kind) return 1 // Map SYM_* kinds to CompletionItemKind: functions 3, variables 6, types 22, @@ -1011,11 +1011,11 @@ fn analysis_completion(line: int, col: int) -> string: loop: if j >= cnt: break - ni := r64(cand, j * 16) + ni := cell_load(cand, j * 16) if analysis_has_prefix(istr_get(ni), prefix) != 0: if str_len(out) > 10: out = out + "," - out = out + analysis_citem(istr_get(ni), r64(cand, j * 16 + 8)) + out = out + analysis_citem(istr_get(ni), cell_load(cand, j * 16 + 8)) j = j + 1 out = out + "]}" return out @@ -1065,10 +1065,10 @@ fn analysis_document_symbol() -> string: ln = ast_line(fn_node) cl = ast_col(fn_node) if ln > 0 && analysis_line_offset(ln) == 0: - w64(ent, cnt * 32, ni) - w64(ent, cnt * 32 + 8, 12) // SymbolKind.Function = 12 - w64(ent, cnt * 32 + 16, ln) - w64(ent, cnt * 32 + 24, cl) + cell_store(ent, cnt * 32, ni) + cell_store(ent, cnt * 32 + 8, 12) // SymbolKind.Function = 12 + cell_store(ent, cnt * 32 + 16, ln) + cell_store(ent, cnt * 32 + 24, cl) cnt = cnt + 1 i = i + 1 i = 0 @@ -1078,12 +1078,12 @@ fn analysis_document_symbol() -> string: ni := si_name(i) d := analysis_decl_tok(T_STRUCT, ni) if d >= 0: - ln := r64(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE) + ln := cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE) if analysis_line_offset(ln) == 0: - w64(ent, cnt * 32, ni) - w64(ent, cnt * 32 + 8, 23) // SymbolKind.Struct = 23 - w64(ent, cnt * 32 + 16, ln) - w64(ent, cnt * 32 + 24, r64(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) + cell_store(ent, cnt * 32, ni) + cell_store(ent, cnt * 32 + 8, 23) // SymbolKind.Struct = 23 + cell_store(ent, cnt * 32 + 16, ln) + cell_store(ent, cnt * 32 + 24, cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) cnt = cnt + 1 i = i + 1 i = 0 @@ -1093,12 +1093,12 @@ fn analysis_document_symbol() -> string: ni := ei_name(i) d := analysis_decl_tok(T_ENUM, ni) if d >= 0: - ln := r64(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE) + ln := cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_LINE) if analysis_line_offset(ln) == 0: - w64(ent, cnt * 32, ni) - w64(ent, cnt * 32 + 8, 10) // SymbolKind.Enum = 10 - w64(ent, cnt * 32 + 16, ln) - w64(ent, cnt * 32 + 24, r64(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) + cell_store(ent, cnt * 32, ni) + cell_store(ent, cnt * 32 + 8, 10) // SymbolKind.Enum = 10 + cell_store(ent, cnt * 32 + 16, ln) + cell_store(ent, cnt * 32 + 24, cell_load(g_tokens, d * ESZ_TOKEN + OFF_TK_COL)) cnt = cnt + 1 i = i + 1 // Insertion-sort by (line, col) for source order. @@ -1106,27 +1106,27 @@ fn analysis_document_symbol() -> string: loop: if j >= cnt: break - key_ni := r64(ent, j * 32) - key_kind := r64(ent, j * 32 + 8) - key_ln := r64(ent, j * 32 + 16) - key_cl := r64(ent, j * 32 + 24) + key_ni := cell_load(ent, j * 32) + key_kind := cell_load(ent, j * 32 + 8) + key_ln := cell_load(ent, j * 32 + 16) + key_cl := cell_load(ent, j * 32 + 24) k : ., mut = j loop: if k <= 0: break - pln := r64(ent, (k - 1) * 32 + 16) - pcl := r64(ent, (k - 1) * 32 + 24) + pln := cell_load(ent, (k - 1) * 32 + 16) + pcl := cell_load(ent, (k - 1) * 32 + 24) if pln < key_ln || (pln == key_ln && pcl <= key_cl): break - w64(ent, k * 32, r64(ent, (k - 1) * 32)) - w64(ent, k * 32 + 8, r64(ent, (k - 1) * 32 + 8)) - w64(ent, k * 32 + 16, pln) - w64(ent, k * 32 + 24, pcl) + cell_store(ent, k * 32, cell_load(ent, (k - 1) * 32)) + cell_store(ent, k * 32 + 8, cell_load(ent, (k - 1) * 32 + 8)) + cell_store(ent, k * 32 + 16, pln) + cell_store(ent, k * 32 + 24, pcl) k = k - 1 - w64(ent, k * 32, key_ni) - w64(ent, k * 32 + 8, key_kind) - w64(ent, k * 32 + 16, key_ln) - w64(ent, k * 32 + 24, key_cl) + cell_store(ent, k * 32, key_ni) + cell_store(ent, k * 32 + 8, key_kind) + cell_store(ent, k * 32 + 16, key_ln) + cell_store(ent, k * 32 + 24, key_cl) j = j + 1 out : string, mut = "[" q : ., mut = 0 @@ -1135,13 +1135,13 @@ fn analysis_document_symbol() -> string: break if q > 0: out = out + "," - ni := r64(ent, q * 32) - ln1 := r64(ent, q * 32 + 16) - cl1 := r64(ent, q * 32 + 24) + ni := cell_load(ent, q * 32) + ln1 := cell_load(ent, q * 32 + 16) + cl1 := cell_load(ent, q * 32 + 24) out = out + "{\"name\":" out = out + lsp_json_escape_str(istr_get(ni)) out = out + ",\"kind\":" - out = out + int_str(r64(ent, q * 32 + 8)) + out = out + int_str(cell_load(ent, q * 32 + 8)) out = out + ",\"range\":" out = out + analysis_range_json(ln1, cl1) out = out + ",\"selectionRange\":" @@ -1184,10 +1184,10 @@ fn analysis_is_builtin_type(ni: int) -> int: // Token index to legend index. T_IDENT is classified by symbol table and builtin type names. fn analysis_token_type(ti: int) -> int: - k := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) + k := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) if k != T_IDENT: return analysis_kind_type(k) - ni := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) + ni := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) if analysis_is_builtin_type(ni) != 0: return 1 if find_func(ni) >= 0: @@ -1200,7 +1200,7 @@ fn analysis_token_type(ti: int) -> int: // Token index to source byte span. Start at st and mirror the lexer consumption rules for the kind. fn analysis_tok_span(ti: int, st: int, sl: int) -> int: - k := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) + k := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_KIND) // String and char spans mirror the lexer after interpolation splitting: // T_STRING is a literal segment, ending at the closing quote, the next `${`, // or a newline. T_INTERP is a hole from `${` to its matching }, with nesting. @@ -1313,7 +1313,7 @@ fn analysis_tok_span(ti: int, st: int, sl: int) -> int: break return p - st // Identifiers, keywords, and type keywords: the lexeme is the source text. - lex := r64(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) + lex := cell_load(g_tokens, ti * ESZ_TOKEN + OFF_TK_LEXEME) if lex >= 0: return str_len(istr_get(lex)) // Operators and separators have lexeme -1; use the fixed length table, defaulting to one byte. @@ -1330,7 +1330,7 @@ fn analysis_semantic_tokens() -> string: loop: if n >= g_token_count: break - if r64(g_tokens, n * ESZ_TOKEN + OFF_TK_KIND) == T_EOF: + if cell_load(g_tokens, n * ESZ_TOKEN + OFF_TK_KIND) == T_EOF: break n = n + 1 if n == 0: @@ -1345,8 +1345,8 @@ fn analysis_semantic_tokens() -> string: loop: if i >= n: break - tline := r64(g_tokens, i * ESZ_TOKEN + OFF_TK_LINE) - tcol := r64(g_tokens, i * ESZ_TOKEN + OFF_TK_COL) + tline := cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_LINE) + tcol := cell_load(g_tokens, i * ESZ_TOKEN + OFF_TK_COL) loop: // 跨行到 tline if ln >= tline: @@ -1367,13 +1367,13 @@ fn analysis_semantic_tokens() -> string: break pos = pos + 1 cl = cl + 1 - w64(offs, i * 8, pos) + cell_store(offs, i * 8, pos) i = i + 1 // Pass 2: differential encoding relative to the previous token; the first is // relative to (0, 0), and modifiers are always 0. Emit only main-file tokens. main_limit : ., mut = sl if g_seg_count > 1: - main_limit = r64(g_seg_starts, 8) + main_limit = cell_load(g_seg_starts, 8) out : string, mut = "{\"data\":[" prev_line : ., mut = 0 prev_col : ., mut = 0 @@ -1382,9 +1382,9 @@ fn analysis_semantic_tokens() -> string: loop: if j >= n: break - st := r64(offs, j * 8) + st := cell_load(offs, j * 8) if st < main_limit: - line0 : ., mut = r64(g_tokens, j * ESZ_TOKEN + OFF_TK_LINE) - 1 + line0 : ., mut = cell_load(g_tokens, j * ESZ_TOKEN + OFF_TK_LINE) - 1 token_line_start := analysis_line_start(line0) col0 := analysis_utf16_units(token_line_start, st) if first == 0: diff --git a/src/lsp/lsp.cr b/src/lsp/lsp.cr index b5019028..60dab63c 100644 --- a/src/lsp/lsp.cr +++ b/src/lsp/lsp.cr @@ -83,8 +83,8 @@ fn lsp_json_escape_str(s: string) -> string: // One diagnostic as a JSON object. Convert 1-based positions to 0-based and use severity 1. fn lsp_diag_json(di: int) -> string: - ln := r64(g_diags, di * DIAG_REC_SIZE + 16) - cl := r64(g_diags, di * DIAG_REC_SIZE + 24) + ln := cell_load(g_diags, di * DIAG_REC_SIZE + 16) + cl := cell_load(g_diags, di * DIAG_REC_SIZE + 24) msg := load_str_ptr(g_diags, di * DIAG_REC_SIZE + 8) line0 : ., mut = ln - 1 if line0 < 0: @@ -115,7 +115,7 @@ fn lsp_send_file_diags(uri: string, fni: int): loop: if i >= g_diag_count: break - if r64(g_diags, i * DIAG_REC_SIZE + 32) == fni: + if cell_load(g_diags, i * DIAG_REC_SIZE + 32) == fni: if first == 0: out = out + "," out = out + lsp_diag_json(i) @@ -128,7 +128,7 @@ fn lsp_send_file_diags(uri: string, fni: int): fn lsp_publish_diags(): main_fni : ., mut = -1 if g_file_count > 0: - main_fni = r64(g_files, 0) + main_fni = cell_load(g_files, 0) main_uri : string, mut = "file://" if g_file_count > 0: mp := load_str_ptr(g_files, 8) @@ -140,14 +140,14 @@ fn lsp_publish_diags(): loop: if i >= g_diag_count: break - fni := r64(g_diags, i * DIAG_REC_SIZE + 32) + fni := cell_load(g_diags, i * DIAG_REC_SIZE + 32) if fni != main_fni && fni >= 0: uri : string, mut = "file://" fi : ., mut = 0 loop: if fi >= g_file_count: break - if r64(g_files, fi * 16) == fni: + if cell_load(g_files, fi * 16) == fni: fp := load_str_ptr(g_files, fi * 16 + 8) if str_len(fp) > 0: uri = "file://" + fp @@ -159,7 +159,7 @@ fn lsp_publish_diags(): loop: if j >= i: break - if r64(g_diags, j * DIAG_REC_SIZE + 32) == fni: + if cell_load(g_diags, j * DIAG_REC_SIZE + 32) == fni: already = 1 break j = j + 1 diff --git a/src/os/linux/callseq.cr b/src/os/linux/callseq.cr index 0249c1c0..7b1f14e0 100644 --- a/src/os/linux/callseq.cr +++ b/src/os/linux/callseq.cr @@ -167,8 +167,8 @@ fn cs_ret_value(buf: string, pos: int, cp: int, s1: int) -> int: else: // Global: load via RIP-relative into rax grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, pos + cp + 3) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, s1) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 cp = cp + e2_lr(buf, pos+cp, 0) // lea r10, [rip+0] // mov rax, [r10], REX.WB with 0x8B @@ -183,7 +183,7 @@ fn cs_ret_value(buf: string, pos: int, cp: int, s1: int) -> int: cp = cp + e2_ld(buf, pos+cp, 0, g2_slot(s1)) // Record the jump position for the caller to patch to the epilogue. grow_ret_patch(g_x86_ret_patch_count + 1) - w64(g_x86_ret_patch_pos, g_x86_ret_patch_count * 8, pos + cp) + cell_store(g_x86_ret_patch_pos, g_x86_ret_patch_count * 8, pos + cp) g_x86_ret_patch_count = g_x86_ret_patch_count + 1 cp = cp + e2_jmp(buf, pos+cp, 0) return cp @@ -203,22 +203,22 @@ fn cs_call_direct(buf: string, pos: int, cp: int, s3: int, d: int) -> int: loop: if tf >= g_x86_func_off_count: break - if str_eq(istr_get(r64(g_x86_func_offsets, tf*16)), fn2) != 0: - to = r64(g_x86_func_offsets, tf*16+8) + if str_eq(istr_get(cell_load(g_x86_func_offsets, tf*16)), fn2) != 0: + to = cell_load(g_x86_func_offsets, tf*16+8) break tf = tf + 1 // Record call position for post-emission patching grow_call_patch(g_x86_call_patch_count + 1) - w64(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) - w64(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern(fn2)) + cell_store(g_x86_call_patch_pos, g_x86_call_patch_count * 8, pos + cp) + cell_store(g_x86_call_patch_name, g_x86_call_patch_count * 8, str_intern(fn2)) g_x86_call_patch_count = g_x86_call_patch_count + 1 if to >= 0: cp = cp + e2_call(buf, pos+cp, (176 + to) - (pos + cp + 5)) else: // Unknown function: emit external relocation (for dynamic linking) grow_ext_rel(g_x86_ext_rel_count + 1) - w64(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp + 1) - w64(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, s3) + cell_store(g_x86_ext_rel_pos, g_x86_ext_rel_count * 8, pos + cp + 1) + cell_store(g_x86_ext_rel_name, g_x86_ext_rel_count * 8, s3) g_x86_ext_rel_count = g_x86_ext_rel_count + 1 cp = cp + e2_call(buf, pos+cp, 0) if d >= 0: diff --git a/src/os/linux/entry.cr b/src/os/linux/entry.cr index b7732608..c1441e77 100644 --- a/src/os/linux/entry.cr +++ b/src/os/linux/entry.cr @@ -36,8 +36,8 @@ fn emit_start(buf: string, pos: int) -> int: rip_pos := cp + 3 cp = cp + e2_lr(buf, cp, 0) grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argc) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argc) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov [r10], rdi, store argc cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8) @@ -49,8 +49,8 @@ fn emit_start(buf: string, pos: int) -> int: rip_pos2 := cp + 3 cp = cp + e2_lr(buf, cp, 0) grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos2) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_argv) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov [r10], rsi, store argv cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8) @@ -63,8 +63,8 @@ fn emit_start(buf: string, pos: int) -> int: rip_ca := cp + 3 cp = cp + e2_lr(buf, cp, 0) // lea r10, [rip+0] grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_ca) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_ca) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gv_current_arena) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 // mov qword [r10], -1, REX.WB + 0xC7 /0 cp = cp + emit_rex(buf, cp, 1, 0, 0, 10/8) @@ -82,8 +82,8 @@ fn emit_start(buf: string, pos: int) -> int: loop: if gi0 >= g_ir_global_count: break - iv := r64(g_ir_globals, gi0 * 24 + 16) - gvv0 := r64(g_ir_globals, gi0 * 24 + 8) + iv := cell_load(g_ir_globals, gi0 * 24 + 16) + gvv0 := cell_load(g_ir_globals, gi0 * 24 + 8) if iv != 0 && gvv0 >= 0: ivv : ., mut = iv if g_x86_big_enabled != 0 && irv_type(gvv0) == TI_INT: @@ -93,8 +93,8 @@ fn emit_start(buf: string, pos: int) -> int: rip_pos_g := cp + 3 cp = cp + e2_lr(buf, cp, 0) grow_rip_patch(g_x86_rip_patch_count + 1) - w64(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_g) - w64(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gvv0) + cell_store(g_x86_rip_patch_pos, g_x86_rip_patch_count * 8, rip_pos_g) + cell_store(g_x86_rip_patch_globals, g_x86_rip_patch_count * 8, gvv0) g_x86_rip_patch_count = g_x86_rip_patch_count + 1 if ivv >= -2147483647 - 1 && ivv <= 2147483647: // mov qword [r10], imm32 (sign-extended), 49 C7 02 imm32 @@ -161,8 +161,8 @@ fn emit_start_size() -> int: loop: if gi0s >= g_ir_global_count: break - ivs := r64(g_ir_globals, gi0s * 24 + 16) - gvvs := r64(g_ir_globals, gi0s * 24 + 8) + ivs := cell_load(g_ir_globals, gi0s * 24 + 16) + gvvs := cell_load(g_ir_globals, gi0s * 24 + 8) if ivs != 0 && gvvs >= 0: if ivs >= -2147483647 - 1 && ivs <= 2147483647: sz = sz + 14 diff --git a/src/runtime/bigint.cr b/src/runtime/bigint.cr index a780a416..9413f7ea 100644 --- a/src/runtime/bigint.cr +++ b/src/runtime/bigint.cr @@ -63,22 +63,22 @@ fn bi_new(n: int) -> string: return alloc(BI_DIGITS + n * 8) fn bi_sign_of(p: string) -> int: - return r64(p, BI_SIGN) + return cell_load(p, BI_SIGN) fn bi_len_of(p: string) -> int: - return r64(p, BI_LEN) + return cell_load(p, BI_LEN) fn bi_digit(p: string, i: int) -> int: - return r64(p, BI_DIGITS + i * 8) + return cell_load(p, BI_DIGITS + i * 8) fn bi_set_sign(p: string, s: int): - w64(p, BI_SIGN, s) + cell_store(p, BI_SIGN, s) fn bi_set_len(p: string, n: int): - w64(p, BI_LEN, n) + cell_store(p, BI_LEN, n) fn bi_set_digit(p: string, i: int, d: int): - w64(p, BI_DIGITS + i * 8, d) + cell_store(p, BI_DIGITS + i * 8, d) fn bi_normalize(p: string) -> string: n := bi_len_of(p) diff --git a/src/stdlib/arena.cr b/src/stdlib/arena.cr index b5bccac2..e6331407 100644 --- a/src/stdlib/arena.cr +++ b/src/stdlib/arena.cr @@ -75,7 +75,7 @@ fn arena_new() -> int: // Try free list first if g_arena_free_list >= 0: ai = g_arena_free_list - g_arena_free_list = r64(g_arena_parents, ai * 8) // pop head + g_arena_free_list = cell_load(g_arena_parents, ai * 8) // pop head if ai < 0: // Allocate new slot (grow metadata arrays) @@ -85,13 +85,13 @@ fn arena_new() -> int: g_arena_count = need // Initialize slot - w64(g_arena_cursors, ai * 8, 0) - w64(g_arena_sizes, ai * 8, g_arena_max_size) - w64(g_arena_parents, ai * 8, -1) + cell_store(g_arena_cursors, ai * 8, 0) + cell_store(g_arena_sizes, ai * 8, g_arena_max_size) + cell_store(g_arena_parents, ai * 8, -1) // Set parent = previous current arena if g_current_arena >= 0: - w64(g_arena_parents, ai * 8, g_current_arena) + cell_store(g_arena_parents, ai * 8, g_current_arena) // Activate g_current_arena = ai @@ -102,10 +102,10 @@ fn arena_reset(ai: int): return // Save parent BEFORE free list overwrites g_arena_parents[ai] - parent := r64(g_arena_parents, ai * 8) + parent := cell_load(g_arena_parents, ai * 8) // Push to free list - w64(g_arena_parents, ai * 8, g_arena_free_list) + cell_store(g_arena_parents, ai * 8, g_arena_free_list) g_arena_free_list = ai // Restore parent @@ -117,6 +117,6 @@ fn arena_reset(ai: int): fn arena_avail(ai: int) -> int: if ai < 0 || ai >= g_arena_count: return 0 - cursor := r64(g_arena_cursors, ai * 8) - limit := r64(g_arena_sizes, ai * 8) + cursor := cell_load(g_arena_cursors, ai * 8) + limit := cell_load(g_arena_sizes, ai * 8) return limit - cursor diff --git a/src/stdlib/chan.cr b/src/stdlib/chan.cr index 304ea7fb..6e07d3dd 100644 --- a/src/stdlib/chan.cr +++ b/src/stdlib/chan.cr @@ -32,58 +32,58 @@ fn chan_make(elemsize: int, cap: int) -> string: _chan_ensure_init() ch := alloc(64) buf := alloc(cap * elemsize) - w64(ch, 0, buf) // buf - w64(ch, 8, cap) // cap - w64(ch, 16, 0) // len - w64(ch, 24, 0) // head - w64(ch, 32, elemsize) // elemsize - w64(ch, 40, -1) // send_wait = empty - w64(ch, 48, -1) // recv_wait = empty - w64(ch, 56, 0) // closed = false + cell_store(ch, 0, buf) // buf + cell_store(ch, 8, cap) // cap + cell_store(ch, 16, 0) // len + cell_store(ch, 24, 0) // head + cell_store(ch, 32, elemsize) // elemsize + cell_store(ch, 40, -1) // send_wait = empty + cell_store(ch, 48, -1) // recv_wait = empty + cell_store(ch, 56, 0) // closed = false return ch fn chan_send(ch: string, val: int): _chan_ensure_init() - buf := r64(ch, 0) - cap := r64(ch, 8) - len := r64(ch, 16) - head := r64(ch, 24) - esize := r64(ch, 32) + buf := cell_load(ch, 0) + cap := cell_load(ch, 8) + len := cell_load(ch, 16) + head := cell_load(ch, 24) + esize := cell_load(ch, 32) // Check for waiting receivers first (direct handoff) - recv_wait := r64(ch, 48) + recv_wait := cell_load(ch, 48) if recv_wait >= 0: // Direct handoff: copy value to the waiting receiver's temp_val - nxt := r64(recv_wait, 48) // save next before overwriting - w64(recv_wait, 72, val) // store value in receiver's temp_val - w64(recv_wait, 8, 0) // set to _Grunnable + nxt := cell_load(recv_wait, 48) // save next before overwriting + cell_store(recv_wait, 72, val) // store value in receiver's temp_val + cell_store(recv_wait, 8, 0) // set to _Grunnable sched_enqueue(recv_wait) - w64(ch, 48, nxt) // remove from recv_wait list + cell_store(ch, 48, nxt) // remove from recv_wait list return if len < cap: // Buffer has space tail := (head + len) % cap - w64(buf, tail * esize, val) - w64(ch, 16, len + 1) + cell_store(buf, tail * esize, val) + cell_store(ch, 16, len + 1) return // Buffer full: block on send_wait list g := sched_get_curg() if g >= 0: - w64(g, 72, val) // store value for later delivery (temp_val) - w64(g, 8, 2) // _Gwaiting - w64(g, 48, -1) // next = -1 + cell_store(g, 72, val) // store value for later delivery (temp_val) + cell_store(g, 8, 2) // _Gwaiting + cell_store(g, 48, -1) // next = -1 // Add to channel's send_wait list - sw := r64(ch, 40) + sw := cell_load(ch, 40) if sw < 0: - w64(ch, 40, g) + cell_store(ch, 40, g) else: loop: - nxt := r64(sw, 48) + nxt := cell_load(sw, 48) if nxt < 0: - w64(sw, 48, g) + cell_store(sw, 48, g) break sw = nxt @@ -92,29 +92,29 @@ fn chan_send(ch: string, val: int): fn chan_recv(ch: string) -> int: _chan_ensure_init() - buf := r64(ch, 0) - cap := r64(ch, 8) - len := r64(ch, 16) - head := r64(ch, 24) - esize := r64(ch, 32) - closed := r64(ch, 56) + buf := cell_load(ch, 0) + cap := cell_load(ch, 8) + len := cell_load(ch, 16) + head := cell_load(ch, 24) + esize := cell_load(ch, 32) + closed := cell_load(ch, 56) // Check for waiting senders first (direct handoff) - send_wait := r64(ch, 40) + send_wait := cell_load(ch, 40) if send_wait >= 0: // Direct handoff: take value from the waiting sender's temp_val - val := r64(send_wait, 72) // read sender's stored value - nxt := r64(send_wait, 48) - w64(send_wait, 8, 0) // set to _Grunnable + val := cell_load(send_wait, 72) // read sender's stored value + nxt := cell_load(send_wait, 48) + cell_store(send_wait, 8, 0) // set to _Grunnable sched_enqueue(send_wait) - w64(ch, 40, nxt) // remove from send_wait list + cell_store(ch, 40, nxt) // remove from send_wait list return val if len > 0: // Buffer has data - val := r64(buf, head * esize) - w64(ch, 24, (head + 1) % cap) - w64(ch, 16, len - 1) + val := cell_load(buf, head * esize) + cell_store(ch, 24, (head + 1) % cap) + cell_store(ch, 16, len - 1) return val if closed != 0: @@ -123,48 +123,48 @@ fn chan_recv(ch: string) -> int: // Buffer empty: block on recv_wait list g := sched_get_curg() if g >= 0: - w64(g, 8, 2) // _Gwaiting - w64(g, 48, -1) // next = -1 + cell_store(g, 8, 2) // _Gwaiting + cell_store(g, 48, -1) // next = -1 - rw := r64(ch, 48) + rw := cell_load(ch, 48) if rw < 0: - w64(ch, 48, g) + cell_store(ch, 48, g) else: loop: - nxt := r64(rw, 48) + nxt := cell_load(rw, 48) if nxt < 0: - w64(rw, 48, g) + cell_store(rw, 48, g) break rw = nxt sched_yield() // After wakeup, value should be in this G's temp_val (delivered by sender) - val := r64(g, 72) + val := cell_load(g, 72) return val fn chan_close(ch: string): _chan_ensure_init() - w64(ch, 56, 1) // closed = true + cell_store(ch, 56, 1) // closed = true // Wake up all waiting senders - sw := r64(ch, 40) + sw := cell_load(ch, 40) loop: if sw < 0: break - nxt := r64(sw, 48) - w64(sw, 8, 0) // _Grunnable + nxt := cell_load(sw, 48) + cell_store(sw, 8, 0) // _Grunnable sched_enqueue(sw) sw = nxt - w64(ch, 40, -1) // clear send_wait + cell_store(ch, 40, -1) // clear send_wait // Wake up all waiting receivers - rw := r64(ch, 48) + rw := cell_load(ch, 48) loop: if rw < 0: break - nxt := r64(rw, 48) - w64(rw, 8, 0) // _Grunnable + nxt := cell_load(rw, 48) + cell_store(rw, 8, 0) // _Grunnable sched_enqueue(rw) rw = nxt - w64(ch, 48, -1) // clear recv_wait + cell_store(ch, 48, -1) // clear recv_wait diff --git a/src/stdlib/cli.cr b/src/stdlib/cli.cr index 71ac2d1c..55e18bbd 100644 --- a/src/stdlib/cli.cr +++ b/src/stdlib/cli.cr @@ -111,60 +111,60 @@ fn cli_cmd(name: string, desc: string): // Flat table with 16-byte records: name and description. String values are // stored as 64-bit words, so access them through @ptr_of inside unsafe. unsafe: - w64(g_cli_cmds, off, @ptr_of(name)) - w64(g_cli_cmds, off + 8, @ptr_of(desc)) + cell_store(g_cli_cmds, off, @ptr_of(name)) + cell_store(g_cli_cmds, off + 8, @ptr_of(desc)) g_cli_cmd_count = g_cli_cmd_count + 1 fn cli_flag(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) off : ., mut = g_cli_flag_count * 56 unsafe: - w64(g_cli_flags, off, @ptr_of(long_name)) - w64(g_cli_flags, off + 8, @ptr_of(short_name)) - w64(g_cli_flags, off + 16, @ptr_of(desc)) - w64(g_cli_flags, off + 24, @ptr_of("")) // value - w64(g_cli_flags, off + 32, 0) // has_value - w64(g_cli_flags, off + 40, 0) // is_bool - w64(g_cli_flags, off + 48, 0) // debug + cell_store(g_cli_flags, off, @ptr_of(long_name)) + cell_store(g_cli_flags, off + 8, @ptr_of(short_name)) + cell_store(g_cli_flags, off + 16, @ptr_of(desc)) + cell_store(g_cli_flags, off + 24, @ptr_of("")) // value + cell_store(g_cli_flags, off + 32, 0) // has_value + cell_store(g_cli_flags, off + 40, 0) // is_bool + cell_store(g_cli_flags, off + 48, 0) // debug g_cli_flag_count = g_cli_flag_count + 1 fn cli_flag_bool(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) off : ., mut = g_cli_flag_count * 56 unsafe: - w64(g_cli_flags, off, @ptr_of(long_name)) - w64(g_cli_flags, off + 8, @ptr_of(short_name)) - w64(g_cli_flags, off + 16, @ptr_of(desc)) - w64(g_cli_flags, off + 24, @ptr_of("")) // value - w64(g_cli_flags, off + 32, 0) // has_value - w64(g_cli_flags, off + 40, 1) // is_bool - w64(g_cli_flags, off + 48, 0) // debug + cell_store(g_cli_flags, off, @ptr_of(long_name)) + cell_store(g_cli_flags, off + 8, @ptr_of(short_name)) + cell_store(g_cli_flags, off + 16, @ptr_of(desc)) + cell_store(g_cli_flags, off + 24, @ptr_of("")) // value + cell_store(g_cli_flags, off + 32, 0) // has_value + cell_store(g_cli_flags, off + 40, 1) // is_bool + cell_store(g_cli_flags, off + 48, 0) // debug g_cli_flag_count = g_cli_flag_count + 1 fn cli_flag_bool_debug(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) off : ., mut = g_cli_flag_count * 56 unsafe: - w64(g_cli_flags, off, @ptr_of(long_name)) - w64(g_cli_flags, off + 8, @ptr_of(short_name)) - w64(g_cli_flags, off + 16, @ptr_of(desc)) - w64(g_cli_flags, off + 24, @ptr_of("")) // value - w64(g_cli_flags, off + 32, 0) // has_value - w64(g_cli_flags, off + 40, 1) // is_bool - w64(g_cli_flags, off + 48, 1) // debug + cell_store(g_cli_flags, off, @ptr_of(long_name)) + cell_store(g_cli_flags, off + 8, @ptr_of(short_name)) + cell_store(g_cli_flags, off + 16, @ptr_of(desc)) + cell_store(g_cli_flags, off + 24, @ptr_of("")) // value + cell_store(g_cli_flags, off + 32, 0) // has_value + cell_store(g_cli_flags, off + 40, 1) // is_bool + cell_store(g_cli_flags, off + 48, 1) // debug g_cli_flag_count = g_cli_flag_count + 1 fn cli_flag_debug(long_name: string, short_name: string, desc: string): grow_cli_flags(g_cli_flag_count + 1) off : ., mut = g_cli_flag_count * 56 unsafe: - w64(g_cli_flags, off, @ptr_of(long_name)) - w64(g_cli_flags, off + 8, @ptr_of(short_name)) - w64(g_cli_flags, off + 16, @ptr_of(desc)) - w64(g_cli_flags, off + 24, @ptr_of("")) // value - w64(g_cli_flags, off + 32, 0) // has_value - w64(g_cli_flags, off + 40, 0) // is_bool - w64(g_cli_flags, off + 48, 1) // debug + cell_store(g_cli_flags, off, @ptr_of(long_name)) + cell_store(g_cli_flags, off + 8, @ptr_of(short_name)) + cell_store(g_cli_flags, off + 16, @ptr_of(desc)) + cell_store(g_cli_flags, off + 24, @ptr_of("")) // value + cell_store(g_cli_flags, off + 32, 0) // has_value + cell_store(g_cli_flags, off + 40, 0) // is_bool + cell_store(g_cli_flags, off + 48, 1) // debug g_cli_flag_count = g_cli_flag_count + 1 // --- Internal helpers --- @@ -176,8 +176,8 @@ fn _cli_find_flag(name: string) -> int: break // Read a string from a 64-bit word through @str_of inside unsafe. unsafe: - ln := @str_of(r64(g_cli_flags, i * 56)) - sn := @str_of(r64(g_cli_flags, i * 56 + 8)) + ln := @str_of(cell_load(g_cli_flags, i * 56)) + sn := @str_of(cell_load(g_cli_flags, i * 56 + 8)) if str_len(ln) > 0 && str_eq(ln, name) != 0: return i if str_len(sn) > 0 && str_eq(sn, name) != 0: @@ -230,7 +230,7 @@ fn cli_parse() -> int: if ci >= g_cli_cmd_count: break if unsafe: - str_eq(@str_of(r64(g_cli_cmds, ci * 16)), first) + str_eq(@str_of(cell_load(g_cli_cmds, ci * 16)), first) != 0: found_cmd = 1 break @@ -273,11 +273,11 @@ fn cli_parse() -> int: println(arg) cli_help() return -1 - if r64(g_cli_flags, fi * 56 + 40) != 0: + if cell_load(g_cli_flags, fi * 56 + 40) != 0: // is_bool - w64(g_cli_flags, fi * 56 + 32, 1) // has_value + cell_store(g_cli_flags, fi * 56 + 32, 1) // has_value unsafe: - w64(g_cli_flags, fi * 56 + 24, @ptr_of("1")) + cell_store(g_cli_flags, fi * 56 + 24, @ptr_of("1")) // value(串字入裸字槽) else: ai = ai + 1 @@ -288,14 +288,14 @@ fn cli_parse() -> int: return -1 val := get_arg(ai) unsafe: - w64(g_cli_flags, fi * 56 + 24, @ptr_of(val)) + cell_store(g_cli_flags, fi * 56 + 24, @ptr_of(val)) // value - w64(g_cli_flags, fi * 56 + 32, 1) // has_value + cell_store(g_cli_flags, fi * 56 + 32, 1) // has_value else: // Positional argument grow_cli_args(g_cli_arg_count + 1) unsafe: - w64(g_cli_args, g_cli_arg_count * 8, @ptr_of(arg)) + cell_store(g_cli_args, g_cli_arg_count * 8, @ptr_of(arg)) g_cli_arg_count = g_cli_arg_count + 1 ai = ai + 1 @@ -311,25 +311,25 @@ fn cli_eq(cmd: string, expected: string) -> int: fn cli_get(name: string) -> string: fi := _cli_find_flag(name) - if fi >= 0 && r64(g_cli_flags, fi * 56 + 32) != 0: + if fi >= 0 && cell_load(g_cli_flags, fi * 56 + 32) != 0: // has_value // The return value is a string stored in a raw word; read it through // @str_of inside unsafe. unsafe: - return @str_of(r64(g_cli_flags, fi * 56 + 24)) + return @str_of(cell_load(g_cli_flags, fi * 56 + 24)) // value return "" fn cli_has(name: string) -> int: fi := _cli_find_flag(name) if fi >= 0: - return r64(g_cli_flags, fi * 56 + 32) // has_value + return cell_load(g_cli_flags, fi * 56 + 32) // has_value return 0 fn cli_arg(n: int) -> string: if n >= 0 && n < g_cli_arg_count: unsafe: - return @str_of(r64(g_cli_args, n * 8)) + return @str_of(cell_load(g_cli_args, n * 8)) // The return value is a string stored in a raw word. return "" @@ -344,11 +344,11 @@ fn cli_print_flags(debug_only: int): if fi >= g_cli_flag_count: break unsafe: - f_debug := r64(g_cli_flags, fi * 56 + 48) + f_debug := cell_load(g_cli_flags, fi * 56 + 48) if f_debug == debug_only: - f_short := @str_of(r64(g_cli_flags, fi * 56 + 8)) - f_long := @str_of(r64(g_cli_flags, fi * 56)) - f_desc := @str_of(r64(g_cli_flags, fi * 56 + 16)) + f_short := @str_of(cell_load(g_cli_flags, fi * 56 + 8)) + f_long := @str_of(cell_load(g_cli_flags, fi * 56)) + f_desc := @str_of(cell_load(g_cli_flags, fi * 56 + 16)) if str_len(f_desc) > 0: print(" ") if str_len(f_short) > 0: @@ -388,7 +388,7 @@ fn cli_help(): if first == 0: print(",") cmd_name_str := unsafe: - @str_of(r64(g_cli_cmds, ci * 16)) + @str_of(cell_load(g_cli_cmds, ci * 16)) print(cmd_name_str) first = 0 ci = ci + 1 @@ -412,7 +412,7 @@ fn cli_help(): if first == 0: print(",") cmd_name_str := unsafe: - @str_of(r64(g_cli_cmds, ci * 16)) + @str_of(cell_load(g_cli_cmds, ci * 16)) print(cmd_name_str) first = 0 ci = ci + 1 @@ -422,8 +422,8 @@ fn cli_help(): if ci >= g_cli_cmd_count: break unsafe: - cmd_name := @str_of(r64(g_cli_cmds, ci * 16)) - cmd_desc := @str_of(r64(g_cli_cmds, ci * 16 + 8)) + cmd_name := @str_of(cell_load(g_cli_cmds, ci * 16)) + cmd_desc := @str_of(cell_load(g_cli_cmds, ci * 16 + 8)) print(" ") print(cmd_name) pad := str_len(cmd_name) diff --git a/src/stdlib/goroutine.cr b/src/stdlib/goroutine.cr index 23ce5fd0..892c3cac 100644 --- a/src/stdlib/goroutine.cr +++ b/src/stdlib/goroutine.cr @@ -51,22 +51,22 @@ fn g_new(entry_fn: int, arg: int, arg_type: int) -> string: g := alloc(80) id := g_next_id g_next_id = g_next_id + 1 - w64(g, 0, id) // id - w64(g, 8, 0) // _Grunnable - w64(g, 16, sp) // stack pointer - w64(g, 24, stack) // stack_lo - w64(g, 32, aid) // arena_id - w64(g, 40, 0) // result_ch = 0 (default) - w64(g, 48, -1) // next = -1 - w64(g, 56, entry_fn) // saved_fn = entry function ptr - w64(g, 64, arg) // saved_arg - w64(g, 72, 0) // temp_val = 0 + cell_store(g, 0, id) // id + cell_store(g, 8, 0) // _Grunnable + cell_store(g, 16, sp) // stack pointer + cell_store(g, 24, stack) // stack_lo + cell_store(g, 32, aid) // arena_id + cell_store(g, 40, 0) // result_ch = 0 (default) + cell_store(g, 48, -1) // next = -1 + cell_store(g, 56, entry_fn) // saved_fn = entry function ptr + cell_store(g, 64, arg) // saved_arg + cell_store(g, 72, 0) // temp_val = 0 return g fn g_free(g: string): // Reset arena (reclaims the fiber stack + G struct, which live in it) - aid := r64(g, 32) + aid := cell_load(g, 32) arena_reset(aid) // Mark G dead so it is never re-enqueued (status 3 = _Gdead) - w64(g, 8, 3) + cell_store(g, 8, 3) diff --git a/src/stdlib/hotpatch.cr b/src/stdlib/hotpatch.cr index 1b65ec92..1ec10109 100644 --- a/src/stdlib/hotpatch.cr +++ b/src/stdlib/hotpatch.cr @@ -18,15 +18,15 @@ fn hp_load_config(): // Increment the in-flight counter for a given function version. // fn_ver: 0-based index into the 64-slot in-flight buffer. fn hp_inflight_inc(fn_ver: int): - cur := r64(g_hp_inflight, fn_ver * 8) - w64(g_hp_inflight, fn_ver * 8, cur + 1) + cur := cell_load(g_hp_inflight, fn_ver * 8) + cell_store(g_hp_inflight, fn_ver * 8, cur + 1) // Decrement the in-flight counter for a given function version. // Saturates at 0 (never goes negative). fn hp_inflight_dec(fn_ver: int): - cur := r64(g_hp_inflight, fn_ver * 8) + cur := cell_load(g_hp_inflight, fn_ver * 8) if cur > 0: - w64(g_hp_inflight, fn_ver * 8, cur - 1) + cell_store(g_hp_inflight, fn_ver * 8, cur - 1) // Initialize hotpatch system at startup. // Allocates the in-flight counter buffer and loads the initial config. @@ -38,6 +38,6 @@ fn hp_init(): loop: if i >= 64: break - w64(g_hp_inflight, i * 8, 0) + cell_store(g_hp_inflight, i * 8, 0) i = i + 1 hp_load_config() diff --git a/src/stdlib/sched.cr b/src/stdlib/sched.cr index 5922c72d..0cba066e 100644 --- a/src/stdlib/sched.cr +++ b/src/stdlib/sched.cr @@ -27,10 +27,10 @@ fn sched_init(): // Initialize M workers (one per CPU) // For now: single-threaded (1 M, cooperative) m := alloc(32) - w64(m, 0, 0) // id = 0 - w64(m, 8, -1) // cur_g = none - w64(m, 16, -1) // runq_head = empty - w64(m, 24, -1) // runq_tail = empty + cell_store(m, 0, 0) // id = 0 + cell_store(m, 8, -1) // cur_g = none + cell_store(m, 16, -1) // runq_head = empty + cell_store(m, 24, -1) // runq_tail = empty g_machines = m g_machine_count = 1 g_current_m_idx = 0 @@ -39,17 +39,17 @@ fn sched_init(): // Its stack_ptr is filled in by fiber_switch the first time it yields // (fiber_switch saves the current RSP into [G+16]). main_g := alloc(80) - w64(main_g, 0, 0) // id = 0 - w64(main_g, 8, 1) // _Grunning - w64(main_g, 16, 0) // stack_ptr (filled on first yield) - w64(main_g, 24, 0) // stack_lo (thread stack — no arena) - w64(main_g, 32, -1) // arena_id = none - w64(main_g, 40, -1) // result_ch = none (never dispatched by a wrapper) - w64(main_g, 48, -1) // next = -1 - w64(main_g, 56, 0) // saved_fn = 0 (never dispatched by a wrapper) - w64(main_g, 64, 0) // saved_arg = 0 - w64(main_g, 72, 0) // temp_val = 0 - w64(m, 8, main_g) // M[0].cur_g = main_g + cell_store(main_g, 0, 0) // id = 0 + cell_store(main_g, 8, 1) // _Grunning + cell_store(main_g, 16, 0) // stack_ptr (filled on first yield) + cell_store(main_g, 24, 0) // stack_lo (thread stack — no arena) + cell_store(main_g, 32, -1) // arena_id = none + cell_store(main_g, 40, -1) // result_ch = none (never dispatched by a wrapper) + cell_store(main_g, 48, -1) // next = -1 + cell_store(main_g, 56, 0) // saved_fn = 0 (never dispatched by a wrapper) + cell_store(main_g, 64, 0) // saved_arg = 0 + cell_store(main_g, 72, 0) // temp_val = 0 + cell_store(m, 8, main_g) // M[0].cur_g = main_g g_set_curg(main_g) g_sched_inited = 1 @@ -67,27 +67,27 @@ fn sched_enqueue(g: string): if m_idx < 0: m_idx = 0 m := g_machines + m_idx * 32 - head := r64(m, 16) + head := cell_load(m, 16) if head < 0: - w64(m, 16, g) // runq_head = g - w64(m, 24, g) // runq_tail = g + cell_store(m, 16, g) // runq_head = g + cell_store(m, 24, g) // runq_tail = g else: - tail := r64(m, 24) - w64(tail, 48, g) // tail.next = g - w64(m, 24, g) // runq_tail = g - w64(g, 48, -1) + tail := cell_load(m, 24) + cell_store(tail, 48, g) // tail.next = g + cell_store(m, 24, g) // runq_tail = g + cell_store(g, 48, -1) fn sched_dequeue(m_idx: int) -> int: if m_idx < 0: m_idx = 0 m := g_machines + m_idx * 32 - head := r64(m, 16) + head := cell_load(m, 16) if head < 0: return -1 - w64(m, 16, r64(head, 48)) - if r64(m, 16) < 0: - w64(m, 24, -1) - w64(head, 48, -1) + cell_store(m, 16, cell_load(head, 48)) + if cell_load(m, 16) < 0: + cell_store(m, 24, -1) + cell_store(head, 48, -1) return head fn sched_get_curg() -> int: @@ -102,10 +102,10 @@ fn sched_yield(): // sender/receiver handoff wakes them — re-enqueueing would double-schedule. // Dead (_Gdead) Gs must never be re-enqueued (goroutine exit path). if cur_g >= 0: - st := r64(cur_g, 8) + st := cell_load(cur_g, 8) if st != 2 && st != 3: // not _Gwaiting, not _Gdead - w64(cur_g, 8, 0) // _Grunnable + cell_store(cur_g, 8, 0) // _Grunnable sched_enqueue(cur_g) // Schedule next @@ -119,15 +119,15 @@ fn sched_schedule(): m_idx := sched_current_m_idx() next_g := sched_dequeue(m_idx) if next_g >= 0: - old_g := r64(g_machines, m_idx * 32 + 8) - w64(g_machines, m_idx * 32 + 8, next_g) + old_g := cell_load(g_machines, m_idx * 32 + 8) + cell_store(g_machines, m_idx * 32 + 8, next_g) g_set_curg(next_g) - w64(next_g, 8, 1) // _Grunning + cell_store(next_g, 8, 1) // _Grunning if old_g >= 0: // Save old_g's sp, switch to next_g's sp old_sp_addr := old_g + 16 // &g.stack_ptr - next_sp := r64(next_g, 16) // g.stack_ptr + next_sp := cell_load(next_g, 16) // g.stack_ptr fiber_switch(old_sp_addr, next_sp) // else: first goroutine, just start it @@ -135,20 +135,20 @@ fn sched_schedule(): // Each worker dequeues and runs goroutines from ITS OWN M's local queue. fn sched_worker_run(m_idx: int): // Store this worker's index in its M struct - w64(g_machines, m_idx * 32, m_idx) // m.id = m_idx + cell_store(g_machines, m_idx * 32, m_idx) // m.id = m_idx g_current_m_idx = m_idx loop: next_g := sched_dequeue(m_idx) if next_g >= 0: - old_g := r64(g_machines, m_idx * 32 + 8) - w64(g_machines, m_idx * 32 + 8, next_g) + old_g := cell_load(g_machines, m_idx * 32 + 8) + cell_store(g_machines, m_idx * 32 + 8, next_g) g_set_curg(next_g) - w64(next_g, 8, 1) // _Grunning + cell_store(next_g, 8, 1) // _Grunning if old_g >= 0: old_sp_addr := old_g + 16 - next_sp := r64(next_g, 16) + next_sp := cell_load(next_g, 16) fiber_switch(old_sp_addr, next_sp) // else: first goroutine on this M, start it // If no work, yield to OS @@ -166,7 +166,7 @@ fn sched_go(fn_ptr: int, arg: int) -> string: g := g_new(fn_ptr, arg, 0) // Store result channel in G struct (offset 40) - w64(g, 40, ch) + cell_store(g, 40, ch) // Enqueue goroutine to the scheduler sched_enqueue(g) @@ -183,10 +183,10 @@ fn sched_spawn_workers(n: int): loop: if mi >= n: break - w64(total_ms, mi * 32, mi) // m.id - w64(total_ms, mi * 32 + 8, -1) // m.cur_g = none - w64(total_ms, mi * 32 + 16, -1) // runq_head - w64(total_ms, mi * 32 + 24, -1) // runq_tail + cell_store(total_ms, mi * 32, mi) // m.id + cell_store(total_ms, mi * 32 + 8, -1) // m.cur_g = none + cell_store(total_ms, mi * 32 + 16, -1) // runq_head + cell_store(total_ms, mi * 32 + 24, -1) // runq_tail mi = mi + 1 g_machines = total_ms g_machine_count = n diff --git a/src/stdlib/scheduler.cr b/src/stdlib/scheduler.cr index 5939286a..33befed0 100644 --- a/src/stdlib/scheduler.cr +++ b/src/stdlib/scheduler.cr @@ -76,21 +76,21 @@ fn runq_push(fi: int): loop: if ci >= g_runq_count: break - w64(nb, ci * 8, r64(g_runq, pos * 8)) + cell_store(nb, ci * 8, cell_load(g_runq, pos * 8)) pos = (pos + 1) % g_runq_cap ci = ci + 1 g_runq = nb g_runq_head = 0 g_runq_tail = g_runq_count g_runq_cap = nc - w64(g_runq, g_runq_tail * 8, fi) + cell_store(g_runq, g_runq_tail * 8, fi) g_runq_tail = (g_runq_tail + 1) % g_runq_cap g_runq_count = g_runq_count + 1 fn runq_pop() -> int: if g_runq_count <= 0: return -1 - fi := r64(g_runq, g_runq_head * 8) + fi := cell_load(g_runq, g_runq_head * 8) g_runq_head = (g_runq_head + 1) % g_runq_cap g_runq_count = g_runq_count - 1 return fi @@ -100,10 +100,10 @@ fn fiber_spawn(fn_idx: int) -> int: loop: if i >= g_fiber_count: break - if r64(g_fibers, i * 32 + 16) == FIBER_FREE: - w64(g_fibers, i * 32, fn_idx) - w64(g_fibers, i * 32 + 8, arena.arena_new()) - w64(g_fibers, i * 32 + 16, FIBER_RUNNABLE) + if cell_load(g_fibers, i * 32 + 16) == FIBER_FREE: + cell_store(g_fibers, i * 32, fn_idx) + cell_store(g_fibers, i * 32 + 8, arena.arena_new()) + cell_store(g_fibers, i * 32 + 16, FIBER_RUNNABLE) runq_push(i) return i i = i + 1 @@ -116,10 +116,10 @@ fn fiber_spawn(fn_idx: int) -> int: fi := g_fiber_count g_fiber_count = g_fiber_count + 1 off := fi * 32 - w64(g_fibers, off, fn_idx) - w64(g_fibers, off + 8, arena.arena_new()) - w64(g_fibers, off + 16, FIBER_RUNNABLE) - w64(g_fibers, off + 24, 0) + cell_store(g_fibers, off, fn_idx) + cell_store(g_fibers, off + 8, arena.arena_new()) + cell_store(g_fibers, off + 16, FIBER_RUNNABLE) + cell_store(g_fibers, off + 24, 0) runq_push(fi) return fi @@ -127,33 +127,33 @@ fn fiber_yield(): if g_cur_fiber < 0: return off := g_cur_fiber * 32 - if r64(g_fibers, off + 16) == FIBER_RUNNABLE: + if cell_load(g_fibers, off + 16) == FIBER_RUNNABLE: runq_push(g_cur_fiber) fn fiber_wait(target: int): if g_cur_fiber < 0: return off := g_cur_fiber * 32 - w64(g_fibers, off + 16, FIBER_WAITING) - w64(g_fibers, off + 24, target) + cell_store(g_fibers, off + 16, FIBER_WAITING) + cell_store(g_fibers, off + 24, target) fn fiber_wake(fi: int): if fi < 0: return off := fi * 32 - if r64(g_fibers, off + 16) == FIBER_WAITING: - w64(g_fibers, off + 16, FIBER_RUNNABLE) - w64(g_fibers, off + 24, 0) + if cell_load(g_fibers, off + 16) == FIBER_WAITING: + cell_store(g_fibers, off + 16, FIBER_RUNNABLE) + cell_store(g_fibers, off + 24, 0) runq_push(fi) fn fiber_done(fi: int): if fi < 0 || fi >= g_fiber_count: return off := fi * 32 - ai := r64(g_fibers, off + 8) + ai := cell_load(g_fibers, off + 8) if ai >= 0: arena.arena_reset(ai) - w64(g_fibers, off + 16, FIBER_DONE) + cell_store(g_fibers, off + 16, FIBER_DONE) fn sched_run(): loop: @@ -162,7 +162,7 @@ fn sched_run(): break g_cur_fiber = fi off := fi * 32 - if r64(g_fibers, off + 16) != FIBER_RUNNABLE: + if cell_load(g_fibers, off + 16) != FIBER_RUNNABLE: continue // Placeholder: fibers complete immediately in interpreter mode. // Native backend (corearch) will call the actual function pointer diff --git a/src/stdlib/variadic.cr b/src/stdlib/variadic.cr index 943cfca0..ce891e85 100644 --- a/src/stdlib/variadic.cr +++ b/src/stdlib/variadic.cr @@ -16,13 +16,13 @@ fn va_count(pack: string) -> int: if str_len(pack) < 8: return 0 - return r64(pack, 0) + return cell_load(pack, 0) // 变参包取值:返回第 n 个参数的 IR 变量 fn va_arg(pack: string, n: int) -> int: if str_len(pack) < (n + 1) * 8 + 8: return -1 - return r64(pack, (n + 1) * 8) + return cell_load(pack, (n + 1) * 8) // println is print plus a newline; detect the "ln" suffix. fn va_fn_has_ln(fn_name: string) -> bool: diff --git a/src/targets/x86_64/linux/elf/main.cr b/src/targets/x86_64/linux/elf/main.cr index a81299d8..90dcc9b3 100644 --- a/src/targets/x86_64/linux/elf/main.cr +++ b/src/targets/x86_64/linux/elf/main.cr @@ -205,7 +205,7 @@ fn corearch_main() -> int: loop: if rpi >= g_x86_rip_patch_count: break - ppos := r64(g_x86_rip_patch_pos, rpi * 8) + ppos := cell_load(g_x86_rip_patch_pos, rpi * 8) if ppos >= 176 && ppos - 176 + 4 <= cs: w32(cd, ppos - 176, 0) w8(cd, ppos + 4 - 176, 144) @@ -230,7 +230,7 @@ fn corearch_main() -> int: loop: if ri >= g_x86_ext_rel_count: break - fn_name := istr_get(r64(g_x86_ext_rel_name, ri * 8)) + fn_name := istr_get(cell_load(g_x86_ext_rel_name, ri * 8)) ctx_add_plt(fn_name, 0) ri = ri + 1 ctx_set_user_code(cd, cs) diff --git a/tests/selfhost/test_bigint_boundaries.py b/tests/selfhost/test_bigint_boundaries.py index 48498ab7..293d6e7e 100644 --- a/tests/selfhost/test_bigint_boundaries.py +++ b/tests/selfhost/test_bigint_boundaries.py @@ -13,13 +13,13 @@ VALID = r""" fn main() -> int: buf := alloc(32) - w64(buf, 0, 42) - if r64(buf, 0) != 42: + cell_store(buf, 0, 42) + if cell_load(buf, 0) != 42: return 1 x := 4611686018427387904 - w64(buf, 8, x) - if r64(buf, 8) != x: + cell_store(buf, 8, x) + if cell_load(buf, 8) != x: return 2 store8(buf, 16, 65) @@ -63,7 +63,7 @@ fn main() -> int: buf := alloc(8) x := 1267650600228229401496703205376 - w64(buf, 0, x) + cell_store(buf, 0, x) return 0 """ diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index b9b0020e..ab6ad6e9 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -178,10 +178,10 @@ # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 canary_elf pa 826d74cf7f099f9758e11a64d443695ff8360327a88e7eb5922ddfd617e0dce5 53430 -pa_ccr pa.ccr 8b0575e0060df241a697b2e2d78e1a128eeff3515b8444f178cf127e03077f7f 96985 -pa_static_ccr pa_st.bin.ccr 667ca1f65421cc11cdc8935e070d663e2b4fa1b692e26e80dad75ace776103dd 97128 -gt_ccr gt.ccr 4df02acd1ea343601ae8a016cff093bf56d7a90f7b6b06c2811b1b3a3a2d3e41 143687 -gt_static_ccr gt_st.bin.ccr bacd9eddd956dce88df3de675a29d0857d832c5a843781707a2e84b3c864f8a1 143830 +pa_ccr pa.ccr 6ef24784e9cf043f38141c1f6bcf809d4ac5b89fd71cb74a34ff771b7a13aacb 96998 +pa_static_ccr pa_st.bin.ccr e1e7a1c1aeb8c883bb88b21be907f9fb0b1fd2c28185997adca84caf4d17bf33 97141 +gt_ccr gt.ccr 93ed0dc5cb33964df5857ba21738f6880700a5248098c3f043087e1369b26f23 143700 +gt_static_ccr gt_st.bin.ccr a9e5343389de547a0ab9a9dde33b030877c44e5505deee24eb683715b700c88f 143843 # ⚠ **本组为最终值(全部编译器源码改动落定后重测)**:重锁分量有两个,**都不是发射面泄漏**—— # ① **内容面**:`fmt.cr` 新增 `bool_str` ⇒ 凡 `import fmt` 的程序多一个函数体(ELF/`.ccr` 同增); # ② **编译器身份位移**(#123 同机制):编译器源码任何改动都会改变**其自身 intern 序** ⇒ 同语义下 @@ -292,3 +292,13 @@ gt_static_ccr gt_st.bin.ccr bacd9eddd956dce88df3de675a29d0857d832c5a843781707a2e # pa_static_ccr a7d2433b0c78428ed93e434ccd617f1308362d7bb268764712d2972e243485a1 97128 # gt_ccr 21b1f39c96bd2242b963bb5a1726eb93b912739f77c31317da055d6b37d88103 143687 # gt_static_ccr 4949ff5437e1bc78a5e2f95e4fb05c07e20da0ed4bee35c58662f19bbdc60271 143830 + +# ── 【本批重锁 2026-10-23】raw 内建改名 cell_store/cell_load ── +# 四条 `.ccr` 各 +13B,sha 全变;ELF canary 不变。 +# 归因 = 编译器侧字符串表新增 `cell_store`/`cell_load` 并移除 `w64`/`r64`; +# 用户程序语义和发射面未变。D1 5/5 PASS。 +# 旧值留痕(自本批起作废): +# pa_ccr 8b0575e0060df241a697b2e2d78e1a128eeff3515b8444f178cf127e03077f7f 96985 +# pa_static_ccr 667ca1f65421cc11cdc8935e070d663e2b4fa1b692e26e80dad75ace776103dd 97128 +# gt_ccr 4df02acd1ea343601ae8a016cff093bf56d7a90f7b6b06c2811b1b3a3a2d3e41 143687 +# gt_static_ccr bacd9eddd956dce88df3de675a29d0857d832c5a843781707a2e84b3c864f8a1 143830 From c12b656534e804de9276c95a85951738ac60bbef Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:42:12 +0800 Subject: [PATCH 39/58] docs: add ccr v10 migration implementation plan --- .../plans/2026-10-23-ccr-v10-migration.md | 577 ++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-23-ccr-v10-migration.md diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md new file mode 100644 index 00000000..c2d0676e --- /dev/null +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -0,0 +1,577 @@ +# v10 目标无关 `.ccr` 迁移 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `.ccr` 从 v9.1 槽格式迁移到 v10 格形态:格式只表达存在、版本、区域、边和语义域;前端不再计算目标布局;corearch 从 v10 + target layout 生成投影。 + +**Architecture:** 先建立独立的 v10 wire oracle 和 Core schema,再迁移 NOD/ENT 语义,最后切换读写器。v9 文件整类拒绝,不做隐式兼容。布局、宽度、字节偏移、寄存器/栈/ABI 只出现在 corearch、target model 和 `.coi`。 + +**Tech Stack:** Core 自举编译器、`src/compiler/ccr_io.cr`、`src/lattice/ent_kernel.cr`、`src/arch/x86_64`、Python 自举测试。 + +**Spec:** `docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md` + +## Global Constraints + +- 每个任务单独提交;前一个任务的验收通过后才开始下一个。 +- 不使用全局字符串替换修改语义代码;移动或重命名时保留函数名、参数名和注释中的技术事实。 +- v10 完成前,`CCR_VERSION` 保持 v9.1;不得产生“v10 header + v9 字段”的混合文件。 +- 除任务 4 的格式切换外,任何任务都不能同时改变 wire 版本和 IR 语义。 +- 前端语义文件(`src/compiler` 的 lexer/parser/checker/ir_gen/ccr_lower)不得出现 `mp_*`、字节偏移、步长、对齐、寄存器、栈和 ABI。 +- `.ccr` 不得依赖 `.coi`;删除全部 `.coi` 后 v10 文件仍能加载、验证和投影。 +- 所有新增 wire 字段只使用角色名:`count/kind/opcode/flags/offset/size/version/id/imm/word`,不写 `u32/i32` 作为语言类型。 +- v4 语法没有 `` 或 `[T]` 泛型参数语法;文档和测试不得引入这两种写法。 +- 任务 1 到 3 的中间态仍使用 v9 writer;只有在 NOD/ENT 语义和 corearch 消费面同时准备好后,任务 4 才 bump 版本。 +- 每个任务结束运行: + ```bash + nice -n 19 python3 build_selfhost_native.py + ./build/corec check src/compiler + ``` +- 任务 4 起还要运行: + ```bash + nice -n 19 python3 tests/selfhost/test_ccr_v7.py + nice -n 19 python3 tests/selfhost/test_ccr_types.py + nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py + nice -n 19 bash tools/baseline/canary_check.sh + ``` + +## Review Focus + +- 输入:一个合法的 v9 `.ccr`。预期:v10 loader 明确拒绝,不能因为段表前缀相似而读成 v10。 +- 输入:v10 段表有重复 tag、缺失段、段体重叠或尾随字节。预期:全部拒绝,不能把缺失段当空表。 +- 输入:NOD 的 `type_id` 越界,或 `src2/src3` 填入旧宽度值。预期:按损坏拒绝,不能把它解释成字节宽度。 +- 输入:ENT 仍有 `home` 或 `live_end` 越界。预期:记录长度或区间校验拒绝。 +- 输入:删除全部 `.coi` 后加载 `.ccr`。预期:加载和投影不变,只允许优化质量下降。 + +--- + +### Task 1: v10 wire oracle 和负向 fixture + +**Files:** +- Create: `tests/selfhost/ccr_v10_model.py` +- Create: `tests/selfhost/test_ccr_v10_format.py` +- Create: `tests/selfhost/fixtures/ccr_v10_min.ccr`(由测试生成,不入库时改为临时文件) + +**Interfaces:** +- Consumes: v10 规格 §3、§4、§5、§7、§11。 +- Produces: + - `encode(segments: dict[int, bytes]) -> bytes` + - `decode(data: bytes) -> dict[int, bytes]` + - `validate(data: bytes) -> None`,失败抛 `CcrV10Error` + - `SEG_TAGS = {1: "STR", 2: "TYPE", 3: "IFACE", 4: "SYM", 5: "NOD", 6: "ENT", 7: "REG", 8: "EDG", 9: "CASE"}` + +- [ ] **Step 1: 写失败测试** + +`test_ccr_v10_format.py` 至少包含: + +```python +def test_header_and_canonical_segment_order(): + data = encode({tag: b"\x00\x00\x00\x00" for tag in SEG_TAGS}) + assert data[:4] == b"CCR1" + assert version(data) == 0x000A0000 + assert seg_count(data) == 9 + assert list(decode(data)) == [1, 2, 3, 4, 5, 6, 7, 8, 9] + +def test_rejects_v9_version(): + data = encode({tag: b"\x00\x00\x00\x00" for tag in SEG_TAGS}) + with pytest.raises(CcrV10Error): + validate(patch_u32(data, 4, 589825)) + +def test_rejects_duplicate_missing_overlap_and_trailing(): + ... +``` + +- [ ] **Step 2: 运行测试确认失败** + +```bash +python3 tests/selfhost/test_ccr_v10_format.py +``` + +Expected: `ModuleNotFoundError` 或 `CcrV10Error` 未定义。 + +- [ ] **Step 3: 实现 oracle** + +`ccr_v10_model.py` 只做 wire 编解码,不理解 NOD 业务字段。header 固定 16B,段表固定 9×12B,段表 tag 升序、offset 连续、末段尾 == 文件长度。所有拒绝路径抛 `CcrV10Error`。 + +- [ ] **Step 4: 运行测试确认通过** + +```bash +python3 tests/selfhost/test_ccr_v10_format.py +``` + +Expected: 全部 PASS。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "test: add ccr v10 wire oracle and negative fixtures" +``` + +--- + +### Task 2: Core v10 schema 常量 + +**Files:** +- Create: `src/compiler/ccr_schema.cr` +- Modify: `src/compiler/ccr_io.cr`(删除重复常量,改引用) +- Modify: `build_selfhost_native.py` +- Modify: `src/targets/x86_64/linux/elf/_import.cr` +- Modify: `src/arch/x86_64/corearch.cr` 的构建清单引用 + +**Interfaces:** +- Consumes: Task 1 的 wire 形状。 +- Produces: + - `CCR_V10_VERSION : int = 655360` + - `CCR_V10_SEG_COUNT : int = 9` + - `CCR_V10_SEG_STR/TYPE/IFACE/SYM/NOD/ENT/REG/EDG/CASE : int` + - `CCR_V10_NOD_DISK : int = 40` + - `CCR_V10_ENT_DISK : int = 24` + - `CCR_V10_REG_DISK : int = 24` + - `CCR_V10_EDG_DISK : int = 8` + - `CCR_V10_CASE_DISK : int = 24` + +- [ ] **Step 1: 先写编译期断言 fixture** + +在 `tests/selfhost/test_ccr_v10_format.py` 增加一个不依赖 Core 的常量对照测试:从 `ccr_schema.cr` 文本中读取 `CCR_V10_*` 常量,和 Python oracle 的 tag/size 比较。测试必须失败,因为文件尚未创建。 + +- [ ] **Step 2: 运行测试确认失败** + +```bash +python3 tests/selfhost/test_ccr_v10_format.py +``` + +Expected: `ccr_schema.cr does not exist`。 + +- [ ] **Step 3: 创建 schema 文件** + +只放 wire 常量、记录尺寸和段 tag,不放布局、宽度、ABI 或目标常量。`ccr_io.cr` 继续使用旧 `CCR_VERSION`,避免在任务 4 前 bump。 + +- [ ] **Step 4: 加入构建清单** + +`ccr_schema.cr` 放在 `ccr_io.cr` 之前、`dyn_arr.cr` 之后;同时出现在 corec 和 corearch 的共享清单。运行: + +```bash +nice -n 19 python3 build_selfhost_native.py +``` + +Expected: 三个 guard 通过,`corec` 构建成功。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "ccr: add v10 wire schema constants" +``` + +--- + +### Task 3: NOD 语义域和 ENT 无 home 化(仍写 v9) + +**Files:** +- Modify: `src/lattice/ent_kernel.cr` +- Modify: `src/compiler/globals.cr` +- Modify: `src/compiler/ccr_types.cr` +- Modify: `src/compiler/ccr_io.cr` +- Modify: `src/arch/x86_64/corearch.cr` +- Test: `tests/selfhost/test_ccr_types.py` + +**Interfaces:** +- Consumes: `iri_tk`、`sh_tk_face_of_code`、`sh_term_of_ti`、ENT 内存表。 +- Produces: + - `ccr_nod_type_populate() -> int`:为每个节点填 `type_id`。 + - `g_ccr_nod_type : string`、`g_ccr_nod_type_count : int`。 + - `nod_type_id(n: int) -> int`。 + - `nod_type_flags(n: int) -> int`(先恒 0)。 + - ENT 落盘不再写 `home`,但 v9 读取端在任务 4 前暂时忽略该字段。 + +- [ ] **Step 1: 写失败测试** + +在 `test_ccr_types.py` 增加: + +```python +def test_nod_type_id_is_type_row_or_minus_one(...): + # type-face 节点:0 <= type_id < row_count + # 非类型节点:type_id == -1 +``` + +以及 ENT 记录不再依赖 `home` 的内存断言。 + +- [ ] **Step 2: 运行测试确认失败** + +```bash +nice -n 19 python3 tests/selfhost/test_ccr_types.py +``` + +Expected: `nod_type_id` 或 `g_ccr_nod_type` 不存在。 + +- [ ] **Step 3: 实现 type_id 映射** + +规则固定为: + +```text +if sh_tk_face_of_code(op, tk) == 0: + type_id = tk // tk 是 TYPE row +else: + type_id = -1 +flags = 0 +``` + +`item` 仍保留在 v9 内存镜像中供诊断;v10 落盘时不再使用 `item`。ENT 的 `home` 从写入路径删除,`OFF_ENTRY_HOME` 只保留给 v9 读取兼容,任务 4 删除。 + +- [ ] **Step 4: 运行回归** + +```bash +nice -n 19 python3 tests/selfhost/test_ccr_types.py +nice -n 19 python3 tests/selfhost/test_live_ranges.py +nice -n 19 python3 tests/selfhost/test_opt_dex.py +``` + +Expected: 全绿;`.ccr` 字节可变化,但 ELF canary 和语义断言不变。如果 ELF 变化,停止并记录原因。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "ccr: derive semantic type ids and drop ent home from v10 path" +``` + +--- + +### Task 4: v10 cutover:版本、段序、ENT 24B、CASE、corearch 重建 + +**Files:** +- Modify: `src/compiler/ccr_io.cr` +- Modify: `src/compiler/ccr_schema.cr` +- Modify: `src/compiler/globals.cr` +- Modify: `src/arch/x86_64/instr.cr` +- Modify: `src/arch/x86_64/regalloc.cr` +- Modify: `src/arch/hit/lower_to_core.cr` +- Modify: `src/arch/x86_64/corearch.cr` +- Modify: `test` 目录中的 v9 断言 +- Test: `tests/selfhost/test_ccr_v7.py` +- Test: `tests/selfhost/test_ccr_types.py` +- Test: `tests/selfhost/test_ccr_v10_format.py` +- Test: `tests/selfhost/test_native_memory.py` +- Test: `tests/selfhost/test_interp_parity.py` + +**Interfaces:** +- Consumes: Task 2 schema、Task 3 NOD/ENT 语义。 +- Produces: + - `CCR_VERSION := CCR_V10_VERSION` + - `CCR_SEG_COUNT := 9` + - `calc_ccr_size`、`save_ccr`、`load_ccr` 使用 canonical order:`STR TYPE IFACE SYM NOD ENT REG EDG CASE` + - v9 版本 `589825` 一律拒绝。 + - corearch 只从 `type_id` + target DataLayout 重建宽度/stride;不得从 `.ccr` 读回宽度。 + - NOD 的 `flags` 只接收语义节点特性,不接收宽度、字节偏移或 ABI。 + +- [ ] **Step 1: 写失败测试** + +新增测试: + +```python +def test_v10_end_to_end(ccr_path): + data = Path(ccr_path).read_bytes() + assert u32(data, 4) == 0x000A0000 + assert u32(data, 8) == 9 + assert tags(data) == [1, 2, 3, 4, 5, 6, 7, 8, 9] + assert segment(data, 6)[0] == 4 # CASE 空 = count 0 + +def test_v9_rejected(v9_path): + r = corearch(v9_path) + assert r.returncode != 0 +``` + +- [ ] **Step 2: 运行测试确认失败** + +```bash +nice -n 19 python3 tests/selfhost/test_ccr_v10_format.py +nice -n 19 python3 tests/selfhost/test_ccr_v7.py +``` + +Expected: 版本/段表断言失败。 + +- [ ] **Step 3: 实现 container cutover** + +- header:`version = 0x000A0000`,`seg_count = 9`,flags 只保留 `PORTABLE/SYSTEM/CONCURRENT` 位。 +- 段表:9×12B,`tag 1..9`;写侧按 canonical order 连续写;CASE 先写 4B `case_count=0`。 +- ENT:`ESZ_ENTRY_DISK = 24`,字段序 `var_id/version/def_nod/live_start/live_end/flags`;不再读 `home`。 +- NOD:写 `op/dest/src1/src2/src3/type_id/flags/first_edge/edge_count`;`type_id` 来自 Task 3;`flags` 只写语义特性。 +- loader:拒绝 v9、8 段、重复/缺失/重叠/尾随;按段表 offset 解析(顺序流式在 Task 6 收紧)。 +- corearch:`nod_tk`/访问宽度改为从 `type_id` 和 target layout 重建;`IR_DEREF`、`IR_ALLOC_ARRAY`、`IR_STORE_PTR` 不再依赖盘上宽度。所有旧 `tk` 辅码必须先分类:语义特性进 `flags`,目标事实留给 corearch,不可分类的码在保存时拒绝。 + +- [ ] **Step 4: 迁移测试常量** + +`test_ccr_v7.py` 改名为语义测试但保留文件名;把 `V7 = 589825` 改为 `V10 = 0x000A0000`,段 tag/顺序、ENT 24B、`CASE` 断言全部更新。旧 v9 fixture 保留在负向测试中,必须继续拒绝。 + +- [ ] **Step 5: 运行完整回归** + +```bash +nice -n 19 python3 tests/selfhost/test_ccr_v7.py +nice -n 19 python3 tests/selfhost/test_ccr_types.py +nice -n 19 python3 tests/selfhost/test_native_memory.py +nice -n 19 python3 tests/selfhost/test_interp_parity.py +nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py +nice -n 19 python3 tests/selfhost/test_opt_dex.py +nice -n 19 python3 tests/selfhost/test_bigint_arith.py +nice -n 19 bash tools/baseline/canary_check.sh +``` + +Expected: 全绿;`.ccr` canary 四条的尺寸/哈希重锁,ELF canary 保持 `826d74cf...`。重锁必须在 `tools/baseline/canary_values.tsv` 注释中写归因:v10 header/段序/ENT/NOD 字段变化。 + +- [ ] **Step 6: 提交** + +```bash +jj commit -m "ccr: cut over to v10 lattice container and reject v9" +``` + +--- + +### Task 5: 前端去布局,corearch 接管 DataLayout + +**Files:** +- Modify: `src/compiler/ir_gen.cr` +- Modify: `src/compiler/ptr_analysis.cr` +- Modify: `src/compiler/provenance_verify.cr` +- Modify: `src/compiler/interp.cr` +- Modify: `src/compiler/ccr_lower.cr` +- Modify: `src/arch/x86_64/instr.cr` +- Modify: `src/arch/x86_64/regalloc.cr` +- Modify: `src/arch/hit/lower_to_core.cr` +- Test: `tests/selfhost/test_native_memory.py` +- Test: `tests/selfhost/test_interp_parity.py` +- Test: `tests/selfhost/test_pointer_safety.py` + +**Interfaces:** +- Consumes: v10 `type_id`、target layout。 +- Produces: + - `ir_gen` 不再调用 `mp_size/mp_align/mp_stride/mp_field_offset/mp_extent/mp_offset`。 + - `IR_DEREF`、`IR_ALLOC_ARRAY`、`IR_STORE_PTR` 的宽度/stride 不再进入 `.ccr`。 + - corearch 的 `dl_*` 函数(`dl_size/dl_align/dl_stride/dl_field_offset`)从 `type_id` 和 target 计算。 + +- [ ] **Step 1: 写失败静态测试** + +在 `tests/selfhost/test_ccr_v10_format.py` 增加: + +```python +def test_frontend_has_no_layout_calls(): + src = Path("src/compiler/ir_gen.cr").read_text() + for name in ("mp_size", "mp_align", "mp_stride", "mp_field_offset"): + assert name not in src +``` + +以及 `ptr_analysis.cr`、`provenance_verify.cr` 的同类断言。 + +- [ ] **Step 2: 运行测试确认失败** + +```bash +python3 tests/selfhost/test_ccr_v10_format.py -k frontend_has_no_layout +``` + +Expected: 找到 `mp_*`。 + +- [ ] **Step 3: 迁移逐处** + +按操作分类: + +```text +IR_ALLOC_ARRAY -> src3 删除 stride;corearch 用数组元素 type_id + target stride +IR_DEREF -> src3 删除 access width;corearch 用 pointee type_id +IR_STORE_PTR -> 同上,值域来自 type_id +ptr_analysis -> 不再做字节地址缩放;只保留语义路径/索引 +provenance -> 只做区间存在性,不做字节 extent +interp -> 使用 target layout 回调,不读编译器 Mapping +``` + +保留现有 `-1`/unknown 拒绝路径,不能把未知宽度当 0。 + +- [ ] **Step 4: 运行回归** + +```bash +nice -n 19 python3 tests/selfhost/test_native_memory.py +nice -n 19 python3 tests/selfhost/test_interp_parity.py +nice -n 19 python3 tests/selfhost/test_pointer_safety.py +nice -n 19 python3 tests/selfhost/test_bigint_containers.py +nice -n 19 python3 tests/selfhost/test_view_builtins.py +``` + +Expected: 全绿;同一源程序的 v10 `.ccr` 不因 target 布局变化而变。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "ccr: move layout out of frontend into target projection" +``` + +--- + +### Task 6: 顺序流式 loader + +**Files:** +- Modify: `src/compiler/ccr_io.cr` +- Test: `tests/selfhost/test_ccr_v10_stream.py` + +**Interfaces:** +- Consumes: Task 4 canonical file order。 +- Produces: + - `load_ccr` 只从当前 cursor 前进,不按段表 offset 回跳。 + - 段表只用于边界/一致性校验,不用于后向 seek。 + - `TYPE` 在 `SYM` 前就绪,`IFACE` 在 `SYM` 前就绪,`NOD` 在 `ENT/REG/EDG` 前就绪。 + +- [ ] **Step 1: 写失败测试** + +构造一个段表合法但段体顺序为 `STR/SYM/.../TYPE` 的文件;流式 loader 必须拒绝,随机访问 loader 可以接受。测试同时验证 canonical order 文件两种模式语义一致。 + +- [ ] **Step 2: 运行测试确认失败** + +```bash +python3 tests/selfhost/test_ccr_v10_stream.py +``` + +Expected: loader 仍然用 offset 回跳,负向用例被接受。 + +- [ ] **Step 3: 重排解析块** + +把 `load_ccr` 的解析顺序改为: + +```text +STR -> TYPE -> IFACE -> SYM -> NOD -> ENT -> REG -> EDG -> CASE +``` + +`REG` 依赖 `SYM` 的 `fn_meta`,`ENT` 依赖 `SYM` 的 var 命名空间和 `NOD` 的 `instr_cnt`;把这些临时状态保留到对应段读完。CASE 先只校验空表,后续 match 迁移时再填充。 + +- [ ] **Step 4: 运行回归** + +```bash +nice -n 19 python3 tests/selfhost/test_ccr_v10_stream.py +nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py +nice -n 19 bash tools/baseline/canary_check.sh +``` + +Expected: 全绿;`test_ccr_v10_stream` 的负向文件被拒。 + +- [ ] **Step 5: 提交** + +```bash +jj commit -m "ccr: make v10 loader consume canonical segment order" +``` + +--- + +### Task 7: `.ccrb` 分发和 `.coi` 边界 + +**Files:** +- Create: `src/dist/ccrb.cr`(或现有分发层中的对应文件) +- Modify: `docs/maintainer/design/coi-optimization-knowledge-sidecar.md` +- Test: `tests/selfhost/test_ccrb_bundle.py` + +**Interfaces:** +- Consumes: raw v10 `.ccr`。 +- Produces: + - `ccrb_pack(raw_ccr: string, coi: string) -> string`,输出 zstd(deterministic tar)。 + - `ccrb_unpack(bundle: string) -> int`,解出 `manifest`、`program.ccr`、可选 `coi/.coi`。 + - corearch 只接收解压后的 raw `.ccr`,不依赖 zstd。 + +- [ ] **Step 1: 写失败测试** + +测试固定 tar 顺序和元数据;同一输入两次打包逐字节相同;删除 `.coi` 后 raw `.ccr` 字节不变;缺失 `.coi` 时加载/投影不变。 + +- [ ] **Step 2: 实现打包/解包** + +manifest 只含 bundle 版本、raw `.ccr` 哈希、`.coi` 目标列表和哈希。`.coi` 不能包含语义、布局或正确性条件。 + +- [ ] **Step 3: 运行测试** + +```bash +nice -n 19 python3 tests/selfhost/test_ccrb_bundle.py +nice -n 19 python3 tests/selfhost/test_ccr_v10_stream.py +``` + +- [ ] **Step 4: 提交** + +```bash +jj commit -m "dist: add ccrb bundle with optional coi sidecar" +``` + +--- + +### Task 8: 删除残留旧面并重锁 + +**Files:** +- Modify: `src/arch/x86_64/x86_frontend_abi.cr` +- Modify: `src/compiler/ccr_io.cr` +- Modify: `src/compiler/globals.cr` +- Modify: `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(标注 v9 历史) +- Modify: `tools/baseline/canary_values.tsv` + +**Interfaces:** +- Consumes: Task 4 至 7 的 v10 路径。 +- Produces: + - 普通语言 API 不再注册 `cell_store/cell_load/load8/store8/load64/w32/load_str_ptr/store_str_ptr/_dyncpy` 中的机器名。 + - v9 常量、`OFF_ENTRY_HOME`、v9 测试常量全部删除或标为历史 fixture。 + - canary 只保留 v10 值。 + +- [ ] **Step 1: 写失败测试** + +静态断言 `src/arch/x86_64/x86_frontend_abi.cr` 不出现 fixed-width builtin 名;`src/compiler/ccr_io.cr` 不出现 `589825`、`ESZ_ENTRY_DISK : int = 28`、`home` 写入。 + +- [ ] **Step 2: 删除并运行** + +```bash +nice -n 19 python3 build_selfhost_native.py +./build/corec check src/compiler +nice -n 19 python3 tests/selfhost/test_opt_dex.py +nice -n 19 python3 tests/selfhost/test_bigint_arith.py +nice -n 19 python3 tests/selfhost/test_backend_bootstrap.py +nice -n 19 bash tools/baseline/canary_check.sh +``` + +Expected: 全绿;`.ccr` 四条重锁后稳定,ELF canary 保持 `826d74cf...`。 + +- [ ] **Step 3: 提交** + +```bash +jj commit -m "refactor: remove v9 ccr and residual raw frontend surface" +``` + +--- + +## 任务依赖 + +```text +Task 1 wire oracle + -> Task 2 schema + -> Task 3 NOD/ENT semantics + -> Task 4 v10 cutover + -> Task 5 layout out of frontend + -> Task 6 streaming loader + -> Task 7 ccrb/coi + -> Task 8 cleanup + canary +``` + +Task 5 和 Task 6 可以在 Task 4 之后并行设计,但**不要并行改同一文件**;Task 5 先合入,Task 6 再重排 `load_ccr`,避免冲突。 + +## 验收总门槛 + +最终一次完整运行: + +```bash +nice -n 19 python3 build_selfhost_native.py +./build/corec check src/compiler +python3 tests/selfhost/test_ccr_v10_format.py +python3 tests/selfhost/test_ccr_v10_stream.py +python3 tests/selfhost/test_ccr_types.py +python3 tests/selfhost/test_ccr_v7.py +python3 tests/selfhost/test_backend_bootstrap.py +python3 tests/selfhost/test_opt_dex.py +python3 tests/selfhost/test_bigint_arith.py +python3 tests/selfhost/test_native_memory.py +python3 tests/selfhost/test_interp_parity.py +bash tools/baseline/canary_check.sh +``` + +全部通过后,用三阶段自举确认: + +```bash +./build/corec build src/compiler/main.cr -o /tmp/corec2 --static -O 0 +/tmp/corec2 build src/compiler/main.cr -o /tmp/corec3 --static -O 0 +cmp /tmp/corec2 /tmp/corec3 +``` + +`cmp` 必须无输出。 From 4f900293bdf929702472c87189a1d9a361c87050 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:43:26 +0800 Subject: [PATCH 40/58] docs: clarify ccr v10 task 3 keeps v9 ent bytes --- docs/superpowers/plans/2026-10-23-ccr-v10-migration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md index c2d0676e..1861550b 100644 --- a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -180,7 +180,7 @@ jj commit -m "ccr: add v10 wire schema constants" - `g_ccr_nod_type : string`、`g_ccr_nod_type_count : int`。 - `nod_type_id(n: int) -> int`。 - `nod_type_flags(n: int) -> int`(先恒 0)。 - - ENT 落盘不再写 `home`,但 v9 读取端在任务 4 前暂时忽略该字段。 + - v9 写侧保持 28B ENT(含 `home`);v10 写侧到 Task 4 才删除 `home`。 - [ ] **Step 1: 写失败测试** @@ -192,7 +192,7 @@ def test_nod_type_id_is_type_row_or_minus_one(...): # 非类型节点:type_id == -1 ``` -以及 ENT 记录不再依赖 `home` 的内存断言。 +以及一个“v9 ENT 仍为 28B 且 `home` 可由旧 loader 读取”的回归断言;v10 的 24B 断言放到 Task 4。 - [ ] **Step 2: 运行测试确认失败** @@ -214,7 +214,7 @@ else: flags = 0 ``` -`item` 仍保留在 v9 内存镜像中供诊断;v10 落盘时不再使用 `item`。ENT 的 `home` 从写入路径删除,`OFF_ENTRY_HOME` 只保留给 v9 读取兼容,任务 4 删除。 +`item` 仍保留在 v9 内存镜像中供诊断;v10 落盘时不再使用 `item`。**本任务不改变 v9 wire 字节**:v9 写侧仍写 28B ENT(含 `home`),`home` 只在 Task 4 的 v10 写侧删除。这样任务 3 的产物仍可被当前 v9 loader 读取,`.ccr` canary 只应因 Task 3 新增的内部登记而按需重锁;如果 ELF 变化,停止并记录原因。 - [ ] **Step 4: 运行回归** From 281d536e87815b7d47eb0af89fed2d521bc50693 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:45:38 +0800 Subject: [PATCH 41/58] test: add ccr v10 wire oracle and negative fixtures --- tests/selfhost/ccr_v10_model.py | 283 +++++++++++++++++++++++++ tests/selfhost/test_ccr_v10_format.py | 293 ++++++++++++++++++++++++++ 2 files changed, 576 insertions(+) create mode 100644 tests/selfhost/ccr_v10_model.py create mode 100644 tests/selfhost/test_ccr_v10_format.py diff --git a/tests/selfhost/ccr_v10_model.py b/tests/selfhost/ccr_v10_model.py new file mode 100644 index 00000000..53bd8ab4 --- /dev/null +++ b/tests/selfhost/ccr_v10_model.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 +"""v10 `.ccr` wire-format oracle. + +Implements only the physical carrier (spec §3): a 16-byte header, a fixed +9 x 12-byte segment table, and nine contiguous segment bodies in canonical +order. It deliberately knows nothing about NOD/ENT/EDG semantics. + +Wire layout (little-endian throughout): + + offset 0 magic u32 = 0x31524343 ("CCR1") + offset 4 version u32 = 0x000A0000 + offset 8 seg_count u32 = 9 + offset 12 flags u32 (bit 3+ reserved, must be 0) + offset 16 table 9 x {tag u32, offset u32, size u32} + offset 124 bodies canonical tag order 1..9 + +Invariants enforced by `validate`: + * exact magic/version/seg_count; + * table tags strictly ascending 1..9 (no unknown/duplicate/missing tags); + * first body offset == 124 and bodies are contiguous; + * final body end == file length (no overlap, no out-of-bounds, no trailing + bytes). +""" + +import struct + +MAGIC = 0x31524343 # "CCR1" little-endian +VERSION = 0x000A0000 +SEG_COUNT = 9 + +HEADER_SIZE = 16 +SEGMENT_ENTRY_SIZE = 12 +SEGMENT_TABLE_SIZE = SEG_COUNT * SEGMENT_ENTRY_SIZE +BODY_START = HEADER_SIZE + SEGMENT_TABLE_SIZE # 124 + +SEG_TAGS = { + 1: "STR", + 2: "TYPE", + 3: "IFACE", + 4: "SYM", + 5: "NOD", + 6: "ENT", + 7: "REG", + 8: "EDG", + 9: "CASE", +} + +FLAGS_KNOWN_MASK = 0x7 # PORTABLE | SYSTEM | CONCURRENT + + +class CcrV10Error(ValueError): + """Raised for any malformed v10 `.ccr` wire image.""" + + +def _as_bytes(data): + if isinstance(data, (bytes, bytearray, memoryview)): + return bytes(data) + raise CcrV10Error(f"expected bytes-like input, got {type(data).__name__}") + + +def _read_u32(data, offset): + if offset < 0 or offset + 4 > len(data): + raise CcrV10Error( + f"read past end: u32 at offset {offset} needs 4 bytes, " + f"file length {len(data)}" + ) + return struct.unpack_from(" len(data): + raise CcrV10Error( + f"patch past end: u32 at offset {offset}, file length {len(data)}" + ) + if not 0 <= value <= 0xFFFFFFFF: + raise CcrV10Error(f"u32 value out of range: {value}") + struct.pack_into("body mapping into a v10 `.ccr` image. + + All nine tags must be present; bodies may be empty. Missing, unknown, or + non-bytes entries raise `CcrV10Error`. Output is canonical and deterministic + regardless of input dict order. + """ + if not isinstance(segments, dict): + raise CcrV10Error(f"segments must be a dict, got {type(segments).__name__}") + + unknown = [tag for tag in segments if tag not in SEG_TAGS] + if unknown: + raise CcrV10Error(f"unknown segment tag(s): {unknown!r}") + missing = [tag for tag in SEG_TAGS if tag not in segments] + if missing: + raise CcrV10Error(f"missing segment tag(s): {missing!r}") + + bodies = [] + for tag in SEG_TAGS: + body = segments[tag] + if not isinstance(body, (bytes, bytearray, memoryview)): + raise CcrV10Error( + f"segment {tag} ({SEG_TAGS[tag]}) body must be bytes-like, " + f"got {type(body).__name__}" + ) + bodies.append(bytes(body)) + + header = struct.pack(" len(data): + raise CcrV10Error( + f"segment bodies out of bounds: end {expected_offset} > " + f"file length {len(data)}" + ) + if expected_offset < len(data): + raise CcrV10Error( + f"trailing bytes after final segment: {len(data) - expected_offset} " + f"unconsumed" + ) + + return None + + +def decode(data): + """Validate and split an image into `{tag: body}` in canonical order.""" + data = _as_bytes(data) + validate(data) + segments = {} + for tag, offset, size in read_segment_table(data): + segments[tag] = data[offset : offset + size] + return segments diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py new file mode 100644 index 00000000..08d52bf1 --- /dev/null +++ b/tests/selfhost/test_ccr_v10_format.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +"""v10 `.ccr` wire format oracle tests (Task 1: header/segment table/segment bodies). + +Wire contract (docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md §3): + + Header 16B + magic 4B = 0x31524343 ("CCR1", little-endian) + version 4B = 0x000A0000 + seg_count 4B = 9 + flags 4B (bit 3+ reserved, must be zero) + Segment table 9 x 12B + tag 4B (1..9, ascending, canonical order) + offset 4B (contiguous, first == 124) + size 4B + Segment bodies, canonical order: + 1 STR / 2 TYPE / 3 IFACE / 4 SYM / 5 NOD / 6 ENT / 7 REG / 8 EDG / 9 CASE + +Scope: wire level only. The oracle does not interpret NOD/ENT/... business +fields; those belong to later v10 tasks. + +Runs standalone (`python3 tests/selfhost/test_ccr_v10_format.py`) and is also +pytest-discoverable (plain `test_*` functions, stdlib only). +""" + +import contextlib + +from ccr_v10_model import ( + BODY_START, + CcrV10Error, + MAGIC, + SEG_COUNT, + SEG_TAGS, + VERSION, + decode, + encode, + flags, + magic, + patch_segment_entry, + patch_u32, + read_segment_table, + seg_count, + segment, + validate, + version, +) + + +# --------------------------------------------------------------------------- +# local raises() so the file stays stdlib-only and works under `python3 file.py` +# --------------------------------------------------------------------------- + + +class _Raises: + value = None + + +@contextlib.contextmanager +def raises(exc_type): + holder = _Raises() + try: + yield holder + except exc_type as exc: + holder.value = exc + else: + raise AssertionError(f"expected {exc_type.__name__}, nothing raised") + + +def _sample_body(tag): + return bytes([tag, tag + 1, tag + 2]) + + +def _sample_segments(): + return {tag: _sample_body(tag) for tag in SEG_TAGS} + + +def _all_empty(): + return {tag: b"" for tag in SEG_TAGS} + + +# --------------------------------------------------------------------------- +# 1. header fields and canonical order +# --------------------------------------------------------------------------- + + +def test_header_and_canonical_segment_order(): + data = encode({tag: b"\x00\x00\x00\x00" for tag in SEG_TAGS}) + + assert data[:4] == b"CCR1" + assert magic(data) == MAGIC == 0x31524343 + assert version(data) == VERSION == 0x000A0000 + assert seg_count(data) == SEG_COUNT == 9 + assert flags(data) == 0 + + assert read_segment_table(data) == [ + (1, 124, 4), + (2, 128, 4), + (3, 132, 4), + (4, 136, 4), + (5, 140, 4), + (6, 144, 4), + (7, 148, 4), + (8, 152, 4), + (9, 156, 4), + ] + assert list(decode(data)) == [1, 2, 3, 4, 5, 6, 7, 8, 9] + assert len(data) == 160 + assert validate(data) is None + + +def test_data_starts_at_124_and_last_end_is_file_length(): + data = encode(_sample_segments()) + table = read_segment_table(data) + assert table[0][1] == BODY_START == 124 + + expected = BODY_START + for tag, offset, size in table: + assert offset == expected, f"tag {tag}: offset {offset} != {expected}" + expected += size + assert expected == len(data) + + +# --------------------------------------------------------------------------- +# 2..10. negative fixtures: every malformed shape must be rejected +# --------------------------------------------------------------------------- + + +def test_rejects_v9_version(): + data = encode(_sample_segments()) + assert 589825 == 0x00090001 # v9.1 + with raises(CcrV10Error): + validate(patch_u32(data, 4, 589825)) + + +def test_rejects_wrong_magic(): + data = encode(_sample_segments()) + with raises(CcrV10Error): + validate(patch_u32(data, 0, 0x31524344)) # "DCR1" + + +def test_rejects_seg_count_not_nine(): + data = encode(_sample_segments()) + with raises(CcrV10Error): + validate(patch_u32(data, 8, 8)) + + +def test_rejects_duplicate_tag(): + data = encode(_sample_segments()) + # Entry 1 claims tag 1 again; tag 2 is now absent. + bad = patch_segment_entry(data, 1, tag=1) + with raises(CcrV10Error) as caught: + validate(bad) + assert "duplicate" in str(caught.value) + + +def test_rejects_missing_tag(): + data = encode(_sample_segments()) + # Entry 1 jumps from tag 1 to tag 3: tag 2 is missing. + bad = patch_segment_entry(data, 1, tag=3) + with raises(CcrV10Error) as caught: + validate(bad) + assert "missing" in str(caught.value) + + +def test_rejects_non_contiguous_offset(): + data = encode(_sample_segments()) + # TYPE must start at 124 + size(STR); shift it one byte later. + bad = patch_segment_entry(data, 1, offset=read_segment_table(data)[1][1] + 1) + with raises(CcrV10Error) as caught: + validate(bad) + assert "contiguous" in str(caught.value) + + +def test_rejects_overlapping_segment(): + data = encode(_sample_segments()) + # Inflate STR so it runs past the start of TYPE. + bad = patch_segment_entry(data, 0, size=read_segment_table(data)[0][2] + 1) + with raises(CcrV10Error) as caught: + validate(bad) + assert "contiguous" in str(caught.value) + + +def test_rejects_out_of_bounds_segment(): + data = encode(_sample_segments()) + # Grow only the last segment: table stays contiguous, end passes EOF. + bad = patch_segment_entry(data, 8, size=read_segment_table(data)[8][2] + 1) + with raises(CcrV10Error) as caught: + validate(bad) + assert "out of bounds" in str(caught.value) + + +def test_rejects_trailing_byte(): + data = encode(_sample_segments()) + with raises(CcrV10Error) as caught: + validate(data + b"\x00") + assert "trailing" in str(caught.value) + + +def test_rejects_truncated_file(): + data = encode(_sample_segments()) + with raises(CcrV10Error): + validate(data[:-1]) + + +def test_rejects_reserved_flag_bits(): + data = encode(_sample_segments()) + with raises(CcrV10Error): + validate(patch_u32(data, 12, 0x8)) # bit 3 is reserved + + +# --------------------------------------------------------------------------- +# 9..10. round trip, empty segments, determinism +# --------------------------------------------------------------------------- + + +def test_empty_segments_roundtrip(): + data = encode(_all_empty()) + assert len(data) == 124 + assert validate(data) is None + assert decode(data) == _all_empty() + assert list(decode(data)) == list(SEG_TAGS) + + +def test_encode_decode_round_trip(): + segments = { + 1: b"str\x00table", + 2: b"", + 3: b"iface", + 4: b"\xff\x00\x7f", + 5: bytes(range(40)), + 6: b"", + 7: b"reg" * 5, + 8: b"edg\x01\x02", + 9: b"case", + } + data = encode(segments) + assert validate(data) is None + assert decode(data) == segments + assert list(decode(data)) == [1, 2, 3, 4, 5, 6, 7, 8, 9] + assert segment(data, 5) == segments[5] + assert segment(data, 6) == b"" + assert encode(decode(data)) == data + + +def test_deterministic_output(): + segments = _sample_segments() + reversed_segments = {tag: segments[tag] for tag in reversed(list(SEG_TAGS))} + + first = encode(segments) + second = encode(segments) + assert first == second + assert encode(reversed_segments) == first + + +def test_encode_rejects_missing_and_unknown_tags(): + with raises(CcrV10Error): + encode({tag: b"" for tag in SEG_TAGS if tag != 9}) + with raises(CcrV10Error): + encode({**{tag: b"" for tag in SEG_TAGS}, 10: b""}) + + +def test_encode_rejects_non_bytes_body(): + segments = {tag: b"" for tag in SEG_TAGS} + segments[5] = "not-bytes" + with raises(CcrV10Error): + encode(segments) + + +# --------------------------------------------------------------------------- +# standalone runner +# --------------------------------------------------------------------------- + + +def main(): + tests = [ + value + for name, value in sorted(globals().items()) + if name.startswith("test_") and callable(value) + ] + failed = 0 + for test in tests: + try: + test() + except Exception as exc: # noqa: BLE001 - report every failure shape + failed += 1 + print(f"FAIL {test.__name__}: {exc!r}") + else: + print(f"PASS {test.__name__}") + print(f"{len(tests) - failed}/{len(tests)} passed") + return 1 if failed else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 507c093d66ac9bfc9fd06801e91e672acd631309 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Fri, 23 Oct 2026 15:50:21 +0800 Subject: [PATCH 42/58] ccr: add v10 wire schema constants --- src/compiler/ccr_schema.cr | 25 +++++++++ tests/selfhost/test_ccr_v10_format.py | 81 +++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/src/compiler/ccr_schema.cr b/src/compiler/ccr_schema.cr index 350cef5b..4e045b88 100644 --- a/src/compiler/ccr_schema.cr +++ b/src/compiler/ccr_schema.cr @@ -14,3 +14,28 @@ OFF_TT_HASH : int = 40 // Optimization metadata record: header plus up to five register pairs. OPT_META_STRIDE : int = 64 + +// ── .ccr v10 wire schema (spec §3) ────────────────────────────────────── +// These constants name wire-format roles and fixed record sizes in the +// carrier file. They are neither language types nor target widths, and they +// imply no machine convention. ccr_io.cr keeps writing v9 (CCR_VERSION) +// until the v10 cutover task. + +CCR_V10_VERSION : int = 655360 // 0x000A0000 +CCR_V10_SEG_COUNT : int = 9 +CCR_V10_SEG_STR : int = 1 +CCR_V10_SEG_TYPE : int = 2 +CCR_V10_SEG_IFACE : int = 3 +CCR_V10_SEG_SYM : int = 4 +CCR_V10_SEG_NOD : int = 5 +CCR_V10_SEG_ENT : int = 6 +CCR_V10_SEG_REG : int = 7 +CCR_V10_SEG_EDG : int = 8 +CCR_V10_SEG_CASE : int = 9 + +// Fixed on-disk record sizes for the graph segments. +CCR_V10_NOD_DISK : int = 40 +CCR_V10_ENT_DISK : int = 24 +CCR_V10_REG_DISK : int = 24 +CCR_V10_EDG_DISK : int = 8 +CCR_V10_CASE_DISK : int = 24 diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 08d52bf1..6d7a781a 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -23,6 +23,8 @@ """ import contextlib +import re +from pathlib import Path from ccr_v10_model import ( BODY_START, @@ -265,6 +267,85 @@ def test_encode_rejects_non_bytes_body(): encode(segments) +# --------------------------------------------------------------------------- +# 11. Core schema constants (static drift guard against the Python oracle) +# --------------------------------------------------------------------------- + +SCHEMA_PATH = ( + Path(__file__).resolve().parent.parent.parent / "src" / "compiler" / "ccr_schema.cr" +) + +# Values are the wire contract from spec §3; they must match ccr_v10_model.py. +EXPECTED_V10_CONSTANTS = { + "CCR_V10_VERSION": 655360, # 0x000A0000 + "CCR_V10_SEG_COUNT": 9, + "CCR_V10_SEG_STR": 1, + "CCR_V10_SEG_TYPE": 2, + "CCR_V10_SEG_IFACE": 3, + "CCR_V10_SEG_SYM": 4, + "CCR_V10_SEG_NOD": 5, + "CCR_V10_SEG_ENT": 6, + "CCR_V10_SEG_REG": 7, + "CCR_V10_SEG_EDG": 8, + "CCR_V10_SEG_CASE": 9, + "CCR_V10_NOD_DISK": 40, + "CCR_V10_ENT_DISK": 24, + "CCR_V10_REG_DISK": 24, + "CCR_V10_EDG_DISK": 8, + "CCR_V10_CASE_DISK": 24, +} + +# `Name : int = ` — the Core constant form used in the schema file. +_SCHEMA_CONST_RE = re.compile( + r"^\s*(CCR_V10_[A-Z0-9_]+)\s*:\s*int\s*=\s*(\d+)\s*(?://.*)?$", re.MULTILINE +) + +SEGMENT_NAMES = ("STR", "TYPE", "IFACE", "SYM", "NOD", "ENT", "REG", "EDG", "CASE") + + +def _schema_text(): + assert SCHEMA_PATH.is_file(), f"ccr_schema.cr does not exist: {SCHEMA_PATH}" + return SCHEMA_PATH.read_text(encoding="utf-8") + + +def _schema_constants(text): + found = {} + for name, value in _SCHEMA_CONST_RE.findall(text): + assert name not in found, f"{name} is defined more than once" + found[name] = int(value) + return found + + +def test_schema_v10_constants_exact(): + found = _schema_constants(_schema_text()) + + missing = sorted(set(EXPECTED_V10_CONSTANTS) - set(found)) + assert not missing, f"missing v10 constants: {missing}" + + for name, expected in EXPECTED_V10_CONSTANTS.items(): + assert found[name] == expected, f"{name} = {found[name]}, expected {expected}" + + extra = sorted(set(found) - set(EXPECTED_V10_CONSTANTS)) + assert not extra, f"unexpected v10 constants: {extra}" + + +def test_schema_v10_matches_python_oracle(): + found = _schema_constants(_schema_text()) + + assert found["CCR_V10_VERSION"] == VERSION + assert found["CCR_V10_SEG_COUNT"] == SEG_COUNT == len(SEG_TAGS) + + tags = [found[f"CCR_V10_SEG_{name}"] for name in SEGMENT_NAMES] + assert tuple(tags) == tuple(SEG_TAGS) == tuple(range(1, 10)) + + +def test_schema_has_no_target_layout_words(): + text = _schema_text() + + for word in ("mp_", "ABI", "align", "stride", "offset"): + assert word not in text, f"ccr_schema.cr must not mention {word!r}" + + # --------------------------------------------------------------------------- # standalone runner # --------------------------------------------------------------------------- From 1ce412d77a70f79f2da9454e16976ad7dcd26fcc Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 00:08:27 +0800 Subject: [PATCH 43/58] docs: align ccr v10 task 3 with v9 wire freeze --- docs/superpowers/plans/2026-10-23-ccr-v10-migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md index 1861550b..a8f0d5cd 100644 --- a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -163,7 +163,7 @@ jj commit -m "ccr: add v10 wire schema constants" --- -### Task 3: NOD 语义域和 ENT 无 home 化(仍写 v9) +### Task 3: NOD 语义域(仍写 v9) **Files:** - Modify: `src/lattice/ent_kernel.cr` @@ -229,7 +229,7 @@ Expected: 全绿;`.ccr` 字节可变化,但 ELF canary 和语义断言不变 - [ ] **Step 5: 提交** ```bash -jj commit -m "ccr: derive semantic type ids and drop ent home from v10 path" +jj commit -m "ccr: derive semantic type ids for v10 nod" ``` --- From 7608aa5d9984d2dba9e6d2cc7f0d89a7b72f5a16 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 00:11:50 +0800 Subject: [PATCH 44/58] ccr: derive semantic type ids for v10 nod --- src/compiler/ccr_types.cr | 58 +++++++++++++- src/compiler/globals.cr | 9 +++ src/lattice/ent_kernel.cr | 13 ++++ tests/selfhost/test_ccr_types.py | 130 ++++++++++++++++++++++++++++++- 4 files changed, 207 insertions(+), 3 deletions(-) diff --git a/src/compiler/ccr_types.cr b/src/compiler/ccr_types.cr index 3f9384e7..d96c368e 100644 --- a/src/compiler/ccr_types.cr +++ b/src/compiler/ccr_types.cr @@ -319,7 +319,12 @@ fn ccr_seg_prepare_save() -> int: // bodies: it only queries the bridge cache, and all rows are already built, so // it adds no terms and does not change segment bytes. Failure uses the same // type-face gate. - return ccr_nod_item_populate() + if ccr_nod_item_populate() != 0: + return -1 + // Derive the v10 NOD semantic type ids from the same save-time type state. + // This is side data for the v10 cutover: the v9 NOD wire keeps `item` at +28 + // and does not gain a type_id field here. + return ccr_nod_type_populate() // Fill NOD term indexes. For each node in global instruction order, look up the // file-space term index from the stored derived code. @@ -346,9 +351,36 @@ fn ccr_nod_item_populate() -> int: ii = ii + 1 return 0 +// Fill NOD semantic type ids into the v10 side buffer. For each node in global +// instruction order, apply the fixed rule: +// sh_tk_face_of_code(op, tk) == 0 -> type_id = tk (tk is a TYPE row) +// otherwise -> type_id = -1 +// The companion flags word is constant 0 in this task; Task 4 classifies semantic +// node features into it. +// This is a pure query/derivation: it does not mutate the type tables, the term +// table, or g_tk_face_fail, and it does not change the v9 wire. The 40B NOD record +// still carries `item` at +28 for diagnostics and for the current v9 loader. +fn ccr_nod_type_populate() -> int: + n := g_ir_instr_count + g_ccr_nod_type = alloc((n + 8) * 4) + g_ccr_nod_type_count = n + ii : ., mut = 0 + loop: + if ii >= n: + break + tk := iri_tk(ii) + tid : ., mut = -1 + if sh_tk_face_of_code(iri_op(ii), tk) == 0: + tid = tk + cell_store32(g_ccr_nod_type, ii * 4, tid) + ii = ii + 1 + return 0 + // --dump-types test channel. The shared surface dump in ccr_io.cr uses the same // print path as corearch, so line formats match across processes. This corec-only -// section adds the row-to-term mapping; tests split on "rowterms:". +// section adds the row-to-term mapping and the NOD semantic type-id side buffer +// (v10 preparation; the v9 wire does not carry it); tests split on "rowterms:" +// and "nodtypes:". fn ccr_type_selftest_dump(): ccr_type_surface_dump() print("rowterms: ") @@ -363,6 +395,28 @@ fn ccr_type_selftest_dump(): print(" ") println(int_str(sh_term_of_ti(i))) i = i + 1 + // v10 NOD semantic type ids (v9 wire frozen): read the side buffer through the + // shared accessors so tests see exactly what nod_type_id returns. One + // deterministic line per node; flags is the constant 0 companion word. + print("nodtypes: ") + print_i(g_ccr_nod_type_count) + println("") + ni : ., mut = 0 + loop: + if ni >= g_ccr_nod_type_count: + break + print("nodtype ") + print_i(ni) + print(" op ") + print_i(iri_op(ni)) + print(" tk ") + print_i(iri_tk(ni)) + print(" tid ") + print_i(nod_type_id(ni)) + print(" flags ") + print_i(nod_type_flags(ni)) + println("") + ni = ni + 1 // --dump-ifaces test channel. The shared surface dump uses the same print path as // corearch. This corec-only section recomputes signature terms after the segment diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index fe8f2f55..4b931792 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -57,6 +57,15 @@ g_ccr_iface_seg_len : int, mut // This file is first in both builds, so these globals must stay here. g_ccr_nod_item : string, mut g_ccr_nod_item_count : int, mut +// NOD semantic type-id side buffer (v10 NOD `type_id`). Filling happens in +// ccr_types.cr:ccr_nod_type_populate during the save path; the v9 wire keeps the +// 40B NOD record with `item` at +28 and does not carry this value. +// Value semantics: >= 0 is a TYPE row (face 0 = the row's item is reversible); +// -1 means no type row. `nod_type_flags` is the companion semantic-flags word +// (constant 0 until the v10 cutover classifies aux codes). +// This file is first in both builds, so these globals must stay here. +g_ccr_nod_type : string, mut +g_ccr_nod_type_count : int, mut g_ast : string, mut g_ast_count : int, mut g_ast_cap : int, mut diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index 25ab1219..bc8ce0a2 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -1035,6 +1035,19 @@ fn nod_tk(n: int) -> int: // 0xFFFFFFFF on disk and has different semantics; do not mix the read types. fn nod_item(n: int) -> int: return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_ITEM) +// Semantic type-id accessor (v10 NOD `type_id` side data). The buffer is filled +// by ccr_types.cr:ccr_nod_type_populate during the corec save path; the v9 wire +// still stores `item` at +28 and has no type_id field, so the read side leaves +// this buffer empty until the v10 cutover. >= 0 is a TYPE row; -1 means none. +// It reads i32 so -1 stays -1, but the sign convention differs from the code +// slot's 0xFFFFFFFF, so do not mix the read types. +fn nod_type_id(n: int) -> int: + return buf_read_i32(g_ccr_nod_type, n * 4) +// Semantic node flags companion word. Constant 0 in v9: no semantic node feature +// is classified yet and the wire has no flags field. Task 4 fills this from +// classified aux codes; callers must not read it as a width or byte offset. +fn nod_type_flags(n: int) -> int: + return 0 fn nod_edge_first(n: int) -> int: return cell_load(g_v7_nod_meta, n * ESZ_NOD_META) fn nod_edge_count(n: int) -> int: diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index d930957f..530c09bb 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -2298,6 +2298,132 @@ def test_p6t3_d18_purity_static(): assert not bad, f"ccr_io.cr references bridge-layer symbols in code: {bad[:3]}" +# ═══════════ CCR v10 Task 3:NOD 语义 type_id(v9 wire 冻结)═══════════ +# 观察通道 = `corec ccr --dump-types` 末尾的 `nodtypes` 节(corec-only;只读、 +# 不产额外产物、无新 CLI flag)。语义 = `ccr_types.cr:ccr_nod_type_populate` +# 的 side buffer:face 0(tk 是可逆 TYPE 行)⇒ type_id = tk;其余 ⇒ -1; +# flags 恒 0(本任务不落 v9 wire,v10 cutover 才消费)。 +# 期望值在 Python 侧按规则**独立复述**(MINT_OPS + rowterms + 项表读逆), +# 不复用编译器判定表——防同源同错自洽假绿。 + +P7_TID_HDR_RE = re.compile(r'^nodtypes: (\d+)$') +P7_TID_RE = re.compile(r'^nodtype (\d+) op (-?\d+) tk (-?\d+) tid (-?\d+) flags (-?\d+)$') + + +def parse_nod_type_dump(text: str): + """`--dump-types` 的 `nodtypes` 节 → (count, [(node, op, tk, tid, flags)])。 + 行格式契约见 ccr_types.cr:ccr_type_selftest_dump。节点序必须无洞。""" + hdr = None + rows = [] + for ln in text.splitlines(): + m = P7_TID_HDR_RE.match(ln) + if m: + hdr = int(m.group(1)) + continue + m = P7_TID_RE.match(ln) + if m: + rows.append(tuple(int(m.group(k)) for k in range(1, 6))) + assert hdr is not None, "dump has no 'nodtypes:' header" + assert len(rows) == hdr, f"nodtype rows {len(rows)} != header {hdr}" + assert [r[0] for r in rows] == list(range(hdr)), "nodtype node ids not contiguous" + return hdr, rows + + +def expected_nod_type_id(op: int, tk: int, rows, terms, rowterms) -> int: + """Python 侧独立复述固定规则: + · 类型行面 op(MINT_OPS = sh_tk_is_type_face 的同一集合)∧ tk 行域内 + ∧ rowterm 是 ATOM(b == tk)(= 可逆行)⇒ type_id = tk; + · 其余(辅码面 / 复合行 / 越界 / 无类型)⇒ -1。""" + if op not in MINT_OPS: + return -1 + if tk < 0 or tk >= len(rows): + return -1 + t = rowterms.get(tk, -1) + if t < 0 or t >= len(terms): + return -1 + tag, _a, b, _c, _d = terms[t] + if tag != TT_ATOM or b != tk: + return -1 + return tk + + +def test_nod_type_id_is_type_row_or_minus_one(): + """Task 3(v10 NOD 语义域,v9 wire 冻结):`ccr_nod_type_populate` 的逐节点 + type_id —— 类型面节点 `0 <= type_id < row_count` 且 `type_id == tk`; + 非类型面节点 `type_id == -1`;`flags == 0`(本任务恒 0)。 + 非空转:夹具必须同时出现类型面节点(const/binary/load…)与非类型面节点 + (deref/bounds/spawn…)。""" + src = tk_fixture('p7tid', TK_FIXTURE) + ccr_path = os.path.join(BASE, 'build', 'test_p7t3_typeid.ccr') + try: + out = corec_dump(src, ccr_path) # ccr --dump-types(save 前 dump) + rows, terms, _probe, rowterms = parse_type_dump(out) + hdr, nrows = parse_nod_type_dump(out) + assert hdr == len(nrows) > 0, "vacuous: no node in nodtype dump" + assert len(rowterms) == len(rows), \ + f"rowterm map {len(rowterms)} != rows {len(rows)}" + # 通道自洽:dump 的节点序 == 同一次 ccr 产物 NOD 段的 (op, tk) 序列 + nod = ccr_nod_records(read_ccr(ccr_path)) + assert hdr == len(nod), f"nodtype count {hdr} != NOD records {len(nod)}" + for i, (n, op, tk, _tid, _flags) in enumerate(nrows): + assert (op, tk) == (nod[i][0], nod[i][1]), \ + f"node {i}: dump (op,tk)=({op},{tk}) != wire {(nod[i][0], nod[i][1])}" + face = 0 + nonface = 0 + face_gt0 = 0 + for (n, op, tk, tid, flags) in nrows: + assert flags == 0, f"node {n}: flags {flags} != 0" + exp = expected_nod_type_id(op, tk, rows, terms, rowterms) + assert tid == exp, \ + f"node {n}: type_id {tid} != expected {exp} (op={op} tk={tk})" + if exp >= 0: + face += 1 + if tid > 0: + face_gt0 += 1 + assert tid == tk, f"node {n}: type_id {tid} != row {tk}" + assert 0 <= tid < len(rows), \ + f"node {n}: type_id {tid} outside row domain {len(rows)}" + else: + nonface += 1 + assert tid == -1, f"node {n}: non-face type_id {tid} != -1" + assert face > 0, "vacuous: fixture has no type-face node" + assert nonface > 0, "vacuous: fixture has no non-type node" + assert face_gt0 > 0, "vacuous: all type ids are row 0 (degenerate mapping)" + finally: + _cleanup(src, ccr_path) + + +def test_v9_ent_still_28b_with_home(): + """Task 3 回归(v9 wire 冻结):ENT 记录仍为 28B(var/version/def/ + live_start/live_end/home/flags),`home` 仍由旧 loader 读回,合法 v9 + 文件仍能载入并发射 ELF。v10 的 24B 断言留在 Task 4。""" + src_path, ccr_path = _shell_fixture('t3ent') + out = os.path.join(BASE, 'build', 'test_p7t3_ent.out') + try: + data = read_ccr(ccr_path) + c = CcrFile(data) # 内含 version == VER(589825) + 8 段规范序断言 + assert VER == 589825, "CCR_VERSION is no longer v9.1" + body = c.body(4) + (cnt,) = struct.unpack_from(' 0, "fixture produced no ENT records (vacuous)" + assert (len(body) - 4) % 28 == 0, \ + f"ENT body {len(body)}B not 4 + n*28" + assert (len(body) - 4) // 28 == cnt, \ + f"ENT count {cnt} != record slots {(len(body) - 4) // 28}" + # 旧 loader 读回 home:每条记录都打印 home 列(28B 记录的 +20 槽) + r = subprocess.run([COREARCH, ccr_path, '--dump-entries'], + capture_output=True, text=True, cwd=BASE, timeout=120) + assert r.returncode == 0, \ + f"corearch --dump-entries rc={r.returncode}: {r.stdout!r} {r.stderr!r}" + homes = re.findall(r' home (-?\d+)', r.stdout) + assert len(homes) == cnt, \ + f"{len(homes)} home values != {cnt} ENT records" + # 合法 v9 文件端到端仍可载入/发射 + arch_elf(ccr_path, out) + finally: + _cleanup(src_path, ccr_path, out) + + if __name__ == '__main__': tests = [test_p4t1_layout_eight_segments, test_p4t1_loader_rejects_valid_v7_file, @@ -2347,7 +2473,9 @@ def test_p6t3_d18_purity_static(): test_p6t3_aux_face_and_composite_preserved, test_p6t3_item_oob_rejected, test_p6t3_nod_count_overflow_rejected, - test_p6t3_d18_purity_static] + test_p6t3_d18_purity_static, + test_nod_type_id_is_type_row_or_minus_one, + test_v9_ent_still_28b_with_home] failed = 0 for t in tests: try: From 87a9379ca6958d39612c4127acb0dc8f9c62cb3f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 00:32:18 +0800 Subject: [PATCH 45/58] ccr: cut over to v10 lattice container and reject v9 --- build_selfhost_native.py | 5 + src/arch/x86_64/backend_flags.cr | 2 +- src/arch/x86_64/corearch.cr | 60 +-- src/arch/x86_64/regalloc.cr | 85 +++- src/compiler/ccr_io.cr | 594 +++++++++++++----------- src/compiler/ccr_schema.cr | 14 +- src/compiler/ccr_types.cr | 105 +++-- src/compiler/checker.cr | 16 +- src/compiler/dyn_arr.cr | 18 + src/compiler/globals.cr | 20 +- src/lattice/ent_kernel.cr | 50 +- src/targets/x86_64/linux/elf/_import.cr | 4 + src/targets/x86_64/linux/elf/main.cr | 2 +- tests/selfhost/test_ccr_types.py | 402 ++++++++-------- tests/selfhost/test_ccr_v10_format.py | 142 ++++++ tests/selfhost/test_ccr_v7.py | 185 ++++---- tests/selfhost/test_interp_parity.py | 50 +- tests/selfhost/test_native_memory.py | 40 ++ tests/selfhost/test_region_cfg.py | 32 +- tools/baseline/canary_values.tsv | 21 +- 20 files changed, 1147 insertions(+), 700 deletions(-) diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 3ac9292b..7112dca4 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -61,6 +61,11 @@ # ① 架构轴 x86_64:寄存器分配 CAG + 指令编码 + 字节尺寸单源(sizes.cr) # + tagged BigInt 立即数/装箱辅助(int_rep.cr)与运行时 blob。 arch_x86_64_files = [ + # Target layout helpers. corearch reconstructs NOD access widths and array + # strides from these instead of reading target facts back from .ccr. They + # depend only on common_files (ast/dyn_arr type rows), not on the frontend. + 'src/compiler/mapping.cr', + 'src/arch/x86_64/x86_mapping.cr', 'src/arch/x86_64/backend_schema.cr', 'src/arch/x86_64/backend_globals.cr', 'src/arch/x86_64/backend_arrays.cr', diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index 68501449..b2d2deb5 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -23,7 +23,7 @@ fn register_backend_flags(): cli_flag_bool_debug("inject-read-gap", "", "Debug hook: truncate last version interval") cli_flag_bool_debug("inject-coexist-oob", "", "Debug hook: probe entries_coexist out of bounds") cli_flag_bool_debug("dump-objects", "", "Debug: dump NOD/EDG objects") - cli_flag_bool_debug("dump-nod-items", "", "Debug: dump NOD type terms") + cli_flag_bool_debug("dump-nod-types", "", "Debug: dump NOD type_id/flags") cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index 97e78efa..8e6d925c 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -250,26 +250,32 @@ fn dump_object_surface(): nod_inputs(ni) // print outgoing edge records through the kernel helper ni = ni + 1 -// --dump-nod-items: print each node's NOD term index, the term table entry, and its -// atom row. The header gives the node count and how many nodes have a term. Each -// body line gives {idx, atom}, so tests can assert atom == code when idx >= 0. -// load_ccr already performs this consistency check as a hard gate; this channel is -// its readable form. It returns before emission. -fn dump_nod_items(): - with_item : ., mut = 0 +// --dump-nod-types: print each node's semantic type_id and feature flags. The +// disk has no term-index field in v10, so this channel exposes the actual wire +// semantics. It returns before emission. +fn dump_nod_types(): + with_type : ., mut = 0 ni : ., mut = 0 loop: if ni >= g_v7_nod_count: break - if nod_item(ni) >= 0: - with_item = with_item + 1 + if nod_type_id(ni) >= 0: + with_type = with_type + 1 ni = ni + 1 - print("[nod-items] nodes=") + print("[nod-types] nodes=") print_i(g_v7_nod_count) - print(" with_item=") - print_i(with_item) - print(" terms=") - print_i(tt_count()) + print(" with_type=") + print_i(with_type) + print(" flags=") + fl : ., mut = 0 + ni2 : ., mut = 0 + loop: + if ni2 >= g_v7_nod_count: + break + if nod_type_flags(ni2) != 0: + fl = fl + 1 + ni2 = ni2 + 1 + print_i(fl) print(" rows=") print_i(g_type_count) println("") @@ -277,20 +283,14 @@ fn dump_nod_items(): loop: if nj >= g_v7_nod_count: break - itv := nod_item(nj) - at : ., mut = -1 - if itv >= 0: - at = tt_atom_of_term(itv) - print("item ") + print("nodtype ") print_i(nj) print(" op ") print_i(nod_op(nj)) - print(" code ") - print_i(nod_tk(nj)) - print(" idx ") - print_i(itv) - print(" atom ") - print_i(at) + print(" tid ") + print_i(nod_type_id(nj)) + print(" flags ") + print_i(nod_type_flags(nj)) println("") nj = nj + 1 @@ -387,11 +387,11 @@ fn corearch_main() -> int: ccr_iface_surface_dump() return 0 - // Hidden channel: print NOD term indexes and their atom rows. It runs after the - // TYPE segment is rebuilt and before build_linear_schedule, so it does not - // trigger emission. load_ccr already enforces consistency. - if cli_has("dump-nod-items") != 0: - dump_nod_items() + // Hidden channel: print NOD semantic type ids and feature flags. It runs after + // the TYPE segment is rebuilt and before build_linear_schedule, so it does not + // trigger emission. + if cli_has("dump-nod-types") != 0: + dump_nod_types() return 0 // The loader produces semantic objects only. The instance rebuilds the linear diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index b15dba11..5b56fb08 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -893,9 +893,80 @@ fn alloc_registers(): ai = ai + 1 fi = fi + 1 +// ── v10 target-fact reconstruction ──────────────────────────────────────── +// The .ccr carries semantic facts only. Access widths, array strides, and +// runtime allocation sizes are target facts; the instance rebuilds them from +// type ids/variable types and the x86-64 layout (mapping.cr/x86_mapping.cr). +// These helpers live here because both backend entries (the concat corearch +// entry and the project-mode target entry) import regalloc.cr. +fn corearch_ptr_pointee(var: int) -> int: + if var < 0: + return TI_INT + ti := irv_type(var) + if ti >= 0: + k := get_type_kind(ti) + if k == TYP_PTR || k == TYP_REF: + return get_type_data(ti) + return TI_INT + +// Access width for IR_DEREF/IR_STORE_PTR. Mirrors the front-end derivation +// ptr_access_width: pointee value size, falling back to one target slot. +fn corearch_nod_access_width(n: int) -> int: + w := mp_size(corearch_ptr_pointee(nod_s1(n))) + if w <= 0: + return mp_stride(TI_INT) + return w + +// Element stride for IR_ALLOC_ARRAY. IR values occupy one target slot. +fn corearch_nod_alloc_stride() -> int: + return mp_stride(TI_INT) + +// Byte extent of the allocation a runtime pointer bound refers to. The base +// variable is the destination of an ALLOC_ARRAY/ALLOC_STRUCT node, and that +// node's semantic count/struct name plus the target layout determine the size. +fn corearch_alloc_size_of_var(v: int) -> int: + if v < 0: + return 0 + ni : ., mut = 0 + loop: + if ni >= g_v7_nod_count: + break + if nod_dest(ni) == v: + aop := nod_op(ni) + if aop == IR_ALLOC_ARRAY: + return mp_extent(nod_s1(ni), TI_INT) + if aop == IR_ALLOC_STRUCT: + nm := nod_s3(ni) + si : ., mut = 0 + loop: + if si >= g_struct_count: + break + if si_name(si) == nm: + return mp_extent(si_field_count(si), TI_INT) + si = si + 1 + return mp_stride(TI_INT) + ni = ni + 1 + return 0 + +// Runtime pointer bound size for IR_DEREF/IR_STORE_PTR. The semantic flag says +// a runtime bound exists; the byte size is derived from the allocation node. +fn corearch_nod_runtime_bound_size(n: int) -> int: + fl := nod_type_flags(n) + if (fl / CCR_NOD_FLAG_RUNTIME_PTR_BOUNDS) % 2 != 1: + return 0 + op := nod_op(n) + base : ., mut = -1 + if op == IR_DEREF: + base = nod_s2(n) + else if op == IR_STORE_PTR: + base = nod_dest(n) + if base < 0: + return 0 + return corearch_alloc_size_of_var(base) + // 语义对象模型:从 NOD 重建线性调度。 // ccr_io.cr load_ccr 从内核对象缓冲顺序生成 48 字节线性流 g_ir_instrs。 -// nod_* 访问器读 g_v7_nod_sem 的 28 字节语义字段,nod_edge_first 和 nod_edge_count +// nod_* 访问器读 g_v7_nod_sem 的 32 字节语义字段,nod_edge_first 和 nod_edge_count // 读邻接域。REG 展开和函数边界回填(root span 到 g_ir_func_instr_start 和 // g_ir_func_instr_count)留在 loader。 // 调用方是 corearch.cr 和组合根 src/targets/x86_64/linux/elf/main.cr;load_ccr 成功后、 @@ -906,11 +977,21 @@ fn build_linear_schedule(): loop: if ni >= g_v7_nod_count: break - iri_set_op(ni, nod_op(ni)) + op := nod_op(ni) + iri_set_op(ni, op) iri_set_dest(ni, nod_dest(ni)) iri_set_s1(ni, nod_s1(ni)) iri_set_s2(ni, nod_s2(ni)) iri_set_s3(ni, nod_s3(ni)) iri_set_tk(ni, nod_tk(ni)) + // v10: the .ccr does not carry target facts. Reconstruct the access + // width for pointer dereferences and the element stride for allocations + // from the semantic type plus the x86-64 layout. The runtime-bound size + // for pointer stores/loads is derived from the allocation node. + if op == IR_ALLOC_ARRAY: + iri_set_s2(ni, corearch_nod_alloc_stride()) + else if op == IR_DEREF || op == IR_STORE_PTR: + iri_set_tk(ni, corearch_nod_access_width(ni)) + iri_set_s3(ni, corearch_nod_runtime_bound_size(ni)) g_ir_instr_count = ni + 1 ni = ni + 1 diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 7a482c25..91123744 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -1,16 +1,17 @@ // === ccr_io.cr === // .ccr binary serialization, the interface between corec and corearch. // -// Version 9 only. The loader checks version == 9 and rejects earlier versions; -// there is no conversion path. The format has 8 segments in canonical order: -// STR, SYM, NOD, ENT, REG, EDG, TYPE, IFACE (tags 1..8). +// Version 10 only. The loader checks version == CCR_V10_VERSION (0x000A0000) +// and rejects v9 (589825) and every other version; there is no conversion path. +// The format has 9 segments in canonical order: +// STR, TYPE, IFACE, SYM, NOD, ENT, REG, EDG, CASE (tags 1..9). // -// Header 16B: magic "CCR1", version 9, segment count 8, reserved 0. -// Segment table: 8 entries of {tag u32, offset u32, size u32}. Tags are in +// Header 16B: magic "CCR1", version 0x000A0000, segment count 9, flags. +// Segment table: 9 entries of {tag u32, offset u32, size u32}. Tags are in // canonical order, offsets are contiguous and start at the end of the table. // // STR(1): [count][{len, data}]. -// SYM(2): +// SYM(4): // globals: [count][{name u32, type u32, init_val i64}] 16B each. // functions: [count][{name u32, param_count u32, ret_type u32, // root_region i32, first_ent i32, last_ent i32} 24B @@ -23,14 +24,15 @@ // variants[variant_count]{name u32, type_count u32, // types[type_count] u32}}]. // opt metadata: [count][{key u32, len u32, data len bytes}]. -// NOD(3): [count][40B {op i32, dest i32, src1 i64, src2 i32, src3 i32, -// tk i32, item i32, first_edge u32, edge_count u32}]. -// EDG(6): [count][8B {to_nod u32, kind u32}]. -// ENT(4): [count][28B {var_id i32, version u32, def_nod i32, live_start u32, -// live_end u32, home i32, flags u32}]. -// REG(5): [count][24B {kind u32, parent i32, enter_nod u32, exit_nod u32, +// NOD(5): [count][40B {op i32, dest i32, src1 i64, src2 i32, src3 i32, +// type_id i32, flags u32, first_edge u32, edge_count u32}]. +// src2/src3 never carry width, byte offset, stride, or alignment. +// EDG(8): [count][8B {to_nod u32, kind u32}]. +// ENT(6): [count][24B {var_id i32, version u32, def_nod i32, live_start u32, +// live_end u32, flags u32}]. home is not part of v10. +// REG(7): [count][24B {kind u32, parent i32, enter_nod u32, exit_nod u32, // first_ent i32, last_ent i32}]. -// TYPE(7): type rows and the type-term DAG. +// TYPE(2): type rows and the type-term DAG. // Layout: [row_count u32] // [row_count * 24B {kind i64, data i64, extra i64}] // [term_count u32] @@ -64,9 +66,8 @@ // - The segment must end exactly at its table boundary. A mismatch rejects the // file. // -// NOD.item is an index into this term table. After TYPE loads, the loader checks -// every NOD item: item = -1 needs no term, and item >= 0 must satisfy -// tt_atom_of_term(item) == the on-disk tk code. A mismatch rejects the file. +// NOD.type_id is a TYPE row (-1 = none). The v10 loader checks every NOD +// type_id after TYPE loads: it must be -1 or an existing row. // // TYPE is required. An empty, truncated, or non-translatable TYPE segment rejects // the whole file; it is never treated as an empty table. Content is built by @@ -74,7 +75,7 @@ // judgment history. Hashes are omitted because they are a pure function of the // five fields. // -// IFACE(8): five sections, each prefixed by a u32 count. +// IFACE(3): five sections, each prefixed by a u32 count. // 1. Native entries: // [count = IFACE_ENTRY_COUNT][24B {ak i32, ti_row i32, name_ni i32, // lit_code i32, ops i64}]. @@ -93,8 +94,8 @@ // - Node ids are file order. Edges are stored per node in a contiguous range; // every to_nod is greater than the owning node. // - ENT is the verification and semantic source. version is the 1-based -// definition ordinal within a var group; live_end is half-open. home is -1 -// and flags is 0 in files produced by the current writer. +// definition ordinal within a var group; live_end is half-open. flags is 0 in +// files produced by the current writer, and v10 has no home field. // - REG is required; function instruction ranges derive from the SG_FUNC root // region span. ENT may be absent, treated as an empty table. // - TYPE and IFACE are required. The loader rejects missing segments rather @@ -109,12 +110,15 @@ // No bitwise ops in Core; use arithmetic instead. CCR_MAGIC : int = 827474755 // "CCR1" (0x31524343) -// v9.1 = (9 << 16) | 1 = 0x00090001. The loader accepts only this value. -CCR_VERSION : int = 589825 -CCR_FLAG_RAW_INT : int = 1 // header reserved field: compiler/self-host program -CCR_SEG_COUNT : int = 8 // STR SYM NOD ENT REG EDG TYPE IFACE -CCR_SEG_TYPE : int = 7 // TYPE 段 tag -CCR_SEG_IFACE : int = 8 // IFACE 段 tag +// v10 = 0x000A0000. The loader accepts only this value; v9 (589825) is rejected. +CCR_VERSION : int = 655360 // CCR_V10_VERSION (0x000A0000) +// Header bit 0 currently also carries the raw-int compiler/self-host selection. +// It is the v10 PORTABLE bit position; Task 8 removes the legacy overload. +CCR_FLAG_RAW_INT : int = 1 +CCR_FLAG_KNOWN_MASK : int = 7 // PORTABLE | SYSTEM | CONCURRENT +CCR_SEG_COUNT : int = 9 // CCR_V10_SEG_COUNT: STR TYPE IFACE SYM NOD ENT REG EDG CASE +CCR_SEG_TYPE : int = 2 // CCR_V10_SEG_TYPE +CCR_SEG_IFACE : int = 3 // CCR_V10_SEG_IFACE // Set by save_ccr from corec (compiler-source detection) and by load_ccr in the // backend. It selects the raw-int compiler path instead of user-program tagging. @@ -122,13 +126,14 @@ g_ccr_emit_raw_int : int, mut g_ccr_raw_int : int, mut // NOD record: 40 bytes. -// {op i32, dest i32, s1 i64, src2 i32, src3 i32, tk i32, item i32, +// {op i32, dest i32, s1 i64, src2 i32, src3 i32, type_id i32, flags u32, // first_edge u32, edge_count u32}. -// tk is the derived type code. item is an index into the TYPE segment term space -// (-1 means none). first_edge and edge_count locate this node's outgoing edges -// in EDG. The in-memory object record is the first 32 bytes (ESZ_NOD_SEM) -// without the adjacency fields. -ESZ_NOD_DISK : int = 40 +// type_id is a TYPE row (-1 = none); flags is the semantic-node feature word +// (CCR_NOD_FLAG_*). Widths, strides, and byte extents are target facts and are +// reconstructed by corearch, never stored here. first_edge and edge_count locate +// this node's outgoing edges in EDG. The in-memory object record keeps the same +// eight semantic fields without the adjacency pair. +ESZ_NOD_DISK : int = 40 // CCR_V10_NOD_DISK // EDG record: 8 bytes, {to_nod u32, kind u32}. The source node is implicit: // node i's outgoing edges are the contiguous range @@ -151,11 +156,11 @@ ESZ_GLOBAL_DISK : int = 16 ESZ_FUNC_HEAD_DISK : int = 24 ESZ_VARDECL_DISK : int = 8 -// On-disk ENT record: 7 × 4B = 28 bytes (var_id/version/def_nod/live_start/ -// live_end/home/flags; version + 半开 live_end 由内存 24B 表转换,见文件头注释). +// On-disk ENT record: 6 × 4B = 24 bytes (var_id/version/def_nod/live_start/ +// live_end/flags; version + 半开 live_end 由内存 24B 表转换,见文件头注释). // The in-memory entry table is ESZ_ENTRY (24 bytes, no version field,the // per-var ordinal is derivable from group order); disk record != memory record. -ESZ_ENTRY_DISK : int = 28 +ESZ_ENTRY_DISK : int = 24 // CCR_V10_ENT_DISK fn bw_byte(val: int, shift: int) -> int: if shift == 0: @@ -346,33 +351,38 @@ fn ccr_ent_seg_size() -> int: fn ccr_reg_seg_size() -> int: return 4 + g_sg_count * ESZ_SG_DISK -// TYPE(7) segment size is the prepared buffer length. The content is built by +fn ccr_case_seg_size() -> int: + // CASE is an empty table until match lowering writes it: 4B case_count = 0. + return 4 + +// TYPE(2) segment size is the prepared buffer length. The content is built by // corec-only ccr_types.cr. ccr_io.cr is shared with corearch and must not refer // to frontend symbols. The buffer includes row_count, so segment size equals // buffer length. fn ccr_type_seg_size() -> int: return g_ccr_type_seg_len -// IFACE(8) segment size is the prepared buffer length. The five sub-sections are +// IFACE(3) segment size is the prepared buffer length. The five sub-sections are // built by corec-only ccr_iface_populate and ccr_iface_seg_build in ccr_types.cr. // This file, which corearch also links, only copies and parses the segment. The // buffer includes native_count, so segment size equals buffer length. fn ccr_iface_seg_size() -> int: return g_ccr_iface_seg_len -// Size calculation: 16B header + 8*12B segment table + segment bodies. +// Size calculation: 16B header + 9*12B segment table + segment bodies. // edge_total = EDG 记录总数(save_ccr 先行收集,见 ccr_collect_edges)。 fn calc_ccr_size(edge_total: int) -> int: sz : ., mut = 16 + CCR_SEG_COUNT * 12 sz = sz + ccr_str_seg_size() + sz = sz + ccr_type_seg_size() + sz = sz + ccr_iface_seg_size() sz = sz + ccr_sym_seg_size() sz = sz + ccr_nod_seg_size() sz = sz + ccr_ent_seg_size() sz = sz + ccr_reg_seg_size() sz = sz + ccr_edg_seg_size(edge_total) - sz = sz + ccr_type_seg_size() - sz = sz + ccr_iface_seg_size() + sz = sz + ccr_case_seg_size() return sz // EDG collection on the write side. @@ -474,11 +484,12 @@ fn ccr_param_entry_id(var_id: int, es: int, ec: int) -> int: // Write-side object mirror. corec does not load through the loader, and the // kernel compute functions read NOD objects through nod_*. Before compute, copy -// g_ir_instrs into g_v7_nod_sem (28B semantic fields: op, dest, s1, s2, s3, tk, -// item). Values come from the same g_ir_instrs that save_ccr writes, after -// ccr_validate_i32_fields. The loader builds the same mirror from the file, so -// both processes call compute_entries on identical inputs and produce the same -// ENT data. +// g_ir_instrs into g_v7_nod_sem (32B semantic fields: op, dest, s1, s2, s3, +// type_id, flags). Values come from the same g_ir_instrs that save_ccr writes, +// after ccr_validate_i32_fields. The loader builds the same mirror from the file, +// so both processes call compute_entries on identical inputs and produce the same +// ENT data. type_id/flags come from the ccr_types.cr side buffers, which Task 3 +// derives from the same save-time type state. fn populate_nod_objects(): g_v7_nod_sem = alloc((g_ir_instr_count + 8) * ESZ_NOD_SEM) g_v7_nod_count = g_ir_instr_count @@ -491,15 +502,14 @@ fn populate_nod_objects(): cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, iri_s1(ii)) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, iri_s2(ii)) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, iri_s3(ii)) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, iri_tk(ii)) - // 项索引(TYPE 段文件空间;-1 = 无项)。缓冲由 corec-only - // 的 ccr_types.cr:ccr_nod_item_populate 装填(:本文件在 corearch 清单内, - // 此处不得引桥接层符号),本函数纯搬运;缓冲短于节点数(不经 - // ccr_seg_prepare_save 的诊断路径),因此落 -1(报错路径由 load 侧一致性校验承担)。 - it0 : ., mut = -1 - if ii < g_ccr_nod_item_count: - it0 = buf_read_i32(g_ccr_nod_item, ii * 4) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it0) + tid0 : ., mut = -1 + if ii < g_ccr_nod_type_count: + tid0 = buf_read_i32(g_ccr_nod_type, ii * 4) + fl0 : ., mut = 0 + if ii < g_ccr_nod_flags_count: + fl0 = buf_read_u32(g_ccr_nod_flags, ii * 4) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TYPE_ID, tid0) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_FLAGS, fl0) ii = ii + 1 // --- Save(写侧与 calc 侧一致;段表规范序、段体连续)--- @@ -553,57 +563,63 @@ fn save_ccr(path: string) -> int: buf_write_u32(buf, pos, rsv) pos = pos + 4 // reserved / raw-compiler flag - // 段体大小(与 calc_ccr_size 同一来源逐段一致) - s1 := ccr_str_seg_size() - s2 := ccr_sym_seg_size() - s3 := ccr_nod_seg_size() - s4 := ccr_ent_seg_size() - s5 := ccr_reg_seg_size() - s6 := ccr_edg_seg_size(edge_total) - s7 := ccr_type_seg_size() // 内容(缓冲长度) - s8 := ccr_iface_seg_size() // 内容(缓冲长度) - - // Seg table(8 × 12B;offset = 前段尾,从段表后起;规范序 tag 1..8) - o1 : ., mut = 16 + CCR_SEG_COUNT * 12 - o2 : ., mut = o1 + s1 - o3 : ., mut = o2 + s2 - o4 : ., mut = o3 + s3 - o5 : ., mut = o4 + s4 - o6 : ., mut = o5 + s5 - o7 : ., mut = o6 + s6 - o8 : ., mut = o7 + s7 - - buf_write_u32(buf, pos, 1) - buf_write_u32(buf, pos + 4, o1) - buf_write_u32(buf, pos + 8, s1) + // 段体大小(与 calc_ccr_size 同一来源逐段一致;canonical order) + s_str := ccr_str_seg_size() + s_type := ccr_type_seg_size() // 内容(缓冲长度) + s_iface := ccr_iface_seg_size() // 内容(缓冲长度) + s_sym := ccr_sym_seg_size() + s_nod := ccr_nod_seg_size() + s_ent := ccr_ent_seg_size() + s_reg := ccr_reg_seg_size() + s_edg := ccr_edg_seg_size(edge_total) + s_case := ccr_case_seg_size() + + // Seg table(9 × 12B;offset = 前段尾,从段表后起;规范序 tag 1..9) + o_str : ., mut = 16 + CCR_SEG_COUNT * 12 + o_type : ., mut = o_str + s_str + o_iface : ., mut = o_type + s_type + o_sym : ., mut = o_iface + s_iface + o_nod : ., mut = o_sym + s_sym + o_ent : ., mut = o_nod + s_nod + o_reg : ., mut = o_ent + s_ent + o_edg : ., mut = o_reg + s_reg + o_case : ., mut = o_edg + s_edg + + buf_write_u32(buf, pos, CCR_V10_SEG_STR) + buf_write_u32(buf, pos + 4, o_str) + buf_write_u32(buf, pos + 8, s_str) pos = pos + 12 - buf_write_u32(buf, pos, 2) - buf_write_u32(buf, pos + 4, o2) - buf_write_u32(buf, pos + 8, s2) + buf_write_u32(buf, pos, CCR_SEG_TYPE) + buf_write_u32(buf, pos + 4, o_type) + buf_write_u32(buf, pos + 8, s_type) pos = pos + 12 - buf_write_u32(buf, pos, 3) - buf_write_u32(buf, pos + 4, o3) - buf_write_u32(buf, pos + 8, s3) + buf_write_u32(buf, pos, CCR_SEG_IFACE) + buf_write_u32(buf, pos + 4, o_iface) + buf_write_u32(buf, pos + 8, s_iface) pos = pos + 12 - buf_write_u32(buf, pos, 4) - buf_write_u32(buf, pos + 4, o4) - buf_write_u32(buf, pos + 8, s4) + buf_write_u32(buf, pos, CCR_V10_SEG_SYM) + buf_write_u32(buf, pos + 4, o_sym) + buf_write_u32(buf, pos + 8, s_sym) pos = pos + 12 - buf_write_u32(buf, pos, 5) - buf_write_u32(buf, pos + 4, o5) - buf_write_u32(buf, pos + 8, s5) + buf_write_u32(buf, pos, CCR_V10_SEG_NOD) + buf_write_u32(buf, pos + 4, o_nod) + buf_write_u32(buf, pos + 8, s_nod) pos = pos + 12 - buf_write_u32(buf, pos, 6) - buf_write_u32(buf, pos + 4, o6) - buf_write_u32(buf, pos + 8, s6) + buf_write_u32(buf, pos, CCR_V10_SEG_ENT) + buf_write_u32(buf, pos + 4, o_ent) + buf_write_u32(buf, pos + 8, s_ent) pos = pos + 12 - buf_write_u32(buf, pos, CCR_SEG_TYPE) - buf_write_u32(buf, pos + 4, o7) - buf_write_u32(buf, pos + 8, s7) + buf_write_u32(buf, pos, CCR_V10_SEG_REG) + buf_write_u32(buf, pos + 4, o_reg) + buf_write_u32(buf, pos + 8, s_reg) pos = pos + 12 - buf_write_u32(buf, pos, CCR_SEG_IFACE) - buf_write_u32(buf, pos + 4, o8) - buf_write_u32(buf, pos + 8, s8) + buf_write_u32(buf, pos, CCR_V10_SEG_EDG) + buf_write_u32(buf, pos + 4, o_edg) + buf_write_u32(buf, pos + 8, s_edg) + pos = pos + 12 + buf_write_u32(buf, pos, CCR_V10_SEG_CASE) + buf_write_u32(buf, pos + 4, o_case) + buf_write_u32(buf, pos + 8, s_case) pos = pos + 12 // === STR: strings === @@ -625,6 +641,37 @@ fn save_ccr(path: string) -> int: ci = ci + 1 si = si + 1 + // === TYPE(2): copy the prepared segment buffer. Content is built by + // corec-only ccr_types.cr ccr_type_prepare_save; this file only copies it. + // 缓冲含首字段 row_count,因此大小 == ccr_type_seg_size()(同一来源)。空缓冲 = + // 未装填(调用点漏调 prepare),因此拒绝落盘:不得产出两小节缺席的 + // 半成品文件(判定约定;记录表恒含原生 9 行,空缓冲只可能是程序错误)。 + if g_ccr_type_seg_len <= 0: + return -1 + ct : ., mut = 0 + loop: + if ct >= g_ccr_type_seg_len: + break + cell_store_byte(buf, pos, cell_load_byte(g_ccr_type_seg, ct)) + pos = pos + 1 + ct = ct + 1 + + // === IFACE(3): copy the prepared segment buffer. Content is built by + // corec-only ccr_iface_populate and ccr_iface_seg_build in ccr_types.cr; + // this file only copies it. + // 缓冲含首字段 native_count,因此大小 == ccr_iface_seg_size()(同一来源)。空缓冲 = + // 未装填(调用点漏调 prepare),因此拒绝落盘(不得产出五小节缺席的半成品; + // 条目表恒 16 行,因此空缓冲只可能是程序错误,判定约定)。 + if g_ccr_iface_seg_len <= 0: + return -1 + ict : ., mut = 0 + loop: + if ict >= g_ccr_iface_seg_len: + break + cell_store_byte(buf, pos, cell_load_byte(g_ccr_iface_seg, ict)) + pos = pos + 1 + ict = ict + 1 + // === SYM: globals(16B each: name u32, type u32, init_val i64)=== // The global record stores type, not var_idx: var_idx always equals the row // order because reg_one_global creates one var row per global and keeps row @@ -819,15 +866,19 @@ fn save_ccr(path: string) -> int: // === NOD: instructions === // 40B each. NOD id is file order and equals global instruction order. Each - // record has the 32B semantic fields plus first_edge and edge_count for the - // adjacency range. EDG is written from edge_buf after the NOD body. + // record has the eight semantic fields plus first_edge and edge_count for the + // adjacency range. src2/src3 never carry a width, stride, or byte extent: + // IR_ALLOC_ARRAY's stride and IR_ADDR_INDEX's scale are target facts, and + // IR_DEREF/IR_STORE_PTR store only the runtime-base reference. EDG is written + // from edge_buf after the NOD body. buf_write_u32(buf, pos, g_ir_instr_count) pos = pos + 4 ii : ., mut = 0 loop: if ii >= g_ir_instr_count: break - buf_write_u32(buf, pos, iri_op(ii)) + nop := iri_op(ii) + buf_write_u32(buf, pos, nop) pos = pos + 4 buf_write_i32(buf, pos, iri_dest(ii)) pos = pos + 4 @@ -835,18 +886,33 @@ fn save_ccr(path: string) -> int: // not truncated to 32 bits. buf_write_i64(buf, pos, iri_s1(ii)) pos = pos + 8 - buf_write_i32(buf, pos, iri_s2(ii)) - pos = pos + 4 - buf_write_i32(buf, pos, iri_s3(ii)) - pos = pos + 4 - buf_write_u32(buf, pos, iri_tk(ii)) - pos = pos + 4 - // 项索引(i32;-1 = 无项),与内存镜像同源 - // (g_ccr_nod_item,corec-only 侧装填;本文件纯搬运)。 - it1 : ., mut = -1 - if ii < g_ccr_nod_item_count: - it1 = buf_read_i32(g_ccr_nod_item, ii * 4) - buf_write_i32(buf, pos, it1) + ns2 : ., mut = iri_s2(ii) + ns3 : ., mut = iri_s3(ii) + if nop == IR_ALLOC_ARRAY: + // element stride is a target fact; corearch reconstructs it + ns2 = 0 + ns3 = 0 + else if nop == IR_ADDR_INDEX: + // SIB scale exponent is a target fact; the instance fixes it + ns3 = 0 + else if nop == IR_DEREF || nop == IR_STORE_PTR: + // allocation byte size is a target fact; the semantic flag records + // that a runtime bound exists and corearch recomputes the size + ns3 = 0 + buf_write_i32(buf, pos, ns2) + pos = pos + 4 + buf_write_i32(buf, pos, ns3) + pos = pos + 4 + // type_id: TYPE row (-1 = none). flags: semantic node features only. + tid1 : ., mut = -1 + if ii < g_ccr_nod_type_count: + tid1 = buf_read_i32(g_ccr_nod_type, ii * 4) + buf_write_i32(buf, pos, tid1) + pos = pos + 4 + fl1 : ., mut = 0 + if ii < g_ccr_nod_flags_count: + fl1 = buf_read_u32(g_ccr_nod_flags, ii * 4) + buf_write_u32(buf, pos, fl1) pos = pos + 4 buf_write_u32(buf, pos, cell_load(edge_offs, ii * 8)) pos = pos + 4 // first_edge @@ -854,7 +920,7 @@ fn save_ccr(path: string) -> int: pos = pos + 4 // edge_count ii = ii + 1 - // === ENT: entries(28B each;内存闭区间 -> 盘上:version 组内序数、live_end+1 半开)=== + // === ENT: entries(24B each;内存闭区间 -> 盘上:version 组内序数、live_end+1 半开)=== // 版本序数:每 var 一张计数槽(var 全局槽 id 唯一属一个函数,组内序 = 全局序) buf_write_u32(buf, pos, g_entry_count) pos = pos + 4 @@ -873,7 +939,6 @@ fn save_ccr(path: string) -> int: ed := ccr_ent_def(ej) els := ccr_ent_ls(ej) ele := ccr_ent_le(ej) - eho := ccr_ent_home(ej) efl := ccr_ent_flags(ej) ord := cell_load(vcnt, ev * 8) + 1 cell_store(vcnt, ev * 8, ord) @@ -887,10 +952,8 @@ fn save_ccr(path: string) -> int: pos = pos + 4 // live_start(含定值) buf_write_u32(buf, pos, ele + 1) pos = pos + 4 // live_end(半开 = 内存闭区间 +1) - buf_write_i32(buf, pos, eho) - pos = pos + 4 // home(-1 = 未分配) buf_write_u32(buf, pos, efl) - pos = pos + 4 // flags + pos = pos + 4 // flags;home 不属于 v10 ej = ej + 1 // === REG: regions === @@ -985,36 +1048,10 @@ fn save_ccr(path: string) -> int: pos = pos + 4 // kind ei3 = ei3 + 1 - // === TYPE(7): copy the prepared segment buffer. Content is built by - // corec-only ccr_types.cr ccr_type_prepare_save; this file only copies it. - // 缓冲含首字段 row_count,因此大小 == ccr_type_seg_size()(同一来源)。空缓冲 = - // 未装填(调用点漏调 prepare),因此拒绝落盘:不得产出两小节缺席的 - // 半成品文件(判定约定;记录表恒含原生 9 行,空缓冲只可能是程序错误)。 - if g_ccr_type_seg_len <= 0: - return -1 - ct : ., mut = 0 - loop: - if ct >= g_ccr_type_seg_len: - break - cell_store_byte(buf, pos, cell_load_byte(g_ccr_type_seg, ct)) - pos = pos + 1 - ct = ct + 1 - - // === IFACE(8): copy the prepared segment buffer. Content is built by - // corec-only ccr_iface_populate and ccr_iface_seg_build in ccr_types.cr; - // this file only copies it. - // 缓冲含首字段 native_count,因此大小 == ccr_iface_seg_size()(同一来源)。空缓冲 = - // 未装填(调用点漏调 prepare),因此拒绝落盘(不得产出五小节缺席的半成品; - // 条目表恒 16 行,因此空缓冲只可能是程序错误,判定约定)。 - if g_ccr_iface_seg_len <= 0: - return -1 - ict : ., mut = 0 - loop: - if ict >= g_ccr_iface_seg_len: - break - cell_store_byte(buf, pos, cell_load_byte(g_ccr_iface_seg, ict)) - pos = pos + 1 - ict = ict + 1 + // === CASE: empty table for now (4B case_count = 0). CASE is last because it + // may reference every NOD target once match lowering fills it in. + buf_write_u32(buf, pos, 0) + pos = pos + 4 // Use syscall directly (write_file uses str_len which stops at null) fd := syscall3(2, path, 577, 420) // open(O_WRONLY|O_CREAT|O_TRUNC, 0644) @@ -1043,18 +1080,20 @@ fn inject_var_shift() -> int: cell_store(g_ir_func_var_start, 0, vs - 1) return 0 -// Load: v9 only. Validate the header and canonical segment table, reject -// out-of-range segments, and reject any version other than 9. The table has 8 -// segments in canonical order. TYPE and IFACE are required. +// Load: v10 only. Validate the header and canonical segment table, reject +// out-of-range segments, and reject every version other than 0x000A0000 (v9 is +// rejected outright). The table has 9 segments in canonical order. All nine are +// required; ENT may carry an empty record table but its 4B count is present. // Loader output is semantic objects plus guards. The linear NOD-to-g_ir_instrs // rebuild is outside this file: build_linear_schedule in regalloc.cr is called by // corearch.cr or src/targets/x86_64/linux/elf/main.cr after a successful load. -// Parse order: STR -> SYM (globals and funcs rebuild the var namespace) -> REG -// (derive nstart/ncount and function instruction bounds) -> NOD (40B; 28B -// semantic fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT (28B -// to the in-memory 24B table; drop version and convert half-open live_end back to +// Parse order (dependencies, not file order; segment offsets are random-access): +// STR -> SYM (globals and funcs rebuild the var namespace) -> REG (derive +// nstart/ncount and function instruction bounds) -> NOD (40B; eight semantic +// fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT (24B to the +// in-memory 24B table; drop version and convert half-open live_end back to // closed) -> EDG (contiguous ranges and topology checks into g_v7_edges) -> -// TYPE/IFACE (rebuild type rows and terms, and interface tables). +// TYPE/IFACE (rebuild type rows and terms, and interface tables) -> CASE. // Guards use only NOD counts, adjacency, and REG-derived bounds; the loader does // not depend on the linear stream. @@ -1071,26 +1110,30 @@ fn load_ccr(data: string, fsize: int) -> int: if magic != CCR_MAGIC: return -1 - // Version: v9 only, with no compatibility or conversion path. + // Version: v10 only, with no compatibility or conversion path. ver := buf_read_u32(data, pos) pos = pos + 4 if ver != CCR_VERSION: return -1 - // Seg count + reserved + // Seg count + header flags seg_cnt := buf_read_u32(data, pos) pos = pos + 4 rsv := buf_read_u32(data, pos) pos = pos + 4 - if rsv != 0 && rsv != CCR_FLAG_RAW_INT: + if seg_cnt != CCR_SEG_COUNT: + return -1 + // Only PORTABLE/SYSTEM/CONCURRENT (bits 0..2) are defined; reserved bits + // must be zero. Bit 0 also selects legacy raw-int mode until Task 8. + if rsv > CCR_FLAG_KNOWN_MASK: return -1 g_ccr_raw_int = 0 - if rsv == CCR_FLAG_RAW_INT: + if rsv % 2 == 1: g_ccr_raw_int = 1 if seg_cnt > (fsize - 16) / 12: return -1 - // Segment table: canonical layout. tag must equal the row number (1..8), + // Segment table: canonical layout. tag must equal the row number (1..9), // offset must equal the previous segment end, and segments must stay in // bounds. The loader rejects any non-canonical layout. seg_off1 : ., mut = 0 @@ -1101,6 +1144,7 @@ fn load_ccr(data: string, fsize: int) -> int: seg_off6 : ., mut = 0 seg_off7 : ., mut = 0 seg_off8 : ., mut = 0 + seg_off9 : ., mut = 0 seg_end1 : ., mut = 0 seg_end2 : ., mut = 0 seg_end3 : ., mut = 0 @@ -1109,6 +1153,7 @@ fn load_ccr(data: string, fsize: int) -> int: seg_end6 : ., mut = 0 seg_end7 : ., mut = 0 seg_end8 : ., mut = 0 + seg_end9 : ., mut = 0 have1 : ., mut = 0 have2 : ., mut = 0 have3 : ., mut = 0 @@ -1117,6 +1162,7 @@ fn load_ccr(data: string, fsize: int) -> int: have6 : ., mut = 0 have7 : ., mut = 0 have8 : ., mut = 0 + have9 : ., mut = 0 cursor : ., mut = 16 + seg_cnt * 12 ri : ., mut = 0 loop: @@ -1178,32 +1224,36 @@ fn load_ccr(data: string, fsize: int) -> int: seg_off6 = soff seg_end6 = soff + ssz have6 = 1 - if tg == CCR_SEG_TYPE: + if tg == 7: if have7 != 0: return -1 seg_off7 = soff seg_end7 = soff + ssz have7 = 1 - if tg == CCR_SEG_IFACE: + if tg == 8: if have8 != 0: return -1 seg_off8 = soff seg_end8 = soff + ssz have8 = 1 + if tg == 9: + if have9 != 0: + return -1 + seg_off9 = soff + seg_end9 = soff + ssz + have9 = 1 cursor = soff + ssz ri = ri + 1 - // STR, SYM, NOD, REG, EDG, TYPE, and IFACE are required. REG is the only - // source for function instruction bounds. ENT may be absent and is treated as - // an empty table. TYPE and IFACE are required so there are not two .ccr - // variants in the wild. - if have1 == 0 || have2 == 0 || have3 == 0 || have5 == 0 || have6 == 0: + // All nine segments are required in v10. TYPE/IFACE are content segments; + // ENT/REG/EDG/CASE are structural. There is no "absent = empty" variant. + if have1 == 0 || have2 == 0 || have3 == 0 || have4 == 0 || have5 == 0: + return -1 + if have6 == 0 || have7 == 0 || have8 == 0 || have9 == 0: return -1 - if have7 == 0 || have8 == 0: + if cursor != fsize: return -1 - if have4 == 0: - seg_off4 = 0 - seg_end4 = 0 + // 无尾随字节(最后一段尾 == 文件大小) // 状态重置(corearch 单次加载;保持可重入) g_str_count = 0 @@ -1265,19 +1315,19 @@ fn load_ccr(data: string, fsize: int) -> int: // var 命名空间重建(流式):全局行 = 记录序 0..G-1(行名/型随本记录携带, // var rows are the same source); id = row order. Function declaration areas // follow in row order. - pos = seg_off2 - if !ccr_has_bytes(pos, 4, seg_end2): + pos = seg_off4 + if !ccr_has_bytes(pos, 4, seg_end4): return -1 gc := buf_read_u32(data, pos) pos = pos + 4 - if gc > (seg_end2 - seg_off2) / ESZ_GLOBAL_DISK: + if gc > (seg_end4 - seg_off4) / ESZ_GLOBAL_DISK: return -1 grow_ir_globals(gc) gi : ., mut = 0 loop: if gi >= gc: break - if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end2): + if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end4): return -1 gname_ni := buf_read_u32(data, pos) pos = pos + 4 @@ -1301,11 +1351,11 @@ fn load_ccr(data: string, fsize: int) -> int: // 头字段 = spec 3.2:name/param_count/ret_type/root_region(REG 记录 id)/ // first_ent/last_ent(本函数条目文件范围)。instr/var 范围不落盘:instr = // root_region span(REG 段解析后回填),var_start = 声明区记录顺序游标。 - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 func_cnt := buf_read_u32(data, pos) pos = pos + 4 - if func_cnt > (seg_end2 - pos) / ESZ_FUNC_HEAD_DISK: + if func_cnt > (seg_end4 - pos) / ESZ_FUNC_HEAD_DISK: return -1 grow_ir_func_meta(func_cnt) // func 记录临时元数据(REG 段回填/ENT 块界校验用):{root i64, first i64, last i64} @@ -1314,7 +1364,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if fi >= func_cnt: break - if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end2): + if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end4): return -1 fname := buf_read_u32(data, pos) pos = pos + 4 @@ -1344,7 +1394,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if pj >= fpc: break - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 pid := buf_read_i32(data, pos) pos = pos + 4 @@ -1352,13 +1402,13 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 pj = pj + 1 // var 声明区(记录顺序 = 创建序,前 param_count 个 = 参数) - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 fvc := buf_read_u32(data, pos) pos = pos + 4 if fpc > fvc: return -1 - if fvc > (seg_end2 - pos) / ESZ_VARDECL_DISK: + if fvc > (seg_end4 - pos) / ESZ_VARDECL_DISK: return -1 fvs2 := g_ir_var_count cell_store(g_ir_func_var_start, fi * 8, fvs2) @@ -1366,7 +1416,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if dj >= fvc: break - if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end2): + if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end4): return -1 dname := buf_read_u32(data, pos) pos = pos + 4 @@ -1386,18 +1436,18 @@ fn load_ccr(data: string, fsize: int) -> int: fi = fi + 1 // === SYM: string constants(4B each)=== - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 str_const_cnt := buf_read_u32(data, pos) pos = pos + 4 - if str_const_cnt > (seg_end2 - seg_off2) / 4: + if str_const_cnt > (seg_end4 - seg_off4) / 4: return -1 grow_ir_str_consts(str_const_cnt) sci : ., mut = 0 loop: if sci >= str_const_cnt: break - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 scv := buf_read_u32(data, pos) pos = pos + 4 @@ -1406,11 +1456,11 @@ fn load_ccr(data: string, fsize: int) -> int: sci = sci + 1 // === SYM: structs === - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 struct_cnt := buf_read_u32(data, pos) pos = pos + 4 - if struct_cnt > (seg_end2 - seg_off2) / 8: + if struct_cnt > (seg_end4 - seg_off4) / 8: return -1 grow_structs(struct_cnt) g_struct_count = struct_cnt @@ -1418,7 +1468,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if sti >= struct_cnt: break - if !ccr_has_bytes(pos, 8, seg_end2): + if !ccr_has_bytes(pos, 8, seg_end4): return -1 sname_ni := buf_read_u32(data, pos) pos = pos + 4 @@ -1426,7 +1476,7 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // 字段数无硬上限(侧表承载),因此旧 MAX_STRUCT_FIELDS // 读回检查与 16 槽清零循环一并删除;段界核算保留(fc 越段,因此拒绝,判定约定)。 - if fc > (seg_end2 - pos) / 8: + if fc > (seg_end4 - pos) / 8: return -1 cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni) cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) @@ -1455,11 +1505,11 @@ fn load_ccr(data: string, fsize: int) -> int: sti = sti + 1 // === SYM: enums === - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 enum_cnt := buf_read_u32(data, pos) pos = pos + 4 - if enum_cnt > (seg_end2 - seg_off2) / 8: + if enum_cnt > (seg_end4 - seg_off4) / 8: return -1 grow_enums(enum_cnt) g_enum_count = enum_cnt @@ -1467,7 +1517,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if ei >= enum_cnt: break - if !ccr_has_bytes(pos, 8, seg_end2): + if !ccr_has_bytes(pos, 8, seg_end4): return -1 ename_ni := buf_read_u32(data, pos) pos = pos + 4 @@ -1475,7 +1525,7 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // 变体/携带值数无硬上限(侧表),因此两处旧检查与 16 槽清零 // 循环删除;段界核算保留(判定约定) - if vc > (seg_end2 - pos) / 8: + if vc > (seg_end4 - pos) / 8: return -1 cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni) cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) @@ -1496,7 +1546,7 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 tc := buf_read_u32(data, pos) pos = pos + 4 - if tc > (seg_end2 - pos) / 4: + if tc > (seg_end4 - pos) / 4: return -1 ei_set_variant_name(ei, vi3, vni) ei_set_variant_type_count(ei, vi3, tc) @@ -1512,7 +1562,7 @@ fn load_ccr(data: string, fsize: int) -> int: ei = ei + 1 // === SYM: opt_meta === - if !ccr_has_bytes(pos, 4, seg_end2): + if !ccr_has_bytes(pos, 4, seg_end4): return -1 mc := buf_read_u32(data, pos) pos = pos + 4 @@ -1520,13 +1570,13 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if mi >= mc: break - if !ccr_has_bytes(pos, 8, seg_end2): + if !ccr_has_bytes(pos, 8, seg_end4): return -1 mk := buf_read_u32(data, pos) pos = pos + 4 md_len := buf_read_u32(data, pos) pos = pos + 4 - if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end2): + if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end4): return -1 // Allocate and store entry grow_opt_meta(mi + 1) @@ -1551,12 +1601,12 @@ fn load_ccr(data: string, fsize: int) -> int: // first/last 校验:SG_FUNC 根记录(记录顺序第 k 个 = 函数 k)的区内条目范围必须 // 与 SYM func 记录 first/last 一致(同信息双写,失配 = 格式不一致)。 // 函数指令边界重建:func root_region(SYM)-> REG span = [enter, exit)。 - pos = seg_off5 - if !ccr_has_bytes(pos, 4, seg_end5): + pos = seg_off7 + if !ccr_has_bytes(pos, 4, seg_end7): return -1 sg_n := buf_read_u32(data, pos) pos = pos + 4 - if sg_n > (seg_end5 - seg_off5) / ESZ_SG_DISK: + if sg_n > (seg_end7 - seg_off7) / ESZ_SG_DISK: return -1 sg_i : ., mut = 0 rfcnt : ., mut = 0 // 已见 SG_FUNC 根记录数(记录顺序 = 函数序 1:1) @@ -1622,21 +1672,24 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store(g_ir_func_instr_count, bfi * 8, rex - ren) bfi = bfi + 1 - // === NOD: instructions(36B each)=== - // loader 产出 = 对象载入 + 检查,28B + // === NOD: instructions(40B each)=== + // loader 产出 = 对象载入 + 检查,32B // Semantic fields go to the kernel object buffer g_v7_nod_sem (the in-memory // record mirrors the disk record without adjacency, with the same offsets). - // Adjacency first_edge/edge_count go to g_v7_nod_meta. + // type_id is a TYPE row (-1 = none); flags is the semantic feature word. + // src2/src3 are opaque auxiliary ids: the producer never writes a width, + // stride, or byte extent there. Adjacency first_edge/edge_count go to + // g_v7_nod_meta. // (EDG 段校验 + 对象数据 nod_edge_first/count 消费)。NOD->g_ir_instrs // 线性重建段已移出本函数(调度重建 = 实例事务 build_linear_schedule, // regalloc.cr,load 返回后、发射/分派前由入口调用);g_ir_instrs/ // g_ir_instr_count 本段不再触碰(重建段原写点全清)。 - pos = seg_off3 - if !ccr_has_bytes(pos, 4, seg_end3): + pos = seg_off5 + if !ccr_has_bytes(pos, 4, seg_end5): return -1 instr_cnt := buf_read_u32(data, pos) pos = pos + 4 - if instr_cnt > (seg_end3 - seg_off3) / ESZ_NOD_DISK: + if instr_cnt > (seg_end5 - seg_off5) / ESZ_NOD_DISK: return -1 g_v7_nod_sem = alloc((instr_cnt + 8) * ESZ_NOD_SEM) g_v7_nod_meta = alloc((instr_cnt + 8) * ESZ_NOD_META) @@ -1644,7 +1697,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if ii >= instr_cnt: break - if !ccr_has_bytes(pos, ESZ_NOD_DISK, seg_end3): + if !ccr_has_bytes(pos, ESZ_NOD_DISK, seg_end5): return -1 opcode := buf_read_u32(data, pos) pos = pos + 4 @@ -1656,14 +1709,18 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 s3 := buf_read_i32(data, pos) pos = pos + 4 - tk := buf_read_u32(data, pos) - pos = pos + 4 - it2 := buf_read_i32(data, pos) - pos = pos + 4 // 项索引(i32;-1 = 无项) + tid2 := buf_read_i32(data, pos) + pos = pos + 4 // type_id(TYPE row;-1 = 无) + fl2 := buf_read_u32(data, pos) + pos = pos + 4 // flags(语义特性;非目标事实) nfe := buf_read_u32(data, pos) pos = pos + 4 // first_edge(邻接索引) nec := buf_read_u32(data, pos) pos = pos + 4 // edge_count + // Reserved flag bits must be zero. A future feature bit needs a reader, + // so rejecting here keeps old loaders from silently ignoring meaning. + if fl2 > CCR_NOD_FLAG_KNOWN_MASK: + return -1 // 对象缓冲写(语义字段 = 盘字节镜像,w32/w64 低 32/64 位存储,存取 // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) @@ -1671,8 +1728,8 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TK, tk) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_ITEM, it2) // 项索引(-1 = 无) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TYPE_ID, tid2) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_FLAGS, fl2) cell_store(g_v7_nod_meta, ii * ESZ_NOD_META, nfe) cell_store(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec) ii = ii + 1 @@ -1694,22 +1751,24 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 rfi = rfi + 1 - // === ENT: entries(28B each -> 内存 24B 表)=== + // === ENT: entries(24B each -> 内存 24B 表)=== // 盘上半开 live_end -> 内存闭区间 −1;version 字段不入内存(组内序可推)。 - if have4 != 0: - pos = seg_off4 - if !ccr_has_bytes(pos, 4, seg_end4): + // v10 has no home field. The in-memory slot stays -1 until Task 8 retires it; + // the loader never reads that slot from disk. + if have6 != 0: + pos = seg_off6 + if !ccr_has_bytes(pos, 4, seg_end6): return -1 ent_cnt := buf_read_u32(data, pos) pos = pos + 4 - if ent_cnt > (seg_end4 - seg_off4) / ESZ_ENTRY_DISK: + if ent_cnt > (seg_end6 - seg_off6) / ESZ_ENTRY_DISK: return -1 grow_entries(ent_cnt) eii : ., mut = 0 loop: if eii >= ent_cnt: break - if !ccr_has_bytes(pos, ESZ_ENTRY_DISK, seg_end4): + if !ccr_has_bytes(pos, ESZ_ENTRY_DISK, seg_end6): return -1 ev := buf_read_i32(data, pos) pos = pos + 4 // var_id @@ -1721,8 +1780,6 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // live_start(含定值) ele := buf_read_u32(data, pos) pos = pos + 4 // live_end(半开) - eho := buf_read_i32(data, pos) - pos = pos + 4 // home efl := buf_read_u32(data, pos) pos = pos + 4 // flags // Semantic validation: reject out-of-range values. @@ -1745,7 +1802,7 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store32(g_ir_entries, mo2 + OFF_ENTRY_DEF, ed) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LS, els) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LE, ele - 1) - cell_store32(g_ir_entries, mo2 + OFF_ENTRY_HOME, eho) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_HOME, -1) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl) g_entry_count = eii + 1 eii = eii + 1 @@ -1795,12 +1852,12 @@ fn load_ccr(data: string, fsize: int) -> int: // 校验通过后边表入 g_v7_edges 缓冲(文件序连续段,图语义消费通道/调试)。 // branch/jump 目标 = 操作数引用非边(v7 4 边界声明),NOD 操作数不参与 // 本段校验。 - pos = seg_off6 - if !ccr_has_bytes(pos, 4, seg_end6): + pos = seg_off8 + if !ccr_has_bytes(pos, 4, seg_end8): return -1 edg_cnt := buf_read_u32(data, pos) pos = pos + 4 - if edg_cnt > (seg_end6 - seg_off6) / ESZ_EDGE_DISK: + if edg_cnt > (seg_end8 - seg_off8) / ESZ_EDGE_DISK: return -1 v7_buf : string, mut = "" if edg_cnt > 0: @@ -1827,7 +1884,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if rec >= nec: break - if !ccr_has_bytes(pos, ESZ_EDGE_DISK, seg_end6): + if !ccr_has_bytes(pos, ESZ_EDGE_DISK, seg_end8): return -1 eto := buf_read_u32(data, pos) pos = pos + 4 @@ -1849,7 +1906,7 @@ fn load_ccr(data: string, fsize: int) -> int: if run_off != edg_cnt: return -1 // 2 Σ edge_count == edg_count - if pos != seg_end6: + if pos != seg_end8: return -1 // 2 行走完 == 段体大小 @@ -1859,7 +1916,7 @@ fn load_ccr(data: string, fsize: int) -> int: // g_tt_index 索引重放(grow_tt_index -> tt_reindex)。 // 逐条校验(违规 = 拒绝,不得当作空表或截断表,判定约定 C.5-3): // 1 计数/长度自洽(计数 × 记录尺寸 <= 段余量);2 段体恰两小节(行走完 - // == seg_end7,无尾随字节);3 tag in 0..10(TT_*)+ a..d >= -1;4 子项引用 + // == seg_end2,无尾随字节);3 tag in 0..10(TT_*)+ a..d >= -1;4 子项引用 // -1 或 < 自身行号(拓扑无环;标注槽按 tag 分派跳过,字段表见 // ccr_type_term_ref_ok,ATOM 的 b = 自类型表记录编号可达数千,不得误拒)。 // 复位:记录表/项层(含引擎预算/memo/lits,见 tt_layer_reset 注记)全部作废 @@ -1868,19 +1925,19 @@ fn load_ccr(data: string, fsize: int) -> int: // 未来判定原语),本处不做语义解释。 g_type_count = 0 tt_layer_reset() - pos = seg_off7 - if !ccr_has_bytes(pos, 4, seg_end7): + pos = seg_off2 + if !ccr_has_bytes(pos, 4, seg_end2): return -1 trow_cnt := buf_read_u32(data, pos) pos = pos + 4 - if trow_cnt > (seg_end7 - pos) / ESZ_TYPE_ROW: + if trow_cnt > (seg_end2 - pos) / ESZ_TYPE_ROW: return -1 grow_types(trow_cnt) trow : ., mut = 0 loop: if trow >= trow_cnt: break - if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end7): + if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end2): return -1 cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)) pos = pos + 8 @@ -1890,18 +1947,18 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 8 trow = trow + 1 g_type_count = trow_cnt - if !ccr_has_bytes(pos, 4, seg_end7): + if !ccr_has_bytes(pos, 4, seg_end2): return -1 tterm_cnt := buf_read_u32(data, pos) pos = pos + 4 - if tterm_cnt > (seg_end7 - pos) / ESZ_TYPE_TERM_DISK: + if tterm_cnt > (seg_end2 - pos) / ESZ_TYPE_TERM_DISK: return -1 grow_type_terms(tterm_cnt + 1) tj : ., mut = 0 loop: if tj >= tterm_cnt: break - if !ccr_has_bytes(pos, ESZ_TYPE_TERM_DISK, seg_end7): + if !ccr_has_bytes(pos, ESZ_TYPE_TERM_DISK, seg_end2): return -1 ttag := buf_read_i64(data, pos) pos = pos + 8 @@ -1927,7 +1984,7 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)) g_type_term_count = tj + 1 tj = tj + 1 - if pos != seg_end7: + if pos != seg_end2: return -1 // 段体恰两小节(无尾随字节) grow_tt_index(g_type_term_count + 1) // 索引重建(cap 已清零 -> 必走重建) @@ -1938,7 +1995,7 @@ fn load_ccr(data: string, fsize: int) -> int: // (ESZ_IFACEINFO 布局;corearch 侧无 AST,因此节点槽恒 -1、项槽 = 段值) // + g_impl_for + g_methods。 // 逐条校验(违规 = 拒绝;判定约定 C.5-3,不得当作空表或截断表): - // 1 五小节计数/长度自洽(计数 × 记录尺寸 <= 段余量)+ 行走完 == seg_end8(无尾随); + // 1 五小节计数/长度自洽(计数 × 记录尺寸 <= 段余量)+ 行走完 == seg_end3(无尾随); // 2 native_count == IFACE_ENTRY_COUNT(表内容 = 常量表,计数漂移 = 损坏); // 3 跨段引用域:name_ni/type_ni/method_ni/mangled_ni in [0, g_str_count)(STR // 段先于本段解析);ti_row in {-1} ∪ [0, g_type_count);term in {-1} ∪ [0, tt_count()) @@ -1946,14 +2003,14 @@ fn load_ccr(data: string, fsize: int) -> int: // 4 结构域:ak in 0..AK_NULL、method_count <= MAX_IFACE_METHODS、 // param_count <= MAX_IFACE_METHOD_PARAMS、self_mode in 0..3。 // IFACE 段 = 纯信息(不参与 ELF 发射);本段解析失败 = 整体拒绝(load_ccr -1)。 - pos = seg_off8 - if !ccr_has_bytes(pos, 4, seg_end8): + pos = seg_off3 + if !ccr_has_bytes(pos, 4, seg_end3): return -1 ifc_nat := buf_read_u32(data, pos) pos = pos + 4 if ifc_nat != IFACE_ENTRY_COUNT: return -1 - if !ccr_has_bytes(pos, ifc_nat * 24, seg_end8): + if !ccr_has_bytes(pos, ifc_nat * 24, seg_end3): return -1 g_iface_entries = alloc(ifc_nat * ESZ_IFACE_ENTRY) ifr : ., mut = 0 @@ -1992,11 +2049,11 @@ fn load_ccr(data: string, fsize: int) -> int: g_iface_registry_ok = 1 // 2 横切形状(名 ni + 形状项) - if !ccr_has_bytes(pos, 4, seg_end8): + if !ccr_has_bytes(pos, 4, seg_end3): return -1 ifc_shn := buf_read_u32(data, pos) pos = pos + 4 - if ifc_shn > (seg_end8 - pos) / 8: + if ifc_shn > (seg_end3 - pos) / 8: return -1 iface_shape_grow(ifc_shn) ifs : ., mut = 0 @@ -2018,18 +2075,18 @@ fn load_ccr(data: string, fsize: int) -> int: g_iface_shape_count = ifc_shn // 3 用户接口(头 16B + 方法 80B × method_count) - if !ccr_has_bytes(pos, 4, seg_end8): + if !ccr_has_bytes(pos, 4, seg_end3): return -1 ifc_usn := buf_read_u32(data, pos) pos = pos + 4 - if ifc_usn > (seg_end8 - pos) / 16: + if ifc_usn > (seg_end3 - pos) / 16: return -1 grow_ifaces(ifc_usn) ifu : ., mut = 0 loop: if ifu >= ifc_usn: break - if !ccr_has_bytes(pos, 16, seg_end8): + if !ccr_has_bytes(pos, 16, seg_end3): return -1 uname := buf_read_i32(data, pos) pos = pos + 4 @@ -2044,7 +2101,7 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 if ugc < 0: return -1 - if !ccr_has_bytes(pos, umc * 80, seg_end8): + if !ccr_has_bytes(pos, umc * 80, seg_end3): return -1 ub := ifu * ESZ_IFACEINFO // 先清零整条记录(防上轮残留;节点槽/项槽的初值 = -1 由下方逐槽写) @@ -2111,11 +2168,11 @@ fn load_ccr(data: string, fsize: int) -> int: g_iface_count = ifc_usn // 4 impl 边(g_impl_for:{trait_ni, type_ni},声明元数据) - if !ccr_has_bytes(pos, 4, seg_end8): + if !ccr_has_bytes(pos, 4, seg_end3): return -1 ifc_imp := buf_read_u32(data, pos) pos = pos + 4 - if ifc_imp > (seg_end8 - pos) / 8: + if ifc_imp > (seg_end3 - pos) / 8: return -1 grow_impl_for(ifc_imp) ifi : ., mut = 0 @@ -2136,11 +2193,11 @@ fn load_ccr(data: string, fsize: int) -> int: g_impl_for_count = ifc_imp // 5 方法表(g_methods:{type_ni, method_ni, mangled_ni},iface_find_method 数据源) - if !ccr_has_bytes(pos, 4, seg_end8): + if !ccr_has_bytes(pos, 4, seg_end3): return -1 ifc_met := buf_read_u32(data, pos) pos = pos + 4 - if ifc_met > (seg_end8 - pos) / 12: + if ifc_met > (seg_end3 - pos) / 12: return -1 grow_methods(ifc_met) ifm : ., mut = 0 @@ -2165,31 +2222,34 @@ fn load_ccr(data: string, fsize: int) -> int: ifm = ifm + 1 g_method_count = ifc_met - if pos != seg_end8: + if pos != seg_end3: return -1 // 五小节行走完 == 段体(无尾随字节) - // === NOD item index consistency check === - // This check runs after TYPE/IFACE because it needs the term table. The NOD - // segment (canonical order 3) is parsed before TYPE (7), so the check cannot - // run there. - // Reject item < -1, item >= tt_count, and item >= 0 when - // tt_atom_of_term(item) differs from the on-disk code. item = -1 needs no - // check: composite rows, aux-face rows, and warm rows without a face carry - // their code in tk. Valid files always pass because both values come from the - // same ccr_types.cr ccr_nod_item_populate derivation. + // === CASE: empty table for now === + // The loader requires case_count == 0 until match lowering writes CASE. Any + // other count is an unknown shape, so reject instead of treating it as empty. + pos = seg_off9 + if !ccr_has_bytes(pos, 4, seg_end9): + return -1 + case_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if case_cnt != 0: + return -1 + if pos != seg_end9: + return -1 + + // === NOD type_id domain check === + // This runs after TYPE because it needs the row count. NOD is parsed before + // TYPE in dependency order, so the check cannot run there. type_id is -1 or a + // TYPE row; anything else is an unknown type domain and rejects the file. ni : ., mut = 0 loop: if ni >= g_v7_nod_count: break - itv := nod_item(ni) - if itv < -1: + tidv := nod_type_id(ni) + if tidv < -1 || tidv >= g_type_count: return -1 - if itv >= 0: - if itv >= tt_count(): - return -1 - if tt_atom_of_term(itv) != nod_tk(ni): - return -1 ni = ni + 1 return 0 diff --git a/src/compiler/ccr_schema.cr b/src/compiler/ccr_schema.cr index 4e045b88..db2b4fb1 100644 --- a/src/compiler/ccr_schema.cr +++ b/src/compiler/ccr_schema.cr @@ -18,8 +18,7 @@ OPT_META_STRIDE : int = 64 // ── .ccr v10 wire schema (spec §3) ────────────────────────────────────── // These constants name wire-format roles and fixed record sizes in the // carrier file. They are neither language types nor target widths, and they -// imply no machine convention. ccr_io.cr keeps writing v9 (CCR_VERSION) -// until the v10 cutover task. +// imply no machine convention. ccr_io.cr writes this schema; v9 is rejected. CCR_V10_VERSION : int = 655360 // 0x000A0000 CCR_V10_SEG_COUNT : int = 9 @@ -39,3 +38,14 @@ CCR_V10_ENT_DISK : int = 24 CCR_V10_REG_DISK : int = 24 CCR_V10_EDG_DISK : int = 8 CCR_V10_CASE_DISK : int = 24 + +// v10 NOD semantic flags. These are node features, not target facts: no bit +// carries a machine extent or physical location. corearch reconstructs those +// from the semantic type and the active target layout. +// bit 0 IR_BOUNDS_CHECK: bound is a runtime value (legacy tk == 1) +// bit 1 IR_SPAWN: dynamic spawn (legacy tk == -1) +// bit 2 IR_DEREF/IR_STORE_PTR: runtime allocation bound is present +CCR_NOD_FLAG_DYNAMIC_LIMIT : int = 1 +CCR_NOD_FLAG_DYNAMIC_SPAWN : int = 2 +CCR_NOD_FLAG_RUNTIME_PTR_BOUNDS : int = 4 +CCR_NOD_FLAG_KNOWN_MASK : int = 7 diff --git a/src/compiler/ccr_types.cr b/src/compiler/ccr_types.cr index d96c368e..42929059 100644 --- a/src/compiler/ccr_types.cr +++ b/src/compiler/ccr_types.cr @@ -314,73 +314,84 @@ fn ccr_seg_prepare_save() -> int: return -1 if ccr_iface_seg_build() != 0: return -1 - // Fill NOD term indexes after ccr_type_populate, because that call rebuilds the - // term table and invalidates emit-time indexes. This runs after both segment - // bodies: it only queries the bridge cache, and all rows are already built, so - // it adds no terms and does not change segment bytes. Failure uses the same - // type-face gate. - if ccr_nod_item_populate() != 0: + // Derive the v10 NOD semantic type ids and feature flags from the same + // save-time type state, then copy them into the in-memory object record. + // Task 3's item side buffer is retired with the v10 disk field. + if ccr_nod_type_populate() != 0: return -1 - // Derive the v10 NOD semantic type ids from the same save-time type state. - // This is side data for the v10 cutover: the v9 NOD wire keeps `item` at +28 - // and does not gain a type_id field here. - return ccr_nod_type_populate() - -// Fill NOD term indexes. For each node in global instruction order, look up the -// file-space term index from the stored derived code. -// Face 0 (a type row whose term is reversible) uses sh_term_of_ti(tk); the loader -// checks tt_atom_of_term(term) == tk. All other cases use -1. The tk slot still -// preserves the code byte for byte. -// Classification and term construction use only the existing bridge entry points. -// This is a pure query and does not set the build-failure bit. -// The index is rederived at save time because ccr_type_populate resets the term -// layer and invalidates emit-time references. -fn ccr_nod_item_populate() -> int: - n := g_ir_instr_count - g_ccr_nod_item = alloc((n + 8) * 4) - g_ccr_nod_item_count = n - ii : ., mut = 0 - loop: - if ii >= n: - break - tk := iri_tk(ii) - it : ., mut = -1 - if sh_tk_face_of_code(iri_op(ii), tk) == 0: - it = sh_term_of_ti(tk) - cell_store32(g_ccr_nod_item, ii * 4, it) - ii = ii + 1 + populate_nod_objects() return 0 -// Fill NOD semantic type ids into the v10 side buffer. For each node in global -// instruction order, apply the fixed rule: +// Fill NOD semantic type ids and feature flags into the v10 side buffers. For +// each node in global instruction order, apply the fixed rule: // sh_tk_face_of_code(op, tk) == 0 -> type_id = tk (tk is a TYPE row) // otherwise -> type_id = -1 -// The companion flags word is constant 0 in this task; Task 4 classifies semantic -// node features into it. +// Flags carry semantic node features only: +// IR_BOUNDS_CHECK, tk != 0 -> DYNAMIC_LIMIT +// IR_SPAWN, tk < 0 -> DYNAMIC_SPAWN +// IR_DEREF/IR_STORE_PTR, s3 > 0 -> RUNTIME_PTR_BOUNDS +// The width/stride/byte extent behind those facts is a target fact and is not +// written; corearch reconstructs it from type_id and the target layout. +// A legacy aux code outside this classification rejects the save instead of +// silently dropping meaning. IR_STR_OF's TI_STR scratch code has no consumer +// and no semantic feature, so it is explicitly classified as featureless. // This is a pure query/derivation: it does not mutate the type tables, the term -// table, or g_tk_face_fail, and it does not change the v9 wire. The 40B NOD record -// still carries `item` at +28 for diagnostics and for the current v9 loader. +// table, or g_tk_face_fail. fn ccr_nod_type_populate() -> int: n := g_ir_instr_count g_ccr_nod_type = alloc((n + 8) * 4) g_ccr_nod_type_count = n + g_ccr_nod_flags = alloc((n + 8) * 4) + g_ccr_nod_flags_count = n ii : ., mut = 0 loop: if ii >= n: break + op := iri_op(ii) tk := iri_tk(ii) tid : ., mut = -1 - if sh_tk_face_of_code(iri_op(ii), tk) == 0: + fl : ., mut = 0 + if sh_tk_face_of_code(op, tk) == 0: tid = tk + else if sh_tk_is_type_face(op) != 0: + // Composite row or out-of-range/untranslatable type code: no + // reversible TYPE row. Task 3 fixes type_id = -1 here. + tid = -1 + else if op == IR_BOUNDS_CHECK: + if tk != 0: + fl = fl + CCR_NOD_FLAG_DYNAMIC_LIMIT + else if op == IR_SPAWN: + if tk < 0: + fl = fl + CCR_NOD_FLAG_DYNAMIC_SPAWN + else if tk != 0: + return -1 + else if op == IR_DEREF || op == IR_STORE_PTR: + if iri_s3(ii) > 0: + fl = fl + CCR_NOD_FLAG_RUNTIME_PTR_BOUNDS + else if op == IR_HOTPATCH_ROUTE || op == IR_STR_OF: + // HOTPATCH_ROUTE carries tk == 0. IR_STR_OF's TI_STR scratch code is + // not read by any backend consumer; neither is a node feature. + fl = 0 + else if op == IR_LOAD_INDEX || op == IR_LOAD_INDEX_VAR || op == IR_CONST_BIG || op == IR_PTR_OF: + // These ops pass a semantic type row as their legacy type_kind + // (INDEX element domain, CONST_BIG/PTR_OF int domain). Task 3 fixes + // type_id to reversible atom rows only and no backend consumer reads + // this code, so it is classified as intentionally not persisted. + // It is not a target fact and not an unclassifiable aux code. + fl = 0 + else if tk != 0: + // Unclassified legacy aux code: refuse to save rather than invent a + // wire meaning for it. + return -1 cell_store32(g_ccr_nod_type, ii * 4, tid) + cell_store32(g_ccr_nod_flags, ii * 4, fl) ii = ii + 1 return 0 // --dump-types test channel. The shared surface dump in ccr_io.cr uses the same // print path as corearch, so line formats match across processes. This corec-only -// section adds the row-to-term mapping and the NOD semantic type-id side buffer -// (v10 preparation; the v9 wire does not carry it); tests split on "rowterms:" -// and "nodtypes:". +// section adds the row-to-term mapping and the NOD semantic type-id/flags side +// data; tests split on "rowterms:" and "nodtypes:". fn ccr_type_selftest_dump(): ccr_type_surface_dump() print("rowterms: ") @@ -395,9 +406,9 @@ fn ccr_type_selftest_dump(): print(" ") println(int_str(sh_term_of_ti(i))) i = i + 1 - // v10 NOD semantic type ids (v9 wire frozen): read the side buffer through the - // shared accessors so tests see exactly what nod_type_id returns. One - // deterministic line per node; flags is the constant 0 companion word. + // v10 NOD semantic type ids and feature flags. Read the in-memory object + // record through the shared accessors so tests see exactly what the writer + // emits. One deterministic line per node. print("nodtypes: ") print_i(g_ccr_nod_type_count) println("") diff --git a/src/compiler/checker.cr b/src/compiler/checker.cr index c9d42d34..2fd36aec 100644 --- a/src/compiler/checker.cr +++ b/src/compiler/checker.cr @@ -266,20 +266,8 @@ fn init_types(): // Runtime builtin declarations moved to the target prelude. The checker only // consumes g_rt_builtin_* through prelude_builtin_init and the shared table. -fn get_type_kind(ti: int) -> int: - if ti >= 0 && ti < g_type_count: - return cell_load(g_types, ti * 24) - return -1 - -fn get_type_data(ti: int) -> int: - if ti >= 0 && ti < g_type_count: - return cell_load(g_types, ti * 24 + 8) - return 0 - -fn get_type_extra(ti: int) -> int: - if ti >= 0 && ti < g_type_count: - return cell_load(g_types, ti * 24 + 16) - return 0 +// Type-row readers live in dyn_arr.cr (shared with corearch so target layout +// helpers can resolve rows without a checker layer). // ─── 常量档数组长度约束 ─── // N 不参与类型身份,身份令牌序列只含元素项,因此数组长度在 type_equal 之外单独检查。 diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index 2069df0d..d2420f49 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -298,6 +298,24 @@ fn grow_types(needed: int): g_types = nb g_type_cap = nc +// Type-row readers. They live here (shared by corec and corearch) because the +// target layout helpers in x86_mapping.cr consume them and corearch has no +// checker layer. Out-of-range rows return the same sentinels as before. +fn get_type_kind(ti: int) -> int: + if ti >= 0 && ti < g_type_count: + return cell_load(g_types, ti * ESZ_TYPE_ROW + OFF_TR_KIND) + return -1 + +fn get_type_data(ti: int) -> int: + if ti >= 0 && ti < g_type_count: + return cell_load(g_types, ti * ESZ_TYPE_ROW + OFF_TR_DATA) + return 0 + +fn get_type_extra(ti: int) -> int: + if ti >= 0 && ti < g_type_count: + return cell_load(g_types, ti * ESZ_TYPE_ROW + OFF_TR_EXTRA) + return 0 + fn grow_funcs(needed: int): if needed < g_func_cap: return diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 4b931792..15064adc 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -48,24 +48,18 @@ g_ccr_type_seg_len : int, mut // buffer length. g_ccr_iface_seg : string, mut g_ccr_iface_seg_len : int, mut -// NOD term-index buffer. Filling happens in ccr_types.cr; ccr_io.cr copies the -// values into the +28 i32 slot of each node record. -// Value semantics: -1 means no term (not a face-0 node, or a missing row in a warm -// snapshot); >= 0 is the term DAG row for that node's atom row. The loader checks -// consistency after parsing the TYPE segment and rejects an out-of-range value or -// an atom_of mismatch. -// This file is first in both builds, so these globals must stay here. -g_ccr_nod_item : string, mut -g_ccr_nod_item_count : int, mut // NOD semantic type-id side buffer (v10 NOD `type_id`). Filling happens in -// ccr_types.cr:ccr_nod_type_populate during the save path; the v9 wire keeps the -// 40B NOD record with `item` at +28 and does not carry this value. +// ccr_types.cr:ccr_nod_type_populate during the save path; the wire writes the +// 40B NOD record with `type_id` at +24 and `flags` at +28. // Value semantics: >= 0 is a TYPE row (face 0 = the row's item is reversible); -// -1 means no type row. `nod_type_flags` is the companion semantic-flags word -// (constant 0 until the v10 cutover classifies aux codes). +// -1 means no type row. `g_ccr_nod_flags` is the companion semantic-flags word +// (CCR_NOD_FLAG_*): dynamic bounds/spawn markers and the runtime-pointer-bound +// marker. It never carries a width, byte extent, stride, or alignment. // This file is first in both builds, so these globals must stay here. g_ccr_nod_type : string, mut g_ccr_nod_type_count : int, mut +g_ccr_nod_flags : string, mut +g_ccr_nod_flags_count : int, mut g_ast : string, mut g_ast_count : int, mut g_ast_cap : int, mut diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index bc8ce0a2..41efe14e 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -1005,12 +1005,11 @@ OFF_NS_DEST : int = 4 OFF_NS_S1 : int = 8 OFF_NS_S2 : int = 16 OFF_NS_S3 : int = 20 -OFF_NS_TK : int = 24 -// TYPE-segment file-space term index (i32; -1 = none). The on-disk record has it -// at +28, so the adjacency fields follow at +32/+36. ccr_types.cr fills it while -// saving; the loader reads it and checks range and consistency after parsing the -// TYPE segment. -OFF_NS_ITEM : int = 28 +// v10 NOD semantic fields. type_id is a TYPE row (-1 = none); flags is the +// semantic feature word (CCR_NOD_FLAG_*). Neither carries a width, byte extent, +// stride, or alignment. There is no disk `item` field in v10. +OFF_NS_TYPE_ID : int = 24 +OFF_NS_FLAGS : int = 28 g_v7_nod_sem : string, mut // NOD semantic records, ESZ_NOD_SEM per node, filled by the loader g_v7_nod_meta : string, mut // NOD adjacency records, ESZ_NOD_META per node @@ -1029,25 +1028,30 @@ fn nod_s2(n: int) -> int: return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_S2) fn nod_s3(n: int) -> int: return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_S3) -fn nod_tk(n: int) -> int: - return buf_read_u32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_TK) -// Term-index accessor. It reads i32 so -1 stays -1. The code slot's -1 is -// 0xFFFFFFFF on disk and has different semantics; do not mix the read types. -fn nod_item(n: int) -> int: - return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_ITEM) -// Semantic type-id accessor (v10 NOD `type_id` side data). The buffer is filled -// by ccr_types.cr:ccr_nod_type_populate during the corec save path; the v9 wire -// still stores `item` at +28 and has no type_id field, so the read side leaves -// this buffer empty until the v10 cutover. >= 0 is a TYPE row; -1 means none. -// It reads i32 so -1 stays -1, but the sign convention differs from the code -// slot's 0xFFFFFFFF, so do not mix the read types. +// Semantic type-id accessor (v10 NOD `type_id`). >= 0 is a TYPE row; -1 means +// none. It reads i32 so -1 stays -1. fn nod_type_id(n: int) -> int: - return buf_read_i32(g_ccr_nod_type, n * 4) -// Semantic node flags companion word. Constant 0 in v9: no semantic node feature -// is classified yet and the wire has no flags field. Task 4 fills this from -// classified aux codes; callers must not read it as a width or byte offset. + return buf_read_i32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_TYPE_ID) +// Semantic node feature flags (v10 NOD `flags`). Read as unsigned: the word is a +// bitset of CCR_NOD_FLAG_* features, never a width or byte offset. fn nod_type_flags(n: int) -> int: - return 0 + return buf_read_u32(g_v7_nod_sem, n * ESZ_NOD_SEM + OFF_NS_FLAGS) +// Legacy derived-code view for diagnostics and for emit consumers that still +// read a single code word. Bounds/spawn features come from flags. Target facts +// (IR_DEREF/IR_STORE_PTR access width) are reconstructed by the instance, not +// here, so this returns type_id (-1 when the node has no semantic type). +fn nod_tk(n: int) -> int: + op := nod_op(n) + fl := nod_type_flags(n) + if op == IR_BOUNDS_CHECK: + if fl % 2 == 1: + return 1 + return 0 + if op == IR_SPAWN: + if (fl / 2) % 2 == 1: + return -1 + return 0 + return nod_type_id(n) fn nod_edge_first(n: int) -> int: return cell_load(g_v7_nod_meta, n * ESZ_NOD_META) fn nod_edge_count(n: int) -> int: diff --git a/src/targets/x86_64/linux/elf/_import.cr b/src/targets/x86_64/linux/elf/_import.cr index e4af2728..c30399b1 100644 --- a/src/targets/x86_64/linux/elf/_import.cr +++ b/src/targets/x86_64/linux/elf/_import.cr @@ -9,6 +9,10 @@ import ccr_schema import globals import bytes import dyn_arr +// Target layout helpers: corearch reconstructs NOD access widths/strides from +// these instead of reading target facts from .ccr. +import mapping +import x86_mapping import backend_schema import backend_globals import backend_arrays diff --git a/src/targets/x86_64/linux/elf/main.cr b/src/targets/x86_64/linux/elf/main.cr index 90dcc9b3..4bbf945e 100644 --- a/src/targets/x86_64/linux/elf/main.cr +++ b/src/targets/x86_64/linux/elf/main.cr @@ -62,7 +62,7 @@ fn backend_has_extended_flags() -> int: return 1 if cli_has("dump-objects") != 0: return 1 - if cli_has("dump-nod-items") != 0: + if cli_has("dump-nod-types") != 0: return 1 if cli_has("dump-types") != 0: return 1 diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 530c09bb..83f99c1e 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -7,7 +7,7 @@ impl 边 + 方法表 / corearch 读回 / 跨段引用域 / 扩列与命名化守门)。 Task 1(机制面): - ① 段表 = 8 段、tag 序 = 1..8、offset 连续、末段尾 == 文件大小; + ① 段表 = 9 段、tag 序 = 1..9、offset 连续、末段尾 == 文件大小; ② header: version = 9、seg_count = 8、reserved = 0; ③ TYPE(7)/IFACE(8) **皆为内容面**(R2 P4 Task 2/3 起——空壳期已退役); ④ loader 负分支(byte mutation,逐个打;全部要求 corearch rc≠0 且**不得 @@ -79,11 +79,13 @@ COREARCH = os.path.join(BASE, 'build/corearch') MAGIC = 0x31524343 # "CCR1" -VER = 589825 # v9.1 = (9 << 16) | 1 -SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE -HEADER_TABLE = 16 + 8 * 12 # 112 -TYPE_TAG = 7 -IFACE_TAG = 8 +VER = 0x000A0000 # v10 lattice container +SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8, 9] # STR TYPE IFACE SYM NOD ENT REG EDG CASE +HEADER_TABLE = 16 + 9 * 12 # 124 +TYPE_TAG = 2 +IFACE_TAG = 3 +ENT_TAG = 6 +NOD_TAG = 5 ESZ_TYPE_ROW = 24 # 类型行表记录:{kind,data,extra} i64×3(D12) ESZ_TYPE_TERM = 40 # 项 DAG 记录:{tag,a,b,c,d} i64×5(哈希不落盘,加载侧重算) IFACE_NATIVE_N = 16 # R2 P4 Task 3 扩列硬值(13 → 16;见 ccr_io.cr 注) @@ -108,7 +110,7 @@ class CcrFile: - """`.ccr` 全段解析(8 段规范序契约;违反 = AssertionError)。""" + """`.ccr` 全段解析(9 段规范序契约;违反 = AssertionError)。""" def __init__(self, data: bytes): self.d = data @@ -117,8 +119,8 @@ def __init__(self, data: bytes): (magic, ver, seg_count, reserved) = struct.unpack_from('<4I', data, 0) assert magic == MAGIC, f"bad magic {magic:#x}" assert ver == VER, f"expected version {VER}, got {ver}" - assert seg_count == 8, f"expected 8 segments, got {seg_count}" - assert reserved == 0, f"reserved != 0: {reserved}" + assert seg_count == 9, f"expected 9 segments, got {seg_count}" + assert reserved <= 0x7, f"reserved flag bits set: {reserved}" self.segs = {} cur = HEADER_TABLE # 段体紧随段表 for i, tag in enumerate(SEG_TAGS): @@ -225,9 +227,9 @@ def _shell_fixture(name: str): return src_path, ccr_path -def test_p4t1_layout_eight_segments(): - """① 段表 = 8 段规范序(tag 1..8)+ offset 连续 + 末段尾 == 文件大小; - header version = 8 / seg_count = 8 / reserved = 0。""" +def test_p4t1_layout_nine_segments(): + """① 段表 = 9 段规范序(tag 1..9)+ offset 连续 + 末段尾 == 文件大小; + header version = 0x000A0000 / seg_count = 9 / flags bits 0..2。""" src_path, ccr_path = _shell_fixture('layout') try: c = CcrFile(read_ccr(ccr_path)) @@ -288,7 +290,7 @@ def test_p4t1_loader_rejects_missing_type_iface(): try: data = bytearray(read_ccr(ccr_path)) c = CcrFile(bytes(data)) - edg_off, _ = c.segs[6] + edg_off, _ = c.segs[8] struct.pack_into(' bytes: return CcrFile(data).body(TYPE_TAG) -def _resize_type_tail(data: bytes, delta: int) -> bytes: - """TYPE 段体尾增(+)/删(-) |delta| 字节,并同步段表(TYPE size、IFACE offset) - ——产出「段表仍自洽、TYPE 内容被破坏」的文件(拒收必须归因于内容闸)。""" +def _resize_seg_tail(data: bytes, table_index: int, delta: int) -> bytes: + """Resize segment body at table row `table_index` and shift every later + segment offset by delta, keeping the table contiguous. The body bytes are + inserted/removed at the segment tail so the file stays structurally valid + apart from the intended content corruption.""" d = bytearray(data) - (t_off, t_size) = struct.unpack_from('<2I', d, 16 + 6 * 12 + 4) - struct.pack_into(' 0: return bytes(d[:cut]) + b'\x00' * delta + bytes(d[cut:]) return bytes(d[:cut + delta]) + bytes(d[cut:]) +def _resize_type_tail(data: bytes, delta: int) -> bytes: + """TYPE 段体尾增(+)/删(-) |delta| 字节,并同步其后全部段 offset + ——产出「段表仍自洽、TYPE 内容被破坏」的文件(拒收必须归因于内容闸)。""" + return _resize_seg_tail(data, 1, delta) + + def sym_type_refs(data: bytes): """SYM 段里全部类型行引用(globals type + func var_decls type)——检查载体 自洽(引用域必须 < TYPE 段行数;T2 用例 ⑮ 的根因量化用)。""" c = CcrFile(data) - off, size = c.segs[2] + off, size = c.segs[4] end = off + size p = off refs = [] @@ -768,7 +779,7 @@ def test_p4t2_loader_rejects_count_and_truncation(): c = CcrFile(data) t_off, _t_size = c.segs[TYPE_TAG] tc_off = t_off + 4 + len(rows) * ESZ_TYPE_ROW - i_off, _i_size = c.segs[8] + i_off, _i_size = c.segs[IFACE_TAG] mutations = {} d = bytearray(data) struct.pack_into(' bytes: - """IFACE 段体尾增(+)/删(-) |delta| 字节(段表有尾随量可容纳时用——本段是末段, - 增删会改变文件长度;调用方负责只做「可容纳」的破坏)。""" - d = bytearray(data) - (i_off, i_size) = struct.unpack_from('<2I', d, 16 + 7 * 12 + 4) - struct.pack_into(' 0: - return bytes(d[:cut]) + b'\x00' * delta + bytes(d[cut:]) - return bytes(d[:cut + delta]) + bytes(d[cut:]) + """IFACE 段体尾增(+)/删(-) |delta| 字节并同步后续段 offset(结构仍自洽)。""" + return _resize_seg_tail(data, 2, delta) def test_p4t3_iface_segment_layout(): @@ -1662,9 +1666,10 @@ def ccr_nod_rows(data: bytes): def ccr_nod_records(data: bytes): - """40B/条独立解析 → `[(op, tk, item), …]`(item = +28 i32;-1 = 无项)。 + """40B/条独立解析 → `[(op, type_id, flags), …]`(type_id = TYPE row/-1, + flags = semantic feature bits)。v10 has no disk `item` field. 硬断 = 步长恒等式(段体 = 4 + n×40)+ 邻接域前缀累计不变量。""" - b = CcrFile(data).body(3) + b = CcrFile(data).body(NOD_TAG) n = struct.unpack_from('= -1, f"node {i}: type_id {type_id} < -1" + assert flags_ <= 0x7, f"node {i}: unknown flag bits {flags_:#x}" run += ec - out.append((op, tk, item)) + out.append((op, type_id, flags_)) return out def test_p4t4_ccr_nod_tk_equals_graph_tk(): - """㉘ `.ccr` 面零改动:NOD **40B/条**(R2 P6 T3 起;`tk` 槽语义/字节不变);且 NOD 的 (op, tk) 序列与 DF 节点的 - (op, tk) 序列**逐位置相同**(1:1 同序)——即「tk 升格前后逐字节同」的 - .ccr 侧证据(tk 只从 iri_tk 读出,升格只加了内存第 9 槽)。""" + """㉘ `.ccr` NOD **40B/条**:v10 carries `type_id`/`flags` instead of the + derived code. For a face-0 node (the `.cir` dump shows a live term) the wire + type_id equals the derived row; every non-face node carries -1.""" src = tk_fixture('nod') ccr_path = os.path.join(BASE, 'build', 'test_p4t4_nod.ccr') dot = os.path.join(BASE, 'build', 'test_p4t4_nod.cir') @@ -1696,8 +1703,14 @@ def test_p4t4_ccr_nod_tk_equals_graph_tk(): assert len(nod) == len(rows), \ f"NOD rows {len(nod)} != DF nodes {len(rows)}" for i, (nr, dr) in enumerate(zip(nod, rows)): - assert nr == (dr[1], dr[2]), \ - f"node {i}: NOD (op,tk)={nr} != graph {(dr[1], dr[2])}" + op, type_id = nr + assert op == dr[1], f"node {i}: NOD op {op} != graph {dr[1]}" + if dr[3] >= 0: + assert type_id == dr[2], \ + f"node {i}: type_id {type_id} != face-0 row {dr[2]}" + else: + assert type_id == -1, \ + f"node {i}: non-face node carries type_id {type_id}" finally: _cleanup(src, ccr_path, dot) @@ -1980,8 +1993,9 @@ def test_p5t2_cold_warm_composite_symmetry(): def test_p5t2_ccr_nod_composite_parity(): - """㊱ 复合行语料的 `.ccr` 面:NOD (op, tk) 与图 (op, tk) 逐位置相同——码没有被 - 项槽吞掉(派生码 ≡ 旧混用码的 .ccr 侧证据)。""" + """㊱ 复合行语料的 `.ccr` 面:v10 writes type_id = -1 for composite type + rows (Task 3 fixes semantic ids to reversible atom rows only). The row code + stays in the emit-time `.cir` surface, not in the wire.""" src = tk_fixture('nodc', COMPOSITE_FIXTURE) ccr_path = os.path.join(BASE, 'build', 'test_p4t4_nodc.ccr') dot = os.path.join(BASE, 'build', 'test_p4t4_nodc.cir') @@ -1990,10 +2004,15 @@ def test_p5t2_ccr_nod_composite_parity(): corec_ccr(src, ccr_path) nod = ccr_nod_rows(read_ccr(ccr_path)) assert len(nod) == len(rows), f"NOD rows {len(nod)} != DF nodes {len(rows)}" + comp = 0 for i, (nr, dr) in enumerate(zip(nod, rows)): - assert nr == (dr[1], dr[2]), f"node {i}: NOD {nr} != graph {(dr[1], dr[2])}" - assert any(r[1] in MINT_OPS and r[8] > 0 for r in rows), \ - "vacuous: no composite row in fixture" + op, type_id = nr + assert op == dr[1], f"node {i}: NOD op {op} != graph {dr[1]}" + if dr[1] in MINT_OPS and dr[8] > 0: + comp += 1 + assert type_id == -1, \ + f"node {i}: composite row leaked into type_id {type_id}" + assert comp > 0, "vacuous: no composite row in fixture" finally: _cleanup(src, ccr_path, dot) @@ -2058,55 +2077,55 @@ def test_p5t2_row_allocating_fn_not_snapshotted(): shutil.rmtree(os.path.dirname(cache_dir), ignore_errors=True) -# ═══════════ R2 P6 Task 3(β):NOD 盘面项索引(+28 i32)+ 版本 9 ═══════════ -# 口径:`item` = **TYPE 段文件空间**项索引(-1 = 无项);写侧 = corec 的 -# ccr_types.cr:ccr_nod_item_populate(保存期由盘上派生码经桥接层重派生),读侧 = -# load_ccr 的 TYPE 段后**一致性硬校验**(域外/不一致 ⇒ 拒绝)+ corearch -# `--dump-nod-items` 读回通道。`tk` 槽语义/字节不变(派生码,D22-①)。 +# ═══════════ v10 NOD semantic type_id/flags(Task 4)══════════ +# 口径:`type_id` = TYPE 段行索引(-1 = 无类型);`flags` = 语义特性位 +# (CCR_NOD_FLAG_*)。写侧 = corec 的 ccr_types.cr:ccr_nod_type_populate; +# 读侧 = load_ccr 的 TYPE 段后**type_id 域校验** + corearch `--dump-nod-types` +# 读回通道。v10 盘面没有 `item`/旧 `tk` 槽。 -def _nod_items_dump(ccr_path: str): - """`corearch F.ccr --dump-nod-items` → (hdr, rows)。 - 行格式契约见 corearch.cr:dump_nod_items: - `[nod-items] nodes= with_item= terms= rows=` - `item op code idx atom ` - rows = [(node, op, code, idx, atom), …]。""" - r = subprocess.run([COREARCH, ccr_path, '--dump-nod-items'], +def _nod_type_dump(ccr_path: str): + """`corearch F.ccr --dump-nod-types` → (hdr, rows). + 行格式契约见 corearch.cr:dump_nod_types: + `[nod-types] nodes= with_type= flags= rows= ` + `nodtype op tid flags ` + rows = [(node, op, type_id, flags), …]。""" + r = subprocess.run([COREARCH, ccr_path, '--dump-nod-types'], capture_output=True, text=True, cwd=BASE, timeout=120) assert r.returncode == 0, \ - f"corearch --dump-nod-items rc={r.returncode}: {r.stdout!r} {r.stderr!r}" + f"corearch --dump-nod-types rc={r.returncode}: {r.stdout!r} {r.stderr!r}" hdr = None rows = [] for ln in r.stdout.splitlines(): - if ln.startswith('[nod-items]'): - m = re.match(r'\[nod-items\] nodes=(\d+) with_item=(\d+) ' - r'terms=(\d+) rows=(\d+)', ln) - assert m, f"malformed nod-items header: {ln!r}" - hdr = {'nodes': int(m.group(1)), 'with_item': int(m.group(2)), - 'terms': int(m.group(3)), 'rows': int(m.group(4))} + if ln.startswith('[nod-types]'): + m = re.match(r'\[nod-types\] nodes=(\d+) with_type=(\d+) ' + r'flags=(\d+) rows=(\d+)', ln) + assert m, f"malformed nod-types header: {ln!r}" + hdr = {'nodes': int(m.group(1)), 'with_type': int(m.group(2)), + 'flags': int(m.group(3)), 'rows': int(m.group(4))} continue p = ln.split() - if p and p[0] == 'item': - assert len(p) == 10 and p[2] == 'op' and p[4] == 'code' \ - and p[6] == 'idx' and p[8] == 'atom', f"malformed item line: {ln!r}" - rows.append(tuple(int(p[k]) for k in (1, 3, 5, 7, 9))) - assert hdr is not None, "nod-items header missing" + if p and p[0] == 'nodtype': + assert len(p) == 8 and p[2] == 'op' and p[4] == 'tid' \ + and p[6] == 'flags', f"malformed nodtype line: {ln!r}" + rows.append(tuple(int(p[k]) for k in (1, 3, 5, 7))) + assert hdr is not None, "nod-types header missing" assert len(rows) == hdr['nodes'], \ f"rows {len(rows)} != header nodes {hdr['nodes']}" return hdr, rows def test_p6t3_version_gate_rejects_v8(): - """㊳ 版本闸:v9 接受 / **v8 拒收**(D10 先例——旧格式整类拒收,不得静默)。 + """㊳ 版本闸:v10 接受 / **v9.1 拒收**(旧格式整类拒收,不得静默)。 非平凡性控制:未补丁版必须 rc=0 + 出产物(否则「拒绝」可能只是畸形字节的副产品)。""" src = tk_fixture('p6t3ver') ccr_path = os.path.join(BASE, 'build', 'test_p6t3_ver.ccr') - bad = ccr_path + '.v8' + bad = ccr_path + '.v9' out = os.path.join(BASE, 'build', 'test_p6t3_ver.out') try: corec_ccr(src, ccr_path) - arch_elf(ccr_path, out) # v9 正控(非平凡性) + arch_elf(ccr_path, out) # v10 正控(非平凡性) data = bytearray(read_ccr(ccr_path)) - struct.pack_into('= 0: + assert o[5] == type_id, \ + f"node {i}: object tk {o[5]} != type_id {type_id}" + if flags_ & 1: + assert o[5] == 1, f"node {i}: dynamic bound flag lost in dump" + if flags_ & 2: + assert o[5] == -1, f"node {i}: dynamic spawn flag lost in dump" finally: _cleanup(src, ccr_path) -def test_p6t3_nod_items_roundtrip(): - """㊵ **项索引落盘/读回对拍(本任务承重例)**:corearch 逐节点 `item → 项表取项 → - `tt_atom_of_term` → 原子行`,判据 = ① `mism == 0`(有项节点 atom ≡ 盘上码)· - ② `with_item > 0`(非空转)· ③ **非退化**:至少一节点 `idx != code`(项 DAG 序 ≠ - 行序——否则「回读通道其实是把码打印两遍」的假绿不可辨)。 - 写侧若误用 emit 期活表索引(而非保存期重派生),① 必红。""" +def test_p6t3_nod_types_roundtrip(): + """㊵ **type_id/flags 落盘/读回对拍(本任务承重例)**:corearch 的 + `--dump-nod-types` 逐节点输出必须与独立解析的 NOD 段一致。 + 判据 = ① 逐节点 (op/type_id/flags) 相同 · ② `with_type > 0`(非空转)· + ③ `flags > 0`(至少一条语义特性被分类——否则 flags 分类是死路)。""" + total_with_type = 0 + total_flagged = 0 for src_name in ('nod', 'comp'): src = tk_fixture('p6t3rt_' + src_name, TK_FIXTURE if src_name == 'nod' else COMPOSITE_FIXTURE) ccr_path = os.path.join(BASE, 'build', f'test_p6t3_rt_{src_name}.ccr') try: corec_ccr(src, ccr_path) - hdr, rows = _nod_items_dump(ccr_path) - assert hdr['with_item'] > 0, f"{src_name}: vacuous (no item-bearing node)" - mism = [r for r in rows if r[3] >= 0 and r[4] != r[2]] - assert not mism, f"{src_name}: {len(mism)} item/code mismatch, e.g. {mism[:3]}" - with_i = [r for r in rows if r[3] >= 0] - assert any(r[3] != r[2] for r in with_i), \ - f"{src_name}: every idx == code — read-back may be degenerate" - for (n, op, code, idx, atom) in rows: - if idx >= 0: - assert 0 <= idx < hdr['terms'], f"node {n}: idx {idx} out of term space" - assert atom == code, f"node {n}: atom {atom} != code {code}" + hdr, rows = _nod_type_dump(ccr_path) + recs = ccr_nod_records(read_ccr(ccr_path)) + assert len(rows) == len(recs), f"{src_name}: dump/wire node count drift" + with_type = 0 + flagged = 0 + for (n, op, tid, fl), (wop, wtid, wfl) in zip(rows, recs): + assert (op, tid, fl) == (wop, wtid, wfl), \ + f"{src_name} node {n}: dump {(op, tid, fl)} != wire {(wop, wtid, wfl)}" + if tid >= 0: + with_type += 1 + if fl != 0: + flagged += 1 + assert hdr['with_type'] == with_type + assert hdr['flags'] == flagged + assert with_type > 0, f"{src_name}: no node carries a semantic type" + total_with_type += with_type + total_flagged += flagged finally: _cleanup(src, ccr_path) + assert total_flagged > 0, "vacuous: no node carries a semantic flag in either corpus" -def test_p6t3_items_match_memory_face(): - """㊶ 两通道面一致:`(item ≥ 0) ⟺ (内存项槽 ≥ 0)` 逐节点 + 计数相等 - (`--dump-tk-terms` 的 `term` 列 = emit 期分类结果;`item` = 保存期同判定的投影)。 - 同时锁 D22-①:**盘上 tk 列 ≡ 内存派生码列**(全节点,含辅码面/复合行)。""" +def test_p6t3_types_match_memory_face(): + """㊶ 两通道面一致:`(type_id ≥ 0) ⟺ (内存项槽 ≥ 0)` 逐节点 + 计数相等 + (`--dump-tk-terms` 的 `term` 列 = emit 期分类结果;type_id = 同一判定的投影)。 + 同时锁 Task 3 规则:face-0 节点 type_id == derived row code。""" src = tk_fixture('p6t3face', COMPOSITE_FIXTURE) ccr_path = os.path.join(BASE, 'build', 'test_p6t3_face.ccr') dot = os.path.join(BASE, 'build', 'test_p6t3_face.cir') @@ -2180,84 +2213,88 @@ def test_p6t3_items_match_memory_face(): recs = ccr_nod_records(read_ccr(ccr_path)) assert len(recs) == len(rows), f"nodes {len(recs)} != mem rows {len(rows)}" w_mem = sum(1 for r in rows if r[3] >= 0) - nhdr, nrows = _nod_items_dump(ccr_path) - assert nhdr['with_item'] == w_mem, \ - f"with_item {nhdr['with_item']} != memory with_term {w_mem}" - for i, ((op, tk, item), r) in enumerate(zip(recs, rows)): - assert (op, tk) == (r[1], r[2]), \ - f"node {i}: NOD (op,tk)=({op},{tk}) != memory derived ({r[1]},{r[2]})" - assert (item >= 0) == (r[3] >= 0), \ - f"node {i}: item {item} vs memory term {r[3]} (face disagreement)" + nhdr, nrows = _nod_type_dump(ccr_path) + assert nhdr['with_type'] == w_mem, \ + f"with_type {nhdr['with_type']} != memory with_term {w_mem}" + for i, ((op, type_id, flags_), r) in enumerate(zip(recs, rows)): + assert op == r[1], f"node {i}: NOD op {op} != memory op {r[1]}" + assert (type_id >= 0) == (r[3] >= 0), \ + f"node {i}: type_id {type_id} vs memory term {r[3]} (face disagreement)" + if type_id >= 0: + assert type_id == r[2], \ + f"node {i}: type_id {type_id} != derived code {r[2]}" finally: _cleanup(src, ccr_path, dot) def test_p6t3_aux_face_and_composite_preserved(): - """㊷ 辅码面 + 复合行保真:辅码面 op(`IR_BOUNDS_CHECK`/`IR_DEREF`/`IR_STORE_PTR`/ - `IR_SPAWN`/`IR_HOTPATCH_ROUTE`)**必须无项**(`item == -1`)且码原值;复合行 - (仓内语料 `ptr_ref_first.cr` 的 `IR_BINARY` 指针行)同(F2 裁决:不可逆行不建项)。 + """㊷ 辅码面 + 复合行分类:辅码面 op(`IR_BOUNDS_CHECK`/`IR_DEREF`/ + `IR_STORE_PTR`/`IR_SPAWN`/`IR_HOTPATCH_ROUTE`)**必须无 type_id**(-1); + 复合行(`ptr_ref_first.cr` 的 `IR_BINARY` 指针行)同。语义特性只进 flags: + BOUNDS_CHECK 的动态上限 = bit0,SPAWN 的动态标记 = bit1。 非空转:三类形态在语料里都必须真出现。""" aux_ops = {IR_BOUNDS_CHECK, IR_DEREF, IR_STORE_PTR, IR_SPAWN, IR_HOTPATCH_ROUTE} - # 语料分工(各自覆盖不同形态;非空转 = 三类形态合计必须真出现): + # 语料分工: # ptr_ref_first.cr = 复合行(IR_BINARY 指针行)+ hotpatch route 辅码; - # p_dex.cr = IR_STORE_PTR 宽度 + IR_BOUNDS_CHECK 旗标; - # p_spawn.cr = IR_SPAWN 计数(**含负码** —— 盘上以 u32 位型承载)。 + # p_dex.cr = IR_STORE_PTR + IR_BOUNDS_CHECK 旗标; + # p_spawn.cr = IR_SPAWN 动态标记。 sources = [(os.path.join(BASE, 'tests', 'suite', 'ptr_ref_first.cr'), 'prf', 'comp'), (os.path.join(BASE, 'tests', 'probes', 'p_dex.cr'), 'dex', 'aux'), - (os.path.join(BASE, 'tests', 'probes', 'p_spawn.cr'), 'spawn', 'aux')] + (os.path.join(BASE, 'tests', 'probes', 'p_spawn.cr'), 'spawn', 'aux'), + (tk_fixture('p6t3aux_tk', TK_FIXTURE), 'tk', 'aux')] total_aux = 0 + saw_bound = 0 + saw_spawn = 0 for src, tag, kind in sources: ccr_path = os.path.join(BASE, 'build', f'test_p6t3_aux_{tag}.ccr') try: corec_ccr(src, ccr_path) recs = ccr_nod_records(read_ccr(ccr_path)) - total_aux += sum(1 for (op, _tk, _it) in recs if op in aux_ops) + aux = [r for r in recs if r[0] in aux_ops] + total_aux += len(aux) if kind == 'comp': - comp = [r for r in recs if r[0] == IR_BINARY and r[2] == -1 and r[1] >= 0] + comp = [r for r in recs if r[0] == IR_BINARY and r[1] == -1] assert comp, f"{tag}: vacuous (no composite-row node)" - for (op, tk, item) in recs: - if op in aux_ops: - assert item == -1, \ - f"{tag}: aux-face op {op} carries item {item} (must be -1)" + for (op, type_id, flags_) in aux: + assert type_id == -1, \ + f"{tag}: aux-face op {op} carries type_id {type_id} (must be -1)" + if op == IR_BOUNDS_CHECK and flags_ & 1: + saw_bound += 1 + if op == IR_SPAWN and flags_ & 2: + saw_spawn += 1 + assert flags_ <= 0x7 finally: _cleanup(ccr_path) + if tag == 'tk': + _cleanup(src) assert total_aux > 0, "vacuous: no aux-face node in any source" + assert saw_bound > 0, "vacuous: no dynamic BOUNDS_CHECK flag in corpus" + assert saw_spawn > 0, "vacuous: no dynamic SPAWN flag in corpus" -def _patch_item(data: bytes, node: int, value: int) -> bytes: - """把第 node 个 NOD 记录的 `item` 字段(+28)改写为 value。""" +def _patch_type_id(data: bytes, node: int, value: int) -> bytes: + """把第 node 个 NOD 记录的 `type_id` 字段(+24)改写为 value。""" d = bytearray(data) - off, _ = CcrFile(bytes(d)).segs[3] - struct.pack_into('= 0] - assert with_i, "vacuous: no item-bearing node to patch" - n0 = with_i[0][0] - # ③ 找一个非原子项索引(tag != TT_ATOM 或 b < 0)——独立读 --dump-types - r = subprocess.run([COREARCH, ccr_path, '--dump-types'], - capture_output=True, text=True, cwd=BASE, timeout=120) - assert r.returncode == 0, "--dump-types rc!=0" - _rows, terms, _probe, _rt = parse_type_dump(r.stdout) - nonatom = [i for i, t in enumerate(terms) - if not (t[0] == TT_ATOM and t[2] >= 0)] - assert nonatom, "vacuous: no non-atom term in file" - cases = [('oob_count', hdr['terms']), ('neg2', -2), ('nonatom', nonatom[0])] - for tag, val in cases: + hdr, rows = _nod_type_dump(ccr_path) + assert hdr['rows'] > 0 + for tag, val in (('oob_row', hdr['rows']), ('neg2', -2)): bad = f'{ccr_path}.{tag}' - _write(bad, _patch_item(data, n0, val)) + _write(bad, _patch_type_id(data, 0, val)) arch_elf(bad, out, must_fail=True) os.unlink(bad) finally: @@ -2275,7 +2312,7 @@ def test_p6t3_nod_count_overflow_rejected(): corec_ccr(src, ccr_path) arch_elf(ccr_path, out) data = bytearray(read_ccr(ccr_path)) - off, size = CcrFile(bytes(data)).segs[3] + off, size = CcrFile(bytes(data)).segs[NOD_TAG] (n,) = struct.unpack_from(' int: def test_nod_type_id_is_type_row_or_minus_one(): - """Task 3(v10 NOD 语义域,v9 wire 冻结):`ccr_nod_type_populate` 的逐节点 - type_id —— 类型面节点 `0 <= type_id < row_count` 且 `type_id == tk`; - 非类型面节点 `type_id == -1`;`flags == 0`(本任务恒 0)。 + """Task 4(v10 NOD 语义域):`ccr_nod_type_populate` 的逐节点 type_id —— + 类型面节点 `0 <= type_id < row_count` 且 `type_id == tk`;非类型面节点 + `type_id == -1`。flags 只携带语义特性(0..7),且与 wire 逐节点一致。 非空转:夹具必须同时出现类型面节点(const/binary/load…)与非类型面节点 (deref/bounds/spawn…)。""" src = tk_fixture('p7tid', TK_FIXTURE) @@ -2362,17 +2399,17 @@ def test_nod_type_id_is_type_row_or_minus_one(): assert hdr == len(nrows) > 0, "vacuous: no node in nodtype dump" assert len(rowterms) == len(rows), \ f"rowterm map {len(rowterms)} != rows {len(rows)}" - # 通道自洽:dump 的节点序 == 同一次 ccr 产物 NOD 段的 (op, tk) 序列 + # 通道自洽:dump 的节点序 == 同一次 ccr 产物 NOD 段的 (op, type_id, flags) nod = ccr_nod_records(read_ccr(ccr_path)) assert hdr == len(nod), f"nodtype count {hdr} != NOD records {len(nod)}" - for i, (n, op, tk, _tid, _flags) in enumerate(nrows): - assert (op, tk) == (nod[i][0], nod[i][1]), \ - f"node {i}: dump (op,tk)=({op},{tk}) != wire {(nod[i][0], nod[i][1])}" + for i, (n, op, tk, tid, flags) in enumerate(nrows): + assert (op, tid, flags) == nod[i], \ + f"node {i}: dump {(op, tid, flags)} != wire {nod[i]}" face = 0 nonface = 0 face_gt0 = 0 for (n, op, tk, tid, flags) in nrows: - assert flags == 0, f"node {n}: flags {flags} != 0" + assert flags <= 0x7, f"node {n}: unknown flag bits {flags:#x}" exp = expected_nod_type_id(op, tk, rows, terms, rowterms) assert tid == exp, \ f"node {n}: type_id {tid} != expected {exp} (op={op} tk={tk})" @@ -2393,24 +2430,23 @@ def test_nod_type_id_is_type_row_or_minus_one(): _cleanup(src, ccr_path) -def test_v9_ent_still_28b_with_home(): - """Task 3 回归(v9 wire 冻结):ENT 记录仍为 28B(var/version/def/ - live_start/live_end/home/flags),`home` 仍由旧 loader 读回,合法 v9 - 文件仍能载入并发射 ELF。v10 的 24B 断言留在 Task 4。""" - src_path, ccr_path = _shell_fixture('t3ent') - out = os.path.join(BASE, 'build', 'test_p7t3_ent.out') +def test_v10_ent_24b_without_home(): + """Task 4 回归:ENT 记录 = 24B(var_id/version/def_nod/live_start/live_end/ + flags),v10 文件不再有 home 字段。合法文件仍能载入并发射 ELF。""" + src_path, ccr_path = _shell_fixture('t4ent') + out = os.path.join(BASE, 'build', 'test_p7t4_ent.out') try: data = read_ccr(ccr_path) - c = CcrFile(data) # 内含 version == VER(589825) + 8 段规范序断言 - assert VER == 589825, "CCR_VERSION is no longer v9.1" - body = c.body(4) + c = CcrFile(data) # 内含 version == 0x000A0000 + 9 段规范序断言 + assert VER == 0x000A0000, "CCR_VERSION is not v10" + body = c.body(ENT_TAG) (cnt,) = struct.unpack_from(' 0, "fixture produced no ENT records (vacuous)" - assert (len(body) - 4) % 28 == 0, \ - f"ENT body {len(body)}B not 4 + n*28" - assert (len(body) - 4) // 28 == cnt, \ - f"ENT count {cnt} != record slots {(len(body) - 4) // 28}" - # 旧 loader 读回 home:每条记录都打印 home 列(28B 记录的 +20 槽) + assert (len(body) - 4) % 24 == 0, \ + f"ENT body {len(body)}B not 4 + n*24" + assert (len(body) - 4) // 24 == cnt, \ + f"ENT count {cnt} != record slots {(len(body) - 4) // 24}" + # The loader's in-memory home slot is -1 because v10 carries no home. r = subprocess.run([COREARCH, ccr_path, '--dump-entries'], capture_output=True, text=True, cwd=BASE, timeout=120) assert r.returncode == 0, \ @@ -2418,14 +2454,16 @@ def test_v9_ent_still_28b_with_home(): homes = re.findall(r' home (-?\d+)', r.stdout) assert len(homes) == cnt, \ f"{len(homes)} home values != {cnt} ENT records" - # 合法 v9 文件端到端仍可载入/发射 + assert all(int(h) == -1 for h in homes), \ + f"v10 loader surfaced a non-(-1) home: {set(homes)}" + # 合法 v10 文件端到端仍可载入/发射 arch_elf(ccr_path, out) finally: _cleanup(src_path, ccr_path, out) if __name__ == '__main__': - tests = [test_p4t1_layout_eight_segments, + tests = [test_p4t1_layout_nine_segments, test_p4t1_loader_rejects_valid_v7_file, test_p4t1_loader_rejects_missing_type_iface, test_p4t1_loader_rejects_tag_mutations, @@ -2468,14 +2506,14 @@ def test_v9_ent_still_28b_with_home(): test_p5t2_row_allocating_fn_not_snapshotted, test_p6t3_version_gate_rejects_v8, test_p6t3_nod_record_40b_field_order, - test_p6t3_nod_items_roundtrip, - test_p6t3_items_match_memory_face, + test_p6t3_nod_types_roundtrip, + test_p6t3_types_match_memory_face, test_p6t3_aux_face_and_composite_preserved, - test_p6t3_item_oob_rejected, + test_p6t3_type_id_oob_rejected, test_p6t3_nod_count_overflow_rejected, test_p6t3_d18_purity_static, test_nod_type_id_is_type_row_or_minus_one, - test_v9_ent_still_28b_with_home] + test_v10_ent_24b_without_home] failed = 0 for t in tests: try: diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 6d7a781a..7a0d4fb3 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -23,7 +23,11 @@ """ import contextlib +import os import re +import struct +import subprocess +import tempfile from pathlib import Path from ccr_v10_model import ( @@ -346,6 +350,144 @@ def test_schema_has_no_target_layout_words(): assert word not in text, f"ccr_schema.cr must not mention {word!r}" +# --------------------------------------------------------------------------- +# 12. end-to-end: corec writes v10, corearch rejects v9 and accepts v10 +# --------------------------------------------------------------------------- + +CORE_BASE = Path(__file__).resolve().parent.parent.parent +COREC = CORE_BASE / "build" / "corec" +COREARCH = CORE_BASE / "build" / "corearch" + +# Exercises the target-fact reconstruction paths: ALLOC_ARRAY, BOUNDS_CHECK, +# DEREF, STORE_PTR, ADDR_INDEX. +E2E_SOURCE = """fn main() -> int: + arr : ., mut = [10, 20, 30] + i := 1 + arr[i] = 9 + p := &arr[1] + *p = 11 + return *p + arr[0] + arr[2] +""" + + +def _run(cmd, **kwargs): + return subprocess.run( + [str(part) for part in cmd], + capture_output=True, + text=True, + cwd=str(CORE_BASE), + timeout=180, + **kwargs, + ) + + +def _build_v10(tmp: Path): + src = tmp / "e2e.cr" + ccr = tmp / "e2e.ccr" + src.write_text(E2E_SOURCE, encoding="utf-8") + r = _run([COREC, "ccr", src, "-o", ccr]) + assert r.returncode == 0, f"corec ccr failed: {r.stdout}\n{r.stderr}" + return ccr.read_bytes() + + +def _nod_records(data): + body = segment(data, 5) + (count,) = struct.unpack_from("= 1 + assert live_start < live_end + assert def_nod < 0 or def_nod == live_start + assert ent_flags <= 0x7 + + # NOD is 40B: op/dest/src1/src2/src3/type_id/flags/first_edge/edge_count. + recs = _nod_records(data) + assert recs + for (op, dest, s1, s2, s3, type_id, nod_flags, fe, ec) in recs: + assert -1 <= type_id + assert nod_flags <= 0x7 + # No target facts in src2/src3 for the width/stride carriers. + if op == 8: # IR_ALLOC_ARRAY + assert s2 == 0 and s3 == 0, f"ALLOC_ARRAY carries target facts: {s2},{s3}" + if op in (25, 26): # IR_DEREF / IR_STORE_PTR + assert s3 == 0, "pointer op carries a byte extent in src3" + if nod_flags & 4: + base = s2 if op == 25 else dest + assert base >= 0, "runtime pointer bound has no base reference" + if op == 31: # IR_ADDR_INDEX + assert s3 == 0, "ADDR_INDEX carries a scale in src3" + + +def test_corearch_accepts_v10_and_rejects_v9(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_v10(tmp) + good = tmp / "good.ccr" + good.write_bytes(data) + out = tmp / "good.elf" + r = _run([COREARCH, good, "--elf", "--static", "-o", out]) + assert r.returncode == 0, f"corearch rejected v10: {r.stdout}\n{r.stderr}" + assert out.exists() + + # A structurally coherent v9 image (8 segments, version 589825) must be + # rejected outright, not read as a v10 file with missing segments. + tags = list(range(1, 9)) + bodies = [struct.pack(" 所属节点(数据/state 前向——DAG 拓扑不变量)。 + type_id = TYPE row / -1;flags = 语义特性位;src2/src3 不携带目标事实。 + EDG(8): edg_count + 8B × edg_count {to_nod u32, kind u32} + 节点出边按节点序连续成段(first_edge = 前缀累计——流式零索引重建); + 每条边 to_nod > 所属节点(数据/state 前向——DAG 拓扑不变量)。 幽灵边修复(Task 0 盘点注 A 裁决——播种修复)后的 EDG 内容语义: g_df_var_producer 槽对全部 var 下标恒 -1 播种(未定值 var——全局/参数/ @@ -84,14 +83,22 @@ COREARCH = os.path.join(BASE, 'build/corearch') MAGIC = 0x31524343 # "CCR1" -V7 = 589825 # R2 P6 Task 3:v9;BigInt 批:v9.1 = (9 << 16) | 1 - # (「v7」= 段表架构代号;改名的引用面 40+ 处、收益为零) -SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8] # STR SYM NOD ENT REG EDG TYPE IFACE +V10 = 0x000A0000 # v10 lattice container +SEG_TAGS = [1, 2, 3, 4, 5, 6, 7, 8, 9] # STR TYPE IFACE SYM NOD ENT REG EDG CASE +SEG_STR = 1 +SEG_TYPE = 2 +SEG_IFACE = 3 +SEG_SYM = 4 +SEG_NOD = 5 +SEG_ENT = 6 +SEG_REG = 7 +SEG_EDG = 8 +SEG_CASE = 9 NOD_REC = 40 -ENT_REC = 28 +ENT_REC = 24 REG_REC = 24 EDG_REC = 8 -HEADER_TABLE = 16 + 8 * 12 # 112 +HEADER_TABLE = 16 + 9 * 12 # 124 # 源程序 span 内(pure_add + main 两函数,节点 0..44)期望 EDG 内容——见 # test_v7_edge_content_small_program 的派生说明。 @@ -131,9 +138,9 @@ def __init__(self, data: bytes): assert len(data) >= HEADER_TABLE, f"file too small for v7 header+table: {len(data)}" (magic, ver, seg_count, reserved) = struct.unpack_from('<4I', data, 0) assert magic == MAGIC, f"bad magic {magic:#x}" - assert ver == V7, f"expected version {V7}, got {ver}" - assert seg_count == 8, f"expected 8 segments, got {seg_count}" - assert reserved == 0, f"reserved != 0: {reserved}" + assert ver == V10, f"expected version {V10}, got {ver}" + assert seg_count == 9, f"expected 9 segments, got {seg_count}" + assert reserved <= 0x7, f"reserved/reserved-flag bits set: {reserved}" # Segment table: canonical order, contiguous layout self.segs = {} cur = HEADER_TABLE # first body follows the whole table @@ -154,7 +161,7 @@ def body(self, tag: int): # --- segment record walkers (each body starts with its count) --- def str_table(self): - b = self.body(1) + b = self.body(SEG_STR) (n,) = struct.unpack_from(' 所属节点(前向拓扑);kind ∈ {0=数据, 1=state}。 返回 edges: {node: [(to, kind), ...]}(文件序)。""" - b = self.body(6) + b = self.body(SEG_EDG) nod = self.nod() n = len(nod) (edg_cnt,) = struct.unpack_from('` `nod op dest s1 s2 s3 tk fe ec ` `edge to kind ` (one per out-edge of node i, index order) - Returns (count, {i: (op, dest, s1, s2, s3, tk, fe, ec)}, {i: [(to, kind)]}).""" + Returns (count, {i: (op, dest, s1, s2, s3, type_id, flags_, fe, ec)}, {i: [(to, kind)]}).""" lines = out.splitlines() assert lines and lines[0].startswith('objects: '), \ f"dump missing count header: {lines[:3]!r}" @@ -424,7 +429,7 @@ def model_compute_entries(nod, funcs, gcount, regs): last = [-1] * vc for ii in range(ic): inst = ist + ii - op, d, s1, s2, s3, tk, fe, ec = nod[inst] + op, d, s1, s2, s3, type_id, flags_, fe, ec = nod[inst] for col in (d, s1, s2): if vs <= col < vs + vc: lv = col - vs @@ -434,7 +439,7 @@ def model_compute_entries(nod, funcs, gcount, regs): prev = [-1] * vc # open-version entry id per local var for ii in range(ic): inst = ist + ii - op, d, s1, s2, s3, tk, fe, ec = nod[inst] + op, d, s1, s2, s3, type_id, flags_, fe, ec = nod[inst] dv = -1 if op == IR_STORE: if vs <= s1 < vs + vc: @@ -454,23 +459,23 @@ def model_compute_entries(nod, funcs, gcount, regs): # closure: overwrite LE of the still-open version row = defs_out[pe] defs_out[pe] = (row[0], row[1], row[2], row[3], pend, - row[5], row[6]) - defs_out.append((dv, 1, inst, inst, last_global, -1, 0)) + row[5]) + defs_out.append((dv, 1, inst, inst, last_global, 0)) prev[lv] = len(defs_out) - 1 row_no += 1 for lv2 in range(vc): if prev[lv2] < 0 and first[lv2] >= 0 and last[lv2] >= 0: defs_out.append((vs + lv2, 1, -1, ist + first[lv2], - ist + last[lv2], -1, 0)) + ist + last[lv2], 0)) row_no += 1 blocks.append((ent0, row_no - ent0)) # version = 同 var 文件序组内 1-based 序数 seen = {} rows = [] - for (var_id, _, dn, ls, le, ho, fl) in defs_out: + for (var_id, _, dn, ls, le, fl) in defs_out: ord_ = seen.get(var_id, 0) + 1 seen[var_id] = ord_ - rows.append((var_id, ord_, dn, ls, le + 1, ho, fl)) + rows.append((var_id, ord_, dn, ls, le + 1, fl)) return rows, blocks @@ -557,15 +562,14 @@ def test_v7_ent_hand_expected_small(): assert len(ents) >= len(exp), \ f"entries {len(ents)} < hand-expected {len(exp)}" for k, (fidx, erow) in enumerate(zip([0] * 2 + [1] * 10, exp)): - evar, ever, edef, els, ele, eho, efl = ents[k] + evar, ever, edef, els, ele, efl = ents[k] assert evar == var_of[(fidx, erow[0])], \ f"row {k}: var {evar} != decl-row {erow[0]} var " \ f"{var_of[(fidx, erow[0])]}" assert (ever, edef, els, ele) == erow[1:], \ f"row {k} {evar}: got ({ever},{edef},{els},{ele}) " \ f"expected {erow[1:]}" - assert eho == -1 and efl == 0, \ - f"row {k}: home/flags ({eho},{efl}) != (-1,0)" + assert efl == 0, f"row {k}: flags {efl} != 0" # SYM func 记录:块界回填 = 文件行 0..1 / 2..11;param_ents 实回填 # (identity 参数 n 的 def=-1 条目 = 文件行 1) f0, f1 = funcs[0], funcs[1] @@ -648,7 +652,7 @@ def test_v7_ent_model_replay_pure_add(): f"!= model ({me},{ml})" # loader 语义不变量显式断言(与 load_ccr ENT 校验同面) instr_cnt = len(nod) - for k, (ev, evr, ed, els, ele, eho, efl) in enumerate(ents): + for k, (ev, evr, ed, els, ele, efl) in enumerate(ents): assert evr >= 1, f"row {k}: version {evr} < 1" assert 0 <= ev < len(sym['globals']) + sum(fn['var_count'] for fn in funcs), \ @@ -694,10 +698,10 @@ def test_v7_ent_hand_expected_pure_add_block(): (vs0 + 0, 1, -1, 1, 2), (vs0 + 1, 1, -1, 1, 2), ] got = [(ev, ever, edef, els, ele) for (ev, ever, edef, els, ele, - eho, efl) in ents[:4]] + efl) in ents[:4]] assert got == exp, f"pure_add block:\n got {got}\n expected {exp}" - for (ev, ever, edef, els, ele, eho, efl) in ents[:4]: - assert eho == -1 and efl == 0 + for (ev, ever, edef, els, ele, efl) in ents[:4]: + assert efl == 0 f0 = funcs[0] assert (f0['first_ent'], f0['last_ent']) == (0, 3), \ f"pure_add range ({f0['first_ent']},{f0['last_ent']}) != (0,3)" @@ -715,7 +719,7 @@ def test_v7_ent_hand_expected_pure_add_block(): block = ents[main['first_ent']:main['last_ent'] + 1] assert len(block) >= 20, f"main block too small: {len(block)}" by_decl = {} - for (ev, ever, edef, els, ele, eho, efl) in block: + for (ev, ever, edef, els, ele, efl) in block: by_decl.setdefault(ev - mvs, []).append((ever, edef, els, ele)) assert by_decl[9] == [(1, 20, 20, 22), (2, 22, 22, 35)], \ f"main v versions {by_decl[9]}" @@ -737,7 +741,7 @@ def _mutate_ent_field(data, rec_idx, field_off, value, fmt=' 0, "precondition: ENT must carry real records" - ent_off, _ = v7.segs[4] + ent_off, _ = v7.segs[SEG_ENT] at = ent_off + 4 + rec_idx * ENT_REC + field_off struct.pack_into(fmt, data, at, value) return ents @@ -858,7 +862,7 @@ def test_v7_loader_rejects_sym_ent_block_mismatch(): f0 = sym['funcs'][0] assert f0['first_ent'] >= 0, \ f"precondition: func0 first_ent real, got {f0['first_ent']}" - sym_off, _ = v7.segs[2] + sym_off, _ = v7.segs[SEG_SYM] # SYM 体:global_count(4) + globals(16B×G) + func_count(4) + func0 头 # 24B——first_ent = 头内 +16 g = len(sym['globals']) @@ -884,7 +888,7 @@ def test_v7_loader_rejects_sym_ent_block_mismatch(): def test_v7_layout_and_walk(): - """段表架构:magic/version=8/8 段规范序(EDG=tag 6 必落;TYPE=7/IFACE=8 + """段表架构:magic/version=0x000A0000/9 段规范序(EDG=tag 8 必落;TYPE=2/IFACE=3 空壳——R2 P4 Task 1)/offset 连续/NOD 40B(P6 T3 起)+ 邻接域/EDG 段完整走查 == 文件大小。""" src = ("fn add(a: int, b: int) -> int:\n return a + b\nfn main() -> int:\n s : ., mut = 0\n for i in 0..4:\n s = s + i\n return s\n") @@ -908,8 +912,8 @@ def test_v7_layout_and_walk(): # Task 2: ENT 实记录(corec 产条目)——函数有 var 即有条目(参数/本地 # 引用 + _arena 定值),本程序条目数 > 0;全行字段自洽 assert len(e) > 0, f"ENT empty — Task 2: corec should write entries: {e}" - for (ev, evr, ed, els, ele, eho, efl) in e: - assert evr >= 1 and 0 <= ev and eho == -1 and efl == 0 + for (ev, evr, ed, els, ele, efl) in e: + assert evr >= 1 and 0 <= ev and efl == 0 assert els < ele and (ed < 0 or ed == els) edg = v7.edg() # full EDG walk + forward/kind invariant checks # EDG 段必落且至少一条边(每函数 arena_new→arena_reset def-use 存在) @@ -967,8 +971,17 @@ def test_v7_object_surface_recipe_readable(): f"dumped nodes {len(nodes)} != file nodes {len(nod)}" for i, rec in enumerate(nod): assert i in nodes, f"node {i} missing from dump" - assert nodes[i] == rec, \ + op, dest, s1, s2, s3, type_id, flags_, fe, ec = rec + # The dump's tk column is the reconstructed semantic code; the wire + # has type_id/flags instead. Compare the shared fields and require + # the diagnostic code to agree with the semantic type domain. + assert nodes[i][:5] == (op, dest, s1, s2, s3), \ f"node {i}: object surface {nodes[i]} != file record {rec}" + assert nodes[i][6:] == (fe, ec), \ + f"node {i}: adjacency drift {nodes[i]} != {rec}" + if type_id >= 0: + assert nodes[i][5] == type_id, \ + f"node {i}: dump tk {nodes[i][5]} != type_id {type_id}" # 出边遍历 == 文件 EDG(空出边节点两侧都缺省) for i, row in file_edges.items(): assert edges.get(i) == row, \ @@ -993,7 +1006,7 @@ def test_v7_object_surface_recipe_readable(): ktext = open(kern_src, encoding='utf-8').read() assert 'fn nod_inputs(n: int):' in ktext, \ "kernel nod_inputs recipe-query wrapper missing (dangling note reverted)" - drv_src = os.path.join(BASE, 'src/compiler/corearch.cr') + drv_src = os.path.join(BASE, 'src/arch/x86_64/corearch.cr') dtext = open(drv_src, encoding='utf-8').read() assert 'nod_inputs(ni)' in dtext, \ "dump_object_surface must traverse edges via kernel nod_inputs" @@ -1072,9 +1085,8 @@ def test_v7_edge_content_small_program(): def test_v7_loader_rejects_version_ne_7(): - """v9-only(R2 P6 Task 3 起合法版本 = 9):version 改成 7/8/6/5(**旧 v7 六段 - 文件与 v8 文件整类拒收**——D10 的落点:不得静默当「TYPE/IFACE 缺席 = 空表」)或 - 任意非 9 → corearch 必须拒绝。""" + """v10-only: version 589825 (v9.1) and older v7/v8 values are rejected + outright (不得静默当「段缺席 = 空表」).""" src = "fn main() -> int:\n return 42\n" ccr_path = os.path.join(BASE, 'build/test_v7_reject.ccr') try: @@ -1083,7 +1095,7 @@ def test_v7_loader_rejects_version_ne_7(): pass try: corec_ccr(src, ccr_path) - for bad_ver in (8, 7, 6, 5): + for bad_ver in (589825, 8, 7, 6, 5): data = bytearray(read_ccr(ccr_path)) struct.pack_into('= 1, f"node 0 has no out edges: {nod[0]}" - edg_off, _ = v7.segs[6] + assert nod[0][8] >= 1, f"node 0 has no out edges: {nod[0]}" + edg_off, _ = v7.segs[SEG_EDG] # node 0 的 EDG 记录 = edg_count(4B) 后第一条:to_nod 字段 = 前 4B struct.pack_into('= 1, f"node 0 has no out edges: {nod[0]}" - nod_off, _ = v7.segs[3] + assert nod[0][8] >= 1, f"node 0 has no out edges: {nod[0]}" + nod_off, _ = v7.segs[SEG_NOD] row0_ec = nod_off + 4 + 0 * NOD_REC + 36 # edge_count field (+36 in 40B row) - struct.pack_into(' 0, "built .ccr should carry real ENT records" - for (ev, evr, ed, els, ele, eho, efl) in ents: - assert evr >= 1 and eho == -1 and efl == 0 + for (ev, evr, ed, els, ele, efl) in ents: + assert evr >= 1 and efl == 0 assert els < ele and (ed < 0 or ed == els) assert sum(len(v) for v in v7.edg().values()) >= 2 # direct corearch invocation on the same .ccr @@ -1281,7 +1294,7 @@ def test_v7_ent_real_optmeta_absent(): pvar = vs + p pid = -1 for i in range(start, start + pcnt): - (ev, evr, ed, els, ele, eho, efl) = ents[i] + (ev, evr, ed, els, ele, efl) = ents[i] if ev == pvar and ed == -1: pid = i assert f['param_ents'][p] == pid, \ @@ -1383,7 +1396,7 @@ def test_v7_sym_reg_target_shape(): for en in ents: assert 0 <= en[0] < total_vars, \ f"ENT var_id {en[0]} outside var namespace 0..{total_vars - 1}" - for (op, dest, s1, s2, s3, tk, fe, ec) in v7.nod(): + for (op, dest, s1, s2, s3, type_id, flags_, fe, ec) in v7.nod(): if dest >= 0: assert dest < total_vars, f"NOD dest {dest} outside var namespace" assert fe >= 0 and ec >= 0 # 邻接域自洽(完整校验在 edg() 走查) @@ -1449,7 +1462,7 @@ def test_v7_sym_func_shapes(): pid = add['param_ents'][p] assert pid >= 0, \ f"add param_ents[{p}] == -1 (expected def=-1 entry id)" - (ev, evr, ed, els, ele, eho, efl) = ents[pid] + (ev, evr, ed, els, ele, efl) = ents[pid] assert ed == -1 and ev == g0 + p, \ f"add param_ents[{p}] -> row {pid} var {ev} (want {g0 + p})" assert funcs[1]['param_count'] == 0 and funcs[1]['param_ents'] == [] @@ -1500,7 +1513,7 @@ def test_v7_loader_rejects_root_span_beyond_nod_space(): rid = roots[-1] assert regs[rid][3] == nod_cnt, \ f"last root exit {regs[rid][3]} != nod_count {nod_cnt}: {regs[rid]}" - reg_off, _ = v7.segs[5] + reg_off, _ = v7.segs[SEG_REG] patch_at = reg_off + 4 + rid * REG_REC + 12 # exit_nod field (+12 in row) struct.pack_into(' provenance cannot fold the offset). + "ptr_runtime_bound_callee", + "", + "a := [1,2,3,4]\ni := 2\np := &a[i]\nreturn *p\n", + "parity", 3, 3, + ), + ( + # Same flag on IR_STORE_PTR (runtime base lives in dest). + "ptr_runtime_bound_store_callee", + "", + "a := [1,2,3,4]\ni := 2\np := &a[i]\n*p = 9\nreturn a[2]\n", + "parity", 9, 9, + ), + # —— 切片 / 边界检查族(24 SLICE / 30 BOUNDS_CHECK)—— ( "slice_runtime_bounds_callee", "", - "a : [int;5] = [10,20,30,40,50]; lo := 1; hi := 3; s := a[lo..hi]; return s[1];", + "a : [int;5] = [10,20,30,40,50]\nlo := 1\nhi := 3\ns := a[lo..hi]\nreturn s[1]", "parity", 30, 30, ), ( "slice_literal_bounds_callee", "", - "a : [int;5] = [10,20,30,40,50]; s := a[1..3]; return s[1];", + "a : [int;5] = [10,20,30,40,50]\ns := a[1..3]\nreturn s[1]", "parity", 30, 30, ), ( @@ -112,7 +138,7 @@ # ELF = ud2 SIGILL。两路径都非静默(rc 非 0)。 "slice_oob_callee_loud", "", - "a : [int;5] = [10,20,30,40,50]; lo := 1; hi := 3; s := a[lo..hi]; return s[2];", + "a : [int;5] = [10,20,30,40,50]\nlo := 1\nhi := 3\ns := a[lo..hi]\nreturn s[2]", "loud", 255, SIGILL, ), # —— dyn 族(43 DYN_PACK / 42 DYN_VAL / 41 DYN_TAG / 44 DYN_DISPATCH)—— @@ -133,26 +159,26 @@ ( "nested_call_callee", "fn g2()->int:\n return 7\n", - "return g2();", + "return g2()", "parity", 7, 7, ), ( "recursion_fib_callee", "fn fib(n: int)->int:\n if n < 2:\n return n\n return fib(n-1) + fib(n-2)\n", - "return fib(10);", + "return fib(10)", "parity", 55, 55, ), ( "mutual_recursion_callee", "fn is_even(n: int)->int:\n if n == 0:\n return 1\n return is_odd(n - 1)\nfn is_odd(n: int)->int:\n if n == 0:\n return 0\n return is_even(n - 1)\n", - "return is_even(10);", + "return is_even(10)", "parity", 1, 1, ), ( # 深度守卫:无界递归必须响亮中止(rc 255 + 诊断),不挂死 "depth_guard_runaway_loud", "fn r(n: int)->int:\n return r(n + 1)\n", - "return r(0);", + "return r(0)", "loud", 255, SIGSEGV, ), # —— 并发族(27 SPAWN:range go;28 YIELD / 29 AWAIT eager 近似)—— @@ -173,7 +199,7 @@ ( "yield_in_callee", "", - "yield; return 11;", + "yield\nreturn 11", "parity", 11, 11, ), ( @@ -187,7 +213,7 @@ ( "extern_call_callee_loud", "extern fn putchar(c: int) -> int\n", - "return putchar(65);", + "return putchar(65)", "loud", 2, SIGSEGV, ), # —— 48 FNADDR / 51 IR_APPROX —— diff --git a/tests/selfhost/test_native_memory.py b/tests/selfhost/test_native_memory.py index 5f806957..98c92abb 100644 --- a/tests/selfhost/test_native_memory.py +++ b/tests/selfhost/test_native_memory.py @@ -76,6 +76,46 @@ 33, 0, ), + ( + "array_pointer_deref_store", + """ +fn main() -> int: + arr : ., mut = [10, 20, 30] + p := &arr[1] + *p = 41 + x : ., mut = 7 + q := &x + *q = 8 + return *p + x + arr[0] + arr[2] +""", + 89, + 1, + ), + ( + "pointer_runtime_bound", + """ +fn main() -> int: + a := [1, 2, 3, 4] + i := 2 + p := &a[i] + return *p +""", + 3, + 0, + ), + ( + "pointer_runtime_bound_store", + """ +fn main() -> int: + a := [1, 2, 3, 4] + i := 2 + p := &a[i] + *p = 9 + return a[2] +""", + 9, + 0, + ), ( "aggregate_o1", """ diff --git a/tests/selfhost/test_region_cfg.py b/tests/selfhost/test_region_cfg.py index cb9fa2f2..07bf6d93 100644 --- a/tests/selfhost/test_region_cfg.py +++ b/tests/selfhost/test_region_cfg.py @@ -229,12 +229,12 @@ def u32(): ver = u32() seg_count = u32() reserved = u32() - assert reserved == 0 - # Segment table: canonical order (STR SYM NOD ENT REG EDG TYPE IFACE), - # contiguous bodies(R2 P4 Task 1 起 8 段——TYPE/IFACE 空壳) + assert reserved <= 0x7 + # Segment table: canonical v10 order (STR TYPE IFACE SYM NOD ENT REG EDG CASE), + # contiguous bodies. segs = {} cur = 16 + seg_count * 12 # first body follows the whole table - for tag in (1, 2, 3, 4, 5, 6, 7, 8): + for tag in (1, 2, 3, 4, 5, 6, 7, 8, 9): t = u32() off = u32() size = u32() @@ -255,21 +255,20 @@ def body(tag): sl = struct.unpack_from('= 2, \ f"expected REG segment with >=2 regions (func+for), got {sg_count}" assert end == fsize, f"format walk ended at {end} of {fsize} bytes" @@ -306,7 +305,8 @@ def test_ccr_writer_rejects_i32_overflow_inputs(): with open(ccr_source, encoding="utf-8") as fh: text = fh.read() assert "fn ccr_i32_fits(val: int) -> int" in text - assert "if ccr_validate_i32_fields() == 0 { return -1; }" in text + assert "if ccr_validate_i32_fields() == 0:" in text + assert "return -1" in text def test_ccr_roundtrip_v2(): """save→load 往返守卫:v6 文件经 corearch 加载后 ELF 输出行为不变。 diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index ab6ad6e9..c75457f8 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -178,10 +178,10 @@ # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 canary_elf pa 826d74cf7f099f9758e11a64d443695ff8360327a88e7eb5922ddfd617e0dce5 53430 -pa_ccr pa.ccr 6ef24784e9cf043f38141c1f6bcf809d4ac5b89fd71cb74a34ff771b7a13aacb 96998 -pa_static_ccr pa_st.bin.ccr e1e7a1c1aeb8c883bb88b21be907f9fb0b1fd2c28185997adca84caf4d17bf33 97141 -gt_ccr gt.ccr 93ed0dc5cb33964df5857ba21738f6880700a5248098c3f043087e1369b26f23 143700 -gt_static_ccr gt_st.bin.ccr a9e5343389de547a0ab9a9dde33b030877c44e5505deee24eb683715b700c88f 143843 +pa_ccr pa.ccr f5fa7faa9e5fdf28db03dbd78e03e275e416cab06c222d4870a07aa12f98a77f 93710 +pa_static_ccr pa_st.bin.ccr df9621c9a9bb45b27b777fb0b4d743602077b65775b1e11ae57b54c98bb4ba2c 93853 +gt_ccr gt.ccr e7f06f04fcecf4fc767e2499566d9e59be6503fd5933f74808ca6783c8bf7308 138672 +gt_static_ccr gt_st.bin.ccr 48d40ccb4f3bfdb7d412e669636ada6fac5a0ea2df95537ccb9749436c59e42b 138815 # ⚠ **本组为最终值(全部编译器源码改动落定后重测)**:重锁分量有两个,**都不是发射面泄漏**—— # ① **内容面**:`fmt.cr` 新增 `bool_str` ⇒ 凡 `import fmt` 的程序多一个函数体(ELF/`.ccr` 同增); # ② **编译器身份位移**(#123 同机制):编译器源码任何改动都会改变**其自身 intern 序** ⇒ 同语义下 @@ -302,3 +302,16 @@ gt_static_ccr gt_st.bin.ccr a9e5343389de547a0ab9a9dde33b030877c44e5505deee24eb68 # pa_static_ccr 667ca1f65421cc11cdc8935e070d663e2b4fa1b692e26e80dad75ace776103dd 97128 # gt_ccr 4df02acd1ea343601ae8a016cff093bf56d7a90f7b6b06c2811b1b3a3a2d3e41 143687 # gt_static_ccr bacd9eddd956dce88df3de675a29d0857d832c5a843781707a2e84b3c864f8a1 143830 + +# ── 【本批重锁 2026-10-24】ccr v10 cutover(Task 4)── +# 四条 `.ccr` 全变:header version 589825 → 0x000A0000、seg_count 8 → 9、 +# 段表 112B → 124B、段序改为 STR TYPE IFACE SYM NOD ENT REG EDG CASE、 +# ENT 28B → 24B(去掉 home)、NOD 40B 的 tk/item 两槽改为 type_id/flags +# (src2/src3 不再携带访问宽度/步长/分配字节数,目标事实由 corearch 用 +# type_id + x86-64 DataLayout 重建)、新增 4B 空 CASE 表。 +# 归因 = 容器与记录布局换代,非发射面回归:canary ELF 逐字节不变 +# (826d74cf…),D1 5/5 PASS。旧值留痕(自本批起作废): +# pa_ccr 6ef24784e9cf043f38141c1f6bcf809d4ac5b89fd71cb74a34ff771b7a13aacb 96998 +# pa_static_ccr e1e7a1c1aeb8c883bb88b21be907f9fb0b1fd2c28185997adca84caf4d17bf33 97141 +# gt_ccr 93ed0dc5cb33964df5857ba21738f6880700a5248098c3f043087e1369b26f23 143700 +# gt_static_ccr a9e5343389de547a0ab9a9dde33b030877c44e5505deee24eb683715b700c88f 143843 From e54c110db9f2dbd4e8f94e0d816c5f3f3969b558 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 01:03:10 +0800 Subject: [PATCH 46/58] ccr: fix v10 header flags and reject legacy target facts --- src/arch/x86_64/backend_flags.cr | 9 +- src/arch/x86_64/corearch.cr | 4 +- src/arch/x86_64/regalloc.cr | 20 ++- src/compiler/ccr_io.cr | 190 +++++++++++++++++------ src/compiler/ccr_schema.cr | 11 ++ src/compiler/main.cr | 4 + src/targets/x86_64/linux/elf/main.cr | 4 +- tests/selfhost/test_backend_bootstrap.py | 4 +- tests/selfhost/test_ccr_types.py | 10 +- tests/selfhost/test_ccr_v10_format.py | 147 +++++++++++++++++- tools/baseline/canary_values.tsv | 20 ++- 11 files changed, 348 insertions(+), 75 deletions(-) diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index b2d2deb5..7ef9c05d 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -27,12 +27,11 @@ fn register_backend_flags(): cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") -// Select the int representation after the .ccr header has been read. Compiler -// and self-host artifacts carry CCR_FLAG_RAW_INT; ordinary user programs get -// tagged mathematical ints by default. --raw-int is an explicit build-chain -// escape hatch. +// Select the int representation. The .ccr header carries no raw-int bit (only +// program flags); the frontend passes --raw-int explicitly for compiler and +// self-host artifacts. Ordinary user programs use tagged mathematical ints. fn backend_select_int_mode(): - if cli_has("raw-int") != 0 || g_ccr_raw_int != 0: + if cli_has("raw-int") != 0: g_x86_big_enabled = 0 else: g_x86_big_enabled = 1 diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index 8e6d925c..fe4f9a17 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -398,7 +398,9 @@ fn corearch_main() -> int: // g_ir_instrs stream with build_linear_schedule after a successful load and // before dispatch or emission. ELF emission and the allocation dumps all read // the linear stream. - build_linear_schedule() + if build_linear_schedule() != 0: + println("error: invalid .ccr file") + return 1 // Hidden channel: dump the object image after linear reconstruction and before // emission. It returns without entering the O2 or emission paths. diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index 5b56fb08..8b87d2a5 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -950,6 +950,8 @@ fn corearch_alloc_size_of_var(v: int) -> int: // Runtime pointer bound size for IR_DEREF/IR_STORE_PTR. The semantic flag says // a runtime bound exists; the byte size is derived from the allocation node. +// -1 means "flag set but base/size unresolvable" — fail closed, never fall back +// to a null-check-only emission. fn corearch_nod_runtime_bound_size(n: int) -> int: fl := nod_type_flags(n) if (fl / CCR_NOD_FLAG_RUNTIME_PTR_BOUNDS) % 2 != 1: @@ -961,8 +963,11 @@ fn corearch_nod_runtime_bound_size(n: int) -> int: else if op == IR_STORE_PTR: base = nod_dest(n) if base < 0: - return 0 - return corearch_alloc_size_of_var(base) + return -1 + sz := corearch_alloc_size_of_var(base) + if sz <= 0: + return -1 + return sz // 语义对象模型:从 NOD 重建线性调度。 // ccr_io.cr load_ccr 从内核对象缓冲顺序生成 48 字节线性流 g_ir_instrs。 @@ -971,7 +976,7 @@ fn corearch_nod_runtime_bound_size(n: int) -> int: // g_ir_func_instr_count)留在 loader。 // 调用方是 corearch.cr 和组合根 src/targets/x86_64/linux/elf/main.cr;load_ccr 成功后、 // 分派和发射前调用一次。 -fn build_linear_schedule(): +fn build_linear_schedule() -> int: grow_ir_instrs(g_v7_nod_count) ni : ., mut = 0 loop: @@ -987,11 +992,16 @@ fn build_linear_schedule(): // v10: the .ccr does not carry target facts. Reconstruct the access // width for pointer dereferences and the element stride for allocations // from the semantic type plus the x86-64 layout. The runtime-bound size - // for pointer stores/loads is derived from the allocation node. + // for pointer stores/loads is derived from the allocation node; a set + // flag with no resolvable allocation is a malformed file (fail closed). if op == IR_ALLOC_ARRAY: iri_set_s2(ni, corearch_nod_alloc_stride()) else if op == IR_DEREF || op == IR_STORE_PTR: iri_set_tk(ni, corearch_nod_access_width(ni)) - iri_set_s3(ni, corearch_nod_runtime_bound_size(ni)) + bsz := corearch_nod_runtime_bound_size(ni) + if bsz < 0: + return -1 + iri_set_s3(ni, bsz) g_ir_instr_count = ni + 1 ni = ni + 1 + return 0 diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 91123744..c99f5df0 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -110,20 +110,11 @@ // No bitwise ops in Core; use arithmetic instead. CCR_MAGIC : int = 827474755 // "CCR1" (0x31524343) -// v10 = 0x000A0000. The loader accepts only this value; v9 (589825) is rejected. -CCR_VERSION : int = 655360 // CCR_V10_VERSION (0x000A0000) -// Header bit 0 currently also carries the raw-int compiler/self-host selection. -// It is the v10 PORTABLE bit position; Task 8 removes the legacy overload. -CCR_FLAG_RAW_INT : int = 1 -CCR_FLAG_KNOWN_MASK : int = 7 // PORTABLE | SYSTEM | CONCURRENT -CCR_SEG_COUNT : int = 9 // CCR_V10_SEG_COUNT: STR TYPE IFACE SYM NOD ENT REG EDG CASE -CCR_SEG_TYPE : int = 2 // CCR_V10_SEG_TYPE -CCR_SEG_IFACE : int = 3 // CCR_V10_SEG_IFACE - -// Set by save_ccr from corec (compiler-source detection) and by load_ccr in the -// backend. It selects the raw-int compiler path instead of user-program tagging. + +// Set by save_ccr from corec (compiler-source detection) and used to compute +// the SYSTEM header bit and to pass --raw-int to the backend. It is not a wire +// field and does not select int mode by itself. g_ccr_emit_raw_int : int, mut -g_ccr_raw_int : int, mut // NOD record: 40 bytes. // {op i32, dest i32, s1 i64, src2 i32, src3 i32, type_id i32, flags u32, @@ -132,20 +123,17 @@ g_ccr_raw_int : int, mut // (CCR_NOD_FLAG_*). Widths, strides, and byte extents are target facts and are // reconstructed by corearch, never stored here. first_edge and edge_count locate // this node's outgoing edges in EDG. The in-memory object record keeps the same -// eight semantic fields without the adjacency pair. -ESZ_NOD_DISK : int = 40 // CCR_V10_NOD_DISK +// eight semantic fields without the adjacency pair. CCR_V10_NOD_DISK = 40. // EDG record: 8 bytes, {to_nod u32, kind u32}. The source node is implicit: // node i's outgoing edges are the contiguous range // [first_edge, first_edge + edge_count). -ESZ_EDGE_DISK : int = 8 // REG record: 24 bytes, {kind i32, parent i32, enter_nod u32, exit_nod u32, // first_ent i32, last_ent i32}. nstart and ncount are not stored; the loader // derives nstart = enter and ncount = exit - enter. // The in-memory SG entry is ESZ_SG (48 bytes, u64 fields), which is not the wire -// format. Use ESZ_SG_DISK for .ccr size math. -ESZ_SG_DISK : int = 24 +// format. Use CCR_V10_REG_DISK for .ccr size math. // SYM 落盘记录尺寸: // global records: 16B {name u32, type u32, init_val i64}. @@ -160,7 +148,7 @@ ESZ_VARDECL_DISK : int = 8 // live_end/flags; version + 半开 live_end 由内存 24B 表转换,见文件头注释). // The in-memory entry table is ESZ_ENTRY (24 bytes, no version field,the // per-var ordinal is derivable from group order); disk record != memory record. -ESZ_ENTRY_DISK : int = 24 // CCR_V10_ENT_DISK +// CCR_V10_ENT_DISK = 24. fn bw_byte(val: int, shift: int) -> int: if shift == 0: @@ -340,21 +328,99 @@ fn ccr_sym_seg_size() -> int: return sz fn ccr_nod_seg_size() -> int: - return 4 + g_ir_instr_count * ESZ_NOD_DISK + return 4 + g_ir_instr_count * CCR_V10_NOD_DISK fn ccr_edg_seg_size(edge_total: int) -> int: - return 4 + edge_total * ESZ_EDGE_DISK + return 4 + edge_total * CCR_V10_EDG_DISK fn ccr_ent_seg_size() -> int: - return 4 + g_entry_count * ESZ_ENTRY_DISK + return 4 + g_entry_count * CCR_V10_ENT_DISK fn ccr_reg_seg_size() -> int: - return 4 + g_sg_count * ESZ_SG_DISK + return 4 + g_sg_count * CCR_V10_REG_DISK fn ccr_case_seg_size() -> int: // CASE is an empty table until match lowering writes it: 4B case_count = 0. return 4 +// ── Header flag classification (spec §3.1) ────────────────────────────── +// SYSTEM boundary ops: runtime/FFI/ABI surfaces. ARENA_NEW only counts when the +// scope actually allocates (s1 == 0 means "no arena", per instr.cr); the +// matching ARENA_RESET is then a no-op and need not be counted separately. +// CONCURRENT ops: execution-policy nodes. +fn ccr_op_is_system(op: int, arena_count: int) -> int: + if op == IR_ARENA_NEW: + if arena_count > 0: + return 1 + return 0 + if op == IR_SPAWN || op == IR_YIELD || op == IR_AWAIT || op == IR_HOTPATCH_ROUTE || op == IR_FNADDR || op == IR_CALL_EXTERN || op == IR_INLINE || op == IR_PTR_OF || op == IR_STR_OF: + return 1 + return 0 + +fn ccr_op_is_concurrent(op: int) -> int: + if op == IR_SPAWN || op == IR_YIELD || op == IR_AWAIT: + return 1 + return 0 + +// v10 src3 is an id/op/index/symbol for these ops. Every other op must carry 0 +// in src3: a nonzero value would be a legacy width/scale/byte-extent target +// fact. ALLOC_ARRAY/ADDR_INDEX/DEREF/STORE_PTR are handled explicitly because +// their old src2/src3 facts are the migration's critical cases. +fn ccr_nod_src3_semantic(op: int) -> int: + if op == IR_UNARY || op == IR_BINARY || op == IR_CALL || op == IR_ALLOC_STRUCT || op == IR_LOAD_FIELD || op == IR_STORE_FIELD || op == IR_STORE_INDEX || op == IR_BRANCH || op == IR_SLICE || op == IR_SPAWN || op == IR_HOTPATCH_ROUTE || op == IR_CALL_EXTERN || op == IR_LOAD_INDEX: + return 1 + return 0 + +fn ccr_compute_header_flags() -> int: + hsys : ., mut = 0 + hconc : ., mut = 0 + if g_ccr_emit_raw_int != 0: + hsys = 1 + ii : ., mut = 0 + loop: + if ii >= g_ir_instr_count: + break + if ccr_op_is_system(iri_op(ii), iri_s1(ii)) != 0: + hsys = 1 + if ccr_op_is_concurrent(iri_op(ii)) != 0: + hconc = 1 + ii = ii + 1 + flags : ., mut = 0 + if hsys == 0: + flags = flags + CCR_FLAG_PORTABLE + else: + flags = flags + CCR_FLAG_SYSTEM + if hconc != 0: + flags = flags + CCR_FLAG_CONCURRENT + return flags + +// Post-NOD validation: the header must not claim PORTABLE while the graph has a +// SYSTEM boundary, and CONCURRENT must match the graph both ways. SYSTEM without +// a boundary node is allowed because raw-int build mode also sets SYSTEM. +fn ccr_validate_header_flags(rsv: int) -> int: + hsys := (rsv / CCR_FLAG_SYSTEM) % 2 + hconc := (rsv / CCR_FLAG_CONCURRENT) % 2 + nsys : ., mut = 0 + nconc : ., mut = 0 + ii : ., mut = 0 + loop: + if ii >= g_v7_nod_count: + break + if ccr_op_is_system(nod_op(ii), nod_s1(ii)) != 0: + nsys = 1 + if ccr_op_is_concurrent(nod_op(ii)) != 0: + nconc = 1 + ii = ii + 1 + if nsys != 0 && hsys == 0: + return -1 + if nconc != 0 && hconc == 0: + return -1 + if hconc != 0 && nconc == 0: + return -1 + if hconc != 0 && hsys == 0: + return -1 + return 0 + // TYPE(2) segment size is the prepared buffer length. The content is built by // corec-only ccr_types.cr. ccr_io.cr is shared with corearch and must not refer // to frontend symbols. The buffer includes row_count, so segment size equals @@ -373,7 +439,7 @@ fn ccr_iface_seg_size() -> int: // edge_total = EDG 记录总数(save_ccr 先行收集,见 ccr_collect_edges)。 fn calc_ccr_size(edge_total: int) -> int: - sz : ., mut = 16 + CCR_SEG_COUNT * 12 + sz : ., mut = 16 + CCR_V10_SEG_COUNT * 12 sz = sz + ccr_str_seg_size() sz = sz + ccr_type_seg_size() sz = sz + ccr_iface_seg_size() @@ -553,15 +619,15 @@ fn save_ccr(path: string) -> int: // Header(16B) buf_write_u32(buf, pos, CCR_MAGIC) pos = pos + 4 - buf_write_u32(buf, pos, CCR_VERSION) + buf_write_u32(buf, pos, CCR_V10_VERSION) pos = pos + 4 - buf_write_u32(buf, pos, CCR_SEG_COUNT) + buf_write_u32(buf, pos, CCR_V10_SEG_COUNT) + pos = pos + 4 + // Header flags describe the program, not the integer representation. + // Raw-int build mode is a SYSTEM property and is passed to corearch via + // --raw-int, never through this word. + buf_write_u32(buf, pos, ccr_compute_header_flags()) pos = pos + 4 - rsv : ., mut = 0 - if g_ccr_emit_raw_int != 0: - rsv = CCR_FLAG_RAW_INT - buf_write_u32(buf, pos, rsv) - pos = pos + 4 // reserved / raw-compiler flag // 段体大小(与 calc_ccr_size 同一来源逐段一致;canonical order) s_str := ccr_str_seg_size() @@ -575,7 +641,7 @@ fn save_ccr(path: string) -> int: s_case := ccr_case_seg_size() // Seg table(9 × 12B;offset = 前段尾,从段表后起;规范序 tag 1..9) - o_str : ., mut = 16 + CCR_SEG_COUNT * 12 + o_str : ., mut = 16 + CCR_V10_SEG_COUNT * 12 o_type : ., mut = o_str + s_str o_iface : ., mut = o_type + s_type o_sym : ., mut = o_iface + s_iface @@ -589,11 +655,11 @@ fn save_ccr(path: string) -> int: buf_write_u32(buf, pos + 4, o_str) buf_write_u32(buf, pos + 8, s_str) pos = pos + 12 - buf_write_u32(buf, pos, CCR_SEG_TYPE) + buf_write_u32(buf, pos, CCR_V10_SEG_TYPE) buf_write_u32(buf, pos + 4, o_type) buf_write_u32(buf, pos + 8, s_type) pos = pos + 12 - buf_write_u32(buf, pos, CCR_SEG_IFACE) + buf_write_u32(buf, pos, CCR_V10_SEG_IFACE) buf_write_u32(buf, pos + 4, o_iface) buf_write_u32(buf, pos + 8, s_iface) pos = pos + 12 @@ -1113,7 +1179,7 @@ fn load_ccr(data: string, fsize: int) -> int: // Version: v10 only, with no compatibility or conversion path. ver := buf_read_u32(data, pos) pos = pos + 4 - if ver != CCR_VERSION: + if ver != CCR_V10_VERSION: return -1 // Seg count + header flags @@ -1121,15 +1187,18 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 rsv := buf_read_u32(data, pos) pos = pos + 4 - if seg_cnt != CCR_SEG_COUNT: + if seg_cnt != CCR_V10_SEG_COUNT: return -1 // Only PORTABLE/SYSTEM/CONCURRENT (bits 0..2) are defined; reserved bits - // must be zero. Bit 0 also selects legacy raw-int mode until Task 8. + // must be zero. PORTABLE excludes SYSTEM; CONCURRENT implies SYSTEM. if rsv > CCR_FLAG_KNOWN_MASK: return -1 - g_ccr_raw_int = 0 - if rsv % 2 == 1: - g_ccr_raw_int = 1 + if (rsv % 2 == 1) && ((rsv / CCR_FLAG_SYSTEM) % 2 == 1): + return -1 + if (rsv % 2 == 1) && ((rsv / CCR_FLAG_CONCURRENT) % 2 == 1): + return -1 + if ((rsv / CCR_FLAG_CONCURRENT) % 2 == 1) && ((rsv / CCR_FLAG_SYSTEM) % 2 == 0): + return -1 if seg_cnt > (fsize - 16) / 12: return -1 @@ -1176,7 +1245,7 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 ssz := buf_read_u32(data, pos) pos = pos + 4 - if tg < 1 || tg > CCR_SEG_COUNT: + if tg < 1 || tg > CCR_V10_SEG_COUNT: return -1 if tg != ri + 1: return -1 @@ -1606,7 +1675,7 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 sg_n := buf_read_u32(data, pos) pos = pos + 4 - if sg_n > (seg_end7 - seg_off7) / ESZ_SG_DISK: + if sg_n > (seg_end7 - seg_off7) / CCR_V10_REG_DISK: return -1 sg_i : ., mut = 0 rfcnt : ., mut = 0 // 已见 SG_FUNC 根记录数(记录顺序 = 函数序 1:1) @@ -1689,7 +1758,7 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 instr_cnt := buf_read_u32(data, pos) pos = pos + 4 - if instr_cnt > (seg_end5 - seg_off5) / ESZ_NOD_DISK: + if instr_cnt > (seg_end5 - seg_off5) / CCR_V10_NOD_DISK: return -1 g_v7_nod_sem = alloc((instr_cnt + 8) * ESZ_NOD_SEM) g_v7_nod_meta = alloc((instr_cnt + 8) * ESZ_NOD_META) @@ -1697,7 +1766,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if ii >= instr_cnt: break - if !ccr_has_bytes(pos, ESZ_NOD_DISK, seg_end5): + if !ccr_has_bytes(pos, CCR_V10_NOD_DISK, seg_end5): return -1 opcode := buf_read_u32(data, pos) pos = pos + 4 @@ -1713,6 +1782,20 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // type_id(TYPE row;-1 = 无) fl2 := buf_read_u32(data, pos) pos = pos + 4 // flags(语义特性;非目标事实) + // src2/src3 may not carry v9 target facts. The writer normalizes them; + // the loader rejects old/foreign files instead of silently accepting. + if opcode == IR_ALLOC_ARRAY: + if s2 != 0 || s3 != 0: + return -1 + else if opcode == IR_ADDR_INDEX: + if s3 != 0: + return -1 + else if opcode == IR_DEREF || opcode == IR_STORE_PTR: + if s3 != 0: + return -1 + else if ccr_nod_src3_semantic(opcode) == 0: + if s3 != 0: + return -1 nfe := buf_read_u32(data, pos) pos = pos + 4 // first_edge(邻接索引) nec := buf_read_u32(data, pos) @@ -1735,6 +1818,11 @@ fn load_ccr(data: string, fsize: int) -> int: ii = ii + 1 g_v7_nod_count = instr_cnt + // Header SYSTEM/CONCURRENT bits must agree with the graph. This needs NOD + // only, so it runs here (before ENT/EDG/TYPE consumption). + if ccr_validate_header_flags(rsv) != 0: + return -1 + // REG root span upper-bound check. Function instruction bounds (root_region // span) are filled when REG is parsed, but instr_cnt is only known at NOD. At // that point only rex >= ren was checked, not the upper bound. An out-of-range @@ -1761,14 +1849,14 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 ent_cnt := buf_read_u32(data, pos) pos = pos + 4 - if ent_cnt > (seg_end6 - seg_off6) / ESZ_ENTRY_DISK: + if ent_cnt > (seg_end6 - seg_off6) / CCR_V10_ENT_DISK: return -1 grow_entries(ent_cnt) eii : ., mut = 0 loop: if eii >= ent_cnt: break - if !ccr_has_bytes(pos, ESZ_ENTRY_DISK, seg_end6): + if !ccr_has_bytes(pos, CCR_V10_ENT_DISK, seg_end6): return -1 ev := buf_read_i32(data, pos) pos = pos + 4 // var_id @@ -1785,6 +1873,9 @@ fn load_ccr(data: string, fsize: int) -> int: // Semantic validation: reject out-of-range values. if evr < 1: return -1 + if efl > 0xF: + return -1 + // ENT flags bits 4+ are reserved. if ev < 0 || ev >= g_ir_var_count: return -1 // var 命名空间 = SYM 行重建总量 @@ -1806,6 +1897,9 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl) g_entry_count = eii + 1 eii = eii + 1 + // The record walk must consume the ENT body exactly (no trailing bytes). + if pos != seg_end6: + return -1 // 函数条目段界重建:条目按函数升序成块落盘(compute_entries func_i 升序), // 每函数一段 [start, count),块判定 = var 槽 in 该函数 var 区间(var 槽 // 全属唯一函数,段序与函数序一致)。重建结果与 SYM func 记录 @@ -1857,7 +1951,7 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 edg_cnt := buf_read_u32(data, pos) pos = pos + 4 - if edg_cnt > (seg_end8 - seg_off8) / ESZ_EDGE_DISK: + if edg_cnt > (seg_end8 - seg_off8) / CCR_V10_EDG_DISK: return -1 v7_buf : string, mut = "" if edg_cnt > 0: @@ -1884,7 +1978,7 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if rec >= nec: break - if !ccr_has_bytes(pos, ESZ_EDGE_DISK, seg_end8): + if !ccr_has_bytes(pos, CCR_V10_EDG_DISK, seg_end8): return -1 eto := buf_read_u32(data, pos) pos = pos + 4 diff --git a/src/compiler/ccr_schema.cr b/src/compiler/ccr_schema.cr index db2b4fb1..399142a5 100644 --- a/src/compiler/ccr_schema.cr +++ b/src/compiler/ccr_schema.cr @@ -39,6 +39,17 @@ CCR_V10_REG_DISK : int = 24 CCR_V10_EDG_DISK : int = 8 CCR_V10_CASE_DISK : int = 24 +// v10 header flags (spec §3.1). They describe program properties only; no bit +// carries raw-int, external-binary-interface, or target identity. +// bit 0 PORTABLE no raw/FFI/external-binary boundary +// bit 1 SYSTEM contains a system boundary node (or raw-int build mode) +// bit 2 CONCURRENT contains concurrency/execution-policy nodes +// bit 3+ reserved, must be zero +CCR_FLAG_PORTABLE : int = 1 +CCR_FLAG_SYSTEM : int = 2 +CCR_FLAG_CONCURRENT : int = 4 +CCR_FLAG_KNOWN_MASK : int = 7 + // v10 NOD semantic flags. These are node features, not target facts: no bit // carries a machine extent or physical location. corearch reconstructs those // from the semantic type and the active target layout. diff --git a/src/compiler/main.cr b/src/compiler/main.cr index 127850bb..26b88b0c 100644 --- a/src/compiler/main.cr +++ b/src/compiler/main.cr @@ -679,6 +679,10 @@ fn corec_main() -> int: println("generate ELF...") // The target owns the backend name, ELF flag, link mode, and output flag. cmd2 : ., mut = target_backend_command(ccr_path, out_path, cli_has("static"), g_opt_level) + // Raw-int is a build-chain decision, not a wire flag: pass it explicitly so + // compiler/self-host artifacts keep raw machine ints. + if g_ccr_emit_raw_int != 0: + cmd2 = cmd2 + " --raw-int" self_path := get_arg(0) sl2 := str_len(self_path) if sl2 > 0: diff --git a/src/targets/x86_64/linux/elf/main.cr b/src/targets/x86_64/linux/elf/main.cr index 4bbf945e..119a841c 100644 --- a/src/targets/x86_64/linux/elf/main.cr +++ b/src/targets/x86_64/linux/elf/main.cr @@ -128,7 +128,9 @@ fn corearch_main() -> int: // The loader produces semantic objects only. Rebuild the linear instruction // stream before elf_gen; this entry is one of the consumers of // build_linear_schedule. - build_linear_schedule() + if build_linear_schedule() != 0: + println("error: invalid .ccr file") + return 1 emit_so := cli_has("shared") link_val := cli_get("link") diff --git a/tests/selfhost/test_backend_bootstrap.py b/tests/selfhost/test_backend_bootstrap.py index bcba0ba7..570d19e8 100644 --- a/tests/selfhost/test_backend_bootstrap.py +++ b/tests/selfhost/test_backend_bootstrap.py @@ -92,13 +92,13 @@ def main() -> int: print("[FAIL] stage1 backend or CCR was not created") return 1 if not run_checked( - [stage1, stage1_ccr, "--static", "-o", stage2], + [stage1, stage1_ccr, "--static", "--raw-int", "-o", stage2], "stage1 emits corearch stage2", env, ): return 1 if not run_checked( - [stage2, stage1_ccr, "--static", "-o", stage3], + [stage2, stage1_ccr, "--static", "--raw-int", "-o", stage3], "stage2 emits corearch stage3", env, ): diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 83f99c1e..62641009 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""R2 P4 Task 1/2/3:`.ccr` TYPE=7 / IFACE=8 段(机制面 + TYPE/IFACE 内容面)断言套件。 +"""R2 P4 Task 1/2/3 + v10 cutover:`.ccr` TYPE=2 / IFACE=3 段(机制面 + 内容面)断言套件。 本套件 = **机制面**(Task 1:段表/版本/loader 三闸)+ **TYPE 内容面**(Task 2: 行表 + 项 DAG 序列化 / D13 确定性装填 / corearch 读回重建 / 判定原语跨进程同值) @@ -8,15 +8,15 @@ Task 1(机制面): ① 段表 = 9 段、tag 序 = 1..9、offset 连续、末段尾 == 文件大小; - ② header: version = 9、seg_count = 8、reserved = 0; - ③ TYPE(7)/IFACE(8) **皆为内容面**(R2 P4 Task 2/3 起——空壳期已退役); + ② header: version = 0x000A0000、seg_count = 9、flags bits 0..2; + ③ TYPE(2)/IFACE(3) **皆为内容面**(R2 P4 Task 2/3 起——空壳期已退役); ④ loader 负分支(byte mutation,逐个打;全部要求 corearch rc≠0 且**不得 静默当空表**——三态纪律 C.5-3): - · version := 7/8 → 拒(D10:旧 v7/v8 文件在版本闸整类拒收); + · version := 7/8/589825 → 拒(v10 版本闸整类拒收); · **结构性合法的 v7(6 段)文件整体重建** → 拒(D10 的正面证据: 非「字节破烂」被拒,而是合法旧文件被版本闸拒); · seg_cnt := 6(末段 size 扩到 EOF——前闸全过)→ 拒(D11 必备集); - · 段 tag ∉ 1..8(tag := 9)/ 重复 tag(7 → 6)→ 拒(闸界 + 规范序); + · 段 tag ∉ 1..9(tag := 10)/ 重复 tag(2 → 1)→ 拒(闸界 + 规范序); · 段体截断(文件 −1B)→ 拒(越界)。 ⑤ 端到端:corec build --static + corearch --elf 双路径 rc=0 + 产物 rc=42 + 产物 .ccr 的 TYPE/IFACE 段非空(内容面入真实构建路径); diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 7a0d4fb3..731055d6 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -381,10 +381,10 @@ def _run(cmd, **kwargs): ) -def _build_v10(tmp: Path): - src = tmp / "e2e.cr" - ccr = tmp / "e2e.ccr" - src.write_text(E2E_SOURCE, encoding="utf-8") +def _build_v10(tmp: Path, source: str = E2E_SOURCE, name: str = "e2e"): + src = tmp / f"{name}.cr" + ccr = tmp / f"{name}.ccr" + src.write_text(source, encoding="utf-8") r = _run([COREC, "ccr", src, "-o", ccr]) assert r.returncode == 0, f"corec ccr failed: {r.stdout}\n{r.stderr}" return ccr.read_bytes() @@ -488,6 +488,145 @@ def test_corearch_accepts_v10_and_rejects_v9(): assert r.returncode != 0, "corearch accepted seg_count 8" +def _append_to_segment(data, tag, extra): + table = read_segment_table(data) + idx = tag - 1 + _tag, off, size = table[idx] + d = bytearray(data) + d[off + size:off + size] = extra + out = bytes(d) + out = patch_segment_entry(out, idx, size=size + len(extra)) + for j in range(idx + 1, SEG_COUNT): + old_offset = table[j][1] + out = patch_segment_entry(out, j, offset=old_offset + len(extra)) + return out + + +def _segment_offset(data, tag): + for t, off, _size in read_segment_table(data): + if t == tag: + return off + raise AssertionError(f"tag {tag} missing") + + +PORTABLE_SOURCE = """fn main() -> int: + return 42 +""" + +SYSTEM_SOURCE = """extern fn putchar(c: int) -> int +fn main() -> int: + return putchar(65) +""" + +RUNTIME_BOUND_SOURCE = """fn main() -> int: + a := [1, 2, 3, 4] + i := 2 + p := &a[i] + return *p +""" + + +def test_header_flags_portable_user_program(): + with tempfile.TemporaryDirectory() as td: + data = _build_v10(Path(td), PORTABLE_SOURCE, "portable") + f = flags(data) + assert f & ~0x7 == 0, f"reserved header bits set: {f:#x}" + assert f & 2 == 0, f"portable user program marked SYSTEM: {f:#x}" + assert f in (0, 1), f"unexpected portable flags {f:#x}" + + +def test_header_flags_system_artifact(): + with tempfile.TemporaryDirectory() as td: + data = _build_v10(Path(td), SYSTEM_SOURCE, "system") + f = flags(data) + assert f & 2 != 0, f"extern/ABI program missing SYSTEM: {f:#x}" + assert f & 1 == 0, f"SYSTEM program also claims PORTABLE: {f:#x}" + + +def test_portable_system_flags_rejected(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_v10(tmp, PORTABLE_SOURCE, "flagbad") + bad = tmp / "flagbad.ccr" + bad.write_bytes(patch_u32(data, 12, 1 | 2)) + r = _run([COREARCH, bad, "--elf", "--static", "-o", tmp / "flagbad.elf"]) + assert r.returncode != 0, "PORTABLE+SYSTEM header accepted" + assert "invalid" in (r.stdout + r.stderr) + + +def _first_node_offset(data, opcode, predicate=None): + body = segment(data, 5) + (count,) = struct.unpack_from(" Date: Sat, 24 Oct 2026 01:16:03 +0800 Subject: [PATCH 47/58] ccr: move layout out of frontend into target projection --- build_selfhost_native.py | 5 + src/arch/x86_64/x86_mapping.cr | 13 +++ src/compiler/interp.cr | 12 +- src/compiler/ir_gen.cr | 60 +++++----- src/compiler/provenance_verify.cr | 110 +++++++++++++----- src/compiler/ptr_analysis.cr | 23 ++-- .../x86_64/linux/elf/frontend_target.cr | 15 +++ tests/selfhost/test_ccr_types.py | 24 ++-- tests/selfhost/test_ccr_v10_format.py | 78 ++++++++++++- tests/selfhost/test_compile.py | 6 + 10 files changed, 262 insertions(+), 84 deletions(-) diff --git a/build_selfhost_native.py b/build_selfhost_native.py index 7112dca4..7fdbb8c1 100644 --- a/build_selfhost_native.py +++ b/build_selfhost_native.py @@ -450,6 +450,11 @@ def main(): 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', + # The shared target descriptor (targets/.../frontend_target.cr) answers + # @sizeOf/@alignOf/@field through the target mapping, so the LSP links + # the same target-neutral interface + x86 implementation as corec. + 'src/compiler/mapping.cr', + 'src/arch/x86_64/x86_mapping.cr', 'src/compiler/prelude.cr', 'src/arch/x86_64/x86_frontend_abi.cr', 'src/os/linux/linux_frontend_abi.cr', diff --git a/src/arch/x86_64/x86_mapping.cr b/src/arch/x86_64/x86_mapping.cr index 82c0404f..789672b1 100644 --- a/src/arch/x86_64/x86_mapping.cr +++ b/src/arch/x86_64/x86_mapping.cr @@ -103,3 +103,16 @@ fn x86_mp_offset(index: int, ti: int) -> int: // Byte offset of a struct field. fn x86_mp_field_offset(field_index: int) -> int: return field_index * X86_MP_WORD_BYTES + +// ── Interpreter layout hooks ─────────────────────────────────────────────── +// `corec run` executes IR directly, without the projection pass corearch runs +// after loading a .ccr. The interpreter therefore asks the target for its +// element scale through these hooks instead of naming a layout interface or +// embedding an x86 constant. A different target replaces this file; interp.cr +// does not change. +fn interp_ptr_offset(index: int) -> int: + return mp_offset(index, TI_INT) + +// Element stride used when turning a pointer difference back into a count. +fn interp_ptr_stride() -> int: + return mp_stride(TI_INT) diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index 3055b13c..ca3c9ad1 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -83,11 +83,11 @@ fn ir_interp_binary_raw(d: int, s1: int, s2: int, s3: int): else if s3 == 13: cell_store(g_ir_vals, d * 8, lv != 0 || rv != 0) else if s3 == 17: - cell_store(g_ir_vals, d * 8, lv + mp_offset(rv, TI_INT)) + cell_store(g_ir_vals, d * 8, lv + interp_ptr_offset(rv)) else if s3 == 18: - cell_store(g_ir_vals, d * 8, lv - mp_offset(rv, TI_INT)) + cell_store(g_ir_vals, d * 8, lv - interp_ptr_offset(rv)) else if s3 == 19: - cell_store(g_ir_vals, d * 8, (lv - rv) / mp_stride(TI_INT)) + cell_store(g_ir_vals, d * 8, (lv - rv) / interp_ptr_stride()) // Shared IR_BINARY dispatch for the main loop and callee loops. // g_ir_vals int slots hold tagged mathematical integers. Char/bool/dex and @@ -106,11 +106,11 @@ fn ir_interp_binary(d: int, s1: int, s2: int, s3: int, ti: int): // Pointer add/sub/diff: the pointer is raw; the offset is mathematical. off := ir_interp_word(rv) if s3 == 17: - cell_store(g_ir_vals, d * 8, lv + mp_offset(off, TI_INT)) + cell_store(g_ir_vals, d * 8, lv + interp_ptr_offset(off)) else if s3 == 18: - cell_store(g_ir_vals, d * 8, lv - mp_offset(off, TI_INT)) + cell_store(g_ir_vals, d * 8, lv - interp_ptr_offset(off)) else: - cell_store(g_ir_vals, d * 8, ir_interp_tag((lv - rv) / mp_stride(TI_INT))) + cell_store(g_ir_vals, d * 8, ir_interp_tag((lv - rv) / interp_ptr_stride())) return if s3 == 12 || s3 == 13: // Boolean and/or store raw 0/1. diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index fc7920cc..152f030a 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -1084,9 +1084,9 @@ fn ti_from_type_expr(node: int) -> int: // Complex type expressions: delegate to checker's resolver return res_type_node(node) -// Type size in bytes. The classic mapping lives in mapping.cr. -fn type_size(ti: int) -> int: - return mp_size(ti) +// The frontend carries the pointee *type*, never a byte width. corearch +// reconstructs the access width for IR_DEREF/IR_STORE_PTR from this type and +// the active target layout. fn ptr_pointee_type(var_idx: int) -> int: if var_idx < 0: @@ -1098,16 +1098,6 @@ fn ptr_pointee_type(var_idx: int) -> int: return get_type_data(ti) return TI_INT -fn ptr_access_width(var_idx: int) -> int: - width := type_size(ptr_pointee_type(var_idx)) - if width <= 0: - return mp_stride(TI_INT) - return width - -// Type alignment in bytes. The classic mapping lives in mapping.cr. -fn type_align(ti: int) -> int: - return mp_align(ti) - // Resolve a type index to a struct info index. // Returns -1 if the type is not a struct type. fn ti_resolve_struct(ti: int) -> int: @@ -1148,8 +1138,10 @@ fn ti_has_field(ti: int, name: string) -> int: fi = fi + 1 return 0 -// Get field byte offset. The slot stride comes from mapping.cr. -fn ti_field_offset(ti: int, name: string) -> int: +// Get the field index of `name` in type `ti`. -1 = no such field. +// @field byte offsets are a target introspection query and are resolved by the +// target descriptor at the call site; this helper stays semantic. +fn ti_field_index(ti: int, name: string) -> int: si := ti_resolve_struct(ti) if si < 0: return -1 @@ -1161,7 +1153,7 @@ fn ti_field_offset(ti: int, name: string) -> int: fn_ni := si_field_name(si, fi) fname := istr_get(fn_ni) if str_eq(fname, name) != 0: - return mp_field_offset(fi) + return fi fi = fi + 1 return -1 @@ -1859,7 +1851,7 @@ fn gen_expr(node: int) -> int: val_var = dex_slot_norm(val_var) // apx: pointee slots are always exact; *p = d must not be approximated. if g_optrep_on != 0 && ti_is_optional(ptr_pointee_type(ptr_var)) != 0: val_var = box_for_slot_flag(val_node, val_var, 1) - emit(IR_STORE_PTR, -1, ptr_var, val_var, 0, ptr_access_width(ptr_var)) + emit(IR_STORE_PTR, -1, ptr_var, val_var, 0, 0) return val_var return val_var @@ -1934,7 +1926,7 @@ fn gen_expr(node: int) -> int: inner_var := gen_expr(ast_a(node)) inner_var = force_if_thunk(inner_var) dv := new_ir_var("deref", ptr_pointee_type(inner_var)) - emit(IR_DEREF, dv, inner_var, -1, 0, ptr_access_width(inner_var)) + emit(IR_DEREF, dv, inner_var, -1, 0, 0) return dv op_var := gen_expr(ast_a(node)) op_var = force_if_thunk(op_var) @@ -2044,18 +2036,20 @@ fn gen_expr(node: int) -> int: name_ni := ast_a(func_node) name := istr_get(name_ni) - // @sizeOf(T): emit IR_CONST with type size + // @sizeOf(T): emit IR_CONST with the target's size for T. Source-level + // layout introspection is a program request, so the target descriptor + // answers it; no IR width or stride depends on this value. if str_eq(name, "sizeOf") != 0: ti := ti_from_type_expr(ast_a(first_arg)) - sz := type_size(ti) + sz := target_introspect_size(ti) v := new_ir_var("_sizeof", TI_INT) emit(IR_CONST, v, sz, 0, 0, TI_INT) return v - // @alignOf(T): emit IR_CONST with type alignment + // @alignOf(T): emit IR_CONST with the target's alignment for T. if str_eq(name, "alignOf") != 0: ti := ti_from_type_expr(ast_a(first_arg)) - al := type_align(ti) + al := target_introspect_align(ti) v := new_ir_var("_alignof", TI_INT) emit(IR_CONST, v, al, 0, 0, TI_INT) return v @@ -2096,13 +2090,17 @@ fn gen_expr(node: int) -> int: emit(IR_CONST, v, exists, 0, 0, TI_BOOL) return v - // @field(T, name): get field offset + // @field(T, name): target byte offset of a field. The field index is + // semantic; the target descriptor maps it to the target layout. if str_eq(name, "field") != 0: ti := ti_from_type_expr(ast_a(first_arg)) name_arg := ast_b(first_arg) name_expr := ast_a(name_arg) fn_name := istr_get(ast_int_val(name_expr)) - off := ti_field_offset(ti, fn_name) + fi := ti_field_index(ti, fn_name) + off : ., mut = -1 + if fi >= 0: + off = target_introspect_field_offset(fi) v := new_ir_var("_fldoff", TI_INT) emit(IR_CONST, v, off, 0, 0, TI_INT) return v @@ -2898,7 +2896,9 @@ fn gen_expr(node: int) -> int: if ast_kind(type_node) == 19: sz := ast_int_val(type_node) if sz > 0: - emit(IR_ALLOC_ARRAY, var, sz, mp_stride(TI_INT), 0, 0) + // Array stride is a target fact; corearch reconstructs it from + // the allocation count. src2/src3 stay zero on the wire. + emit(IR_ALLOC_ARRAY, var, sz, 0, 0, 0) is_arr = 1 // F1:记录数组类型(TYP_ARRAY extra=size),直接索引越界 // 检查(arr_len_lit_of)依赖该长度信息。 @@ -3024,11 +3024,12 @@ fn gen_expr(node: int) -> int: // 只对 `TYP_REF`:`PTR` 不解,与 checker 一致(`p as *S` followed by `p.v` is rejected)。 // `&mut x` 同为 `TYP_REF`(仅 `extra` = mut 标记不同)-> 本判定按 kind、不看 extra -> 一并覆盖。 // - // 发射序逐字镜像 UOP_DEREF 分支(IR_DEREF + ptr_pointee_type + - // ptr_access_width)。该序已被 (*p).v(value 42)证明正确,不另创新序。 + // 发射序逐字镜像 UOP_DEREF 分支(IR_DEREF + ptr_pointee_type)。 + // 该序已被 (*p).v(value 42)证明正确,不另创新序。宽度由 corearch 从 + // pointee 语义类型重建,前端不再携带。 if get_type_kind(irv_type(obj_var)) == TYP_REF: dv := new_ir_var("autoderef", ptr_pointee_type(obj_var)) - emit(IR_DEREF, dv, obj_var, -1, 0, ptr_access_width(obj_var)) + emit(IR_DEREF, dv, obj_var, -1, 0, 0) obj_var = dv v := new_ir_var("field", TI_INT) fi : ., mut = ast_type_val(node) @@ -3323,7 +3324,8 @@ fn gen_expr(node: int) -> int: elem_idx := ast_a(node) ec : ., mut = ast_b(node) tv := new_ir_var("tuple", TI_INT) - emit(IR_ALLOC_ARRAY, tv, ec, 0, mp_stride(TI_INT), 0) // target slot stride + // Element stride is a target fact; corearch reconstructs it. + emit(IR_ALLOC_ARRAY, tv, ec, 0, 0, 0) // Store each element at its offset e : ., mut = 0 loop: diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index 52c4a66d..2b90627f 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -1,9 +1,17 @@ // === provenance_verify.cr === -// ProvenanceVerify pass — checks DEREF offset against allocation size. -// Runs after PointerAnalysis (ptr_analysis.cr) and RegionCheck (region_check.cr). -// Detects out-of-bounds pointer accesses at compile time. +// ProvenanceVerify pass — checks the DEREF/STORE_PTR element interval against +// the allocation extent. Runs after PointerAnalysis (ptr_analysis.cr) and +// RegionCheck (region_check.cr). Detects out-of-bounds pointer accesses at +// compile time. +// +// Units are IR cells (element indices), never target bytes. PointerAnalysis +// stores element offsets and this pass derives the allocation extent from the +// ALLOC node's semantic count. corearch computes the matching byte extent from +// the same node plus the target layout, so this decision is target-independent. -fn get_alloc_size(alloc_seq: int) -> int: +// Semantic extent of an allocation in IR cells. +// -1 = unknown; leave the check to run time. Never approximate unknown as 0. +fn get_alloc_extent(alloc_seq: int) -> int: // alloc_seq is a pts allocation bit, not a DF node index. Map it through // g_pa_alloc_nodes before reading the allocation node. if alloc_seq < 0 || alloc_seq >= g_pa_alloc_count: @@ -14,12 +22,13 @@ fn get_alloc_size(alloc_seq: int) -> int: s3 := cell_load(g_df_nodes, an * ESZ_DFNODE + OFF_DF_S3) // IR_ALLOC marks a scalar variable slot, not a heap allocation, so it has no - // allocation size. + // allocation extent. if op == IR_ALLOC_ARRAY: - return mp_extent(s1, TI_INT) - // count * slot stride; elements are always one slot in IR_ALLOC_ARRAY. + // s1 is the semantic element count; one element occupies one IR cell. + return s1 if op == IR_ALLOC_STRUCT: - // Match IR_ALLOC_STRUCT: field count times the slot stride. + // Field count is the struct's cell extent. An unknown struct name falls + // back to a single cell, matching the scalar placeholder in the IR. fi : int = -1 si2 : ., mut = 0 loop: @@ -30,10 +39,57 @@ fn get_alloc_size(alloc_seq: int) -> int: break si2 = si2 + 1 if fi >= 0: - return mp_extent(si_field_count(fi), TI_INT) - return mp_stride(TI_INT) + return si_field_count(fi) + return 1 return -1 // unknown; leave the check to run time +// Semantic cell span of one value of type `ti`. Arrays and tuples are +// contiguous element sequences, structs cover their fields, a slice view is a +// (data, len) pair, and every scalar occupies one cell. This is the IR value +// model, not a target byte size. +fn pv_value_span(ti: int) -> int: + if ti < 0 || ti >= g_type_count: + return 1 + k := get_type_kind(ti) + if k == TYP_ARRAY: + elem := pv_value_span(get_type_data(ti)) + cnt := get_type_extra(ti) + if cnt <= 0: + return 1 + return elem * cnt + if k == TYP_NAMED: + name_ni := get_type_data(ti) + si : ., mut = 0 + loop: + if si >= g_struct_count: + break + if si_name(si) == name_ni: + fc := si_field_count(si) + if fc > 0: + return fc + return 1 + si = si + 1 + return 1 + if k == TYP_TUPLE: + n := get_type_data(ti) + if n > 0: + return n + return 1 + if k == TYP_SLICE: + return 2 + return 1 + +// Cell span of the value a dereference reads or writes, derived from the +// pointer's pointee type. The frontend no longer stores an access width code. +fn pv_access_span(ptr_var: int) -> int: + ti := irv_type(ptr_var) + if ti < 0: + return 1 + k := get_type_kind(ti) + if k == TYP_PTR || k == TYP_REF: + return pv_value_span(get_type_data(ti)) + return 1 + fn get_alloc_var(alloc_seq: int) -> int: if alloc_seq < 0 || alloc_seq >= g_pa_alloc_count: return -1 @@ -61,10 +117,6 @@ fn provenance_verify_func(nstart: int, ncount: int): break op := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_OPCODE) s1 := cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S1) - // Read the access width in bytes from OFF_DF_AUX. Before the single-slot - // change it lived in the shared tk slot. Reading slot 40 would treat a - // type-term index as a width. - width : ., mut = cell_load(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_AUX) if (op == IR_DEREF || op == IR_STORE_PTR) && s1 >= 0: // Skip checks in unsafe blocks @@ -72,6 +124,9 @@ fn provenance_verify_func(nstart: int, ncount: int): ni = ni + 1 continue + // Access span in IR cells, from the pointee semantic type. + span := pv_access_span(s1) + ptr_ti := irv_type(s1) // This check applies only to TYP_PTR values with a non-zero extra field, // which marks an external address space. It does not apply to TYP_REF: @@ -86,9 +141,6 @@ fn provenance_verify_func(nstart: int, ncount: int): ni = ni + 1 continue - if width <= 0: - width = 8 - pts := cell_load(g_pts, s1 * 8) off := cell_load(g_offsets, s1 * 8) runtime_targets : ., mut = 0 @@ -102,23 +154,25 @@ fn provenance_verify_func(nstart: int, ncount: int): if bi >= 64: break if (pts / mask) % 2 != 0: - alloc_size := get_alloc_size(bi) - if alloc_size >= 0 && off >= 0: - if width > alloc_size || off > alloc_size - width: + alloc_extent := get_alloc_extent(bi) + if alloc_extent >= 0 && off >= 0: + if span > alloc_extent || off > alloc_extent - span: check_error(EC_TK_INDEX, - "pointer out of bounds: offset " + int_str(off) + - " + width " + int_str(width) + - " > size " + int_str(alloc_size), + "pointer out of bounds: index " + int_str(off) + + " + span " + int_str(span) + + " > extent " + int_str(alloc_extent) + + " (IR cells)", 0, 0) - else if alloc_size >= 0: - if width > alloc_size: + else if alloc_extent >= 0: + if span > alloc_extent: check_error(EC_TK_INDEX, - "pointer access width " + int_str(width) + - " exceeds allocation size " + int_str(alloc_size), + "pointer access span " + int_str(span) + + " exceeds allocation extent " + int_str(alloc_extent) + + " (IR cells)", 0, 0) else: runtime_targets = runtime_targets + 1 - runtime_size = alloc_size + runtime_size = alloc_extent runtime_base = get_alloc_var(bi) // Unknown allocation information leaves s2 and s3 unset. The // encoder keeps a null trap for s3 = 0. diff --git a/src/compiler/ptr_analysis.cr b/src/compiler/ptr_analysis.cr index 89db534c..ca30a155 100644 --- a/src/compiler/ptr_analysis.cr +++ b/src/compiler/ptr_analysis.cr @@ -197,7 +197,8 @@ fn pa_store(ptr_var: int, val_var: int) -> int: return changed fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): - // Initialize points-to sets and offsets for this function's variables. + // Initialize points-to sets and semantic element offsets for this function's + // variables. Offsets are element indices, never target byte addresses. vi : ., mut = 0 loop: if vi >= vcount: @@ -260,9 +261,10 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if op == IR_ADDR_INDEX && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 - // The index decides the offset. A constant index gives - // index times the mapping stride; a dynamic index gives -1, - // which makes provenance_verify emit a runtime check. + // The index decides the semantic element offset. A constant + // index adds that many elements; a dynamic index gives -1, + // which makes provenance_verify emit a runtime check. Byte + // scaling is a target fact and belongs to corearch. base_off := cell_load(g_offsets, s1 * 8) idx_val : int = -1 if s2 >= 0: @@ -272,12 +274,13 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if prod_op == IR_CONST: idx_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if idx_val >= 0 && base_off >= 0: - cell_store(g_offsets, d * 8, base_off + mp_offset(idx_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off + idx_val) else: cell_store(g_offsets, d * 8, -1) // IR_SLICE propagates the underlying array provenance for - // d = &arr[low]. A constant low gives low times the mapping stride. + // d = &arr[low]. A constant low adds that many elements; the + // target scale is not part of the analysis. if op == IR_SLICE && s1 >= 0: if pa_merge_pts(d, s1) != 0: changed = 1 @@ -290,7 +293,7 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): if prod_op == IR_CONST: low_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) if low_val >= 0 && base_off >= 0: - cell_store(g_offsets, d * 8, base_off + mp_offset(low_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off + low_val) else: cell_store(g_offsets, d * 8, -1) @@ -308,10 +311,12 @@ fn ptr_analysis_func(nstart: int, ncount: int, vstart: int, vcount: int): prod_op := cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_OPCODE) if prod_op == IR_CONST: off_val = cell_load(g_df_nodes, prod * ESZ_DFNODE + OFF_DF_S1) + // Pointer add and subtract propagate provenance and the + // semantic element offset. Constant offsets are exact. if s3 == OP_PTR_ADD: - cell_store(g_offsets, d * 8, base_off + mp_offset(off_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off + off_val) else: - cell_store(g_offsets, d * 8, base_off - mp_offset(off_val, TI_INT)) + cell_store(g_offsets, d * 8, base_off - off_val) // LOAD: copy provenance from s1 to d. if op == IR_LOAD && s1 >= 0: diff --git a/src/targets/x86_64/linux/elf/frontend_target.cr b/src/targets/x86_64/linux/elf/frontend_target.cr index ec9685ab..290abdd4 100644 --- a/src/targets/x86_64/linux/elf/frontend_target.cr +++ b/src/targets/x86_64/linux/elf/frontend_target.cr @@ -51,6 +51,21 @@ fn target_so_index_path(home_dir: string, fs_path: string) -> string: fn target_static_runtime_source() -> string: return "src/runtime/rt.cr" +// -- Compile-time target introspection ------------------------------------- +// @sizeOf / @alignOf / @field are source-level layout queries: the program asks +// the target for a value, and the frontend emits that value as a program +// constant. They are not frontend layout decisions — no IR opcode, access +// width, element stride, or allocation extent depends on them — so the answer +// lives with the target descriptor, not in the semantic frontend. +fn target_introspect_size(ti: int) -> int: + return mp_size(ti) + +fn target_introspect_align(ti: int) -> int: + return mp_align(ti) + +fn target_introspect_field_offset(field_index: int) -> int: + return mp_field_offset(field_index) + // Build the backend command for this target. The caller prefixes the sibling // corearch directory; the target owns the backend name and ABI flags. fn target_backend_command(ccr_path: string, out_path: string, is_static: int, opt_level: int) -> string: diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 62641009..ebfb6e9b 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -1564,11 +1564,13 @@ def cir_entry_nodes(path: str): def test_p4t4_tk_slot_rule_per_node(): - """㉖(P5 T2 重钉:双槽 → 单槽化)逐节点规则(探针夹具): + """㉖(P5 T2 重钉:双槽 → 单槽化;v10 Task 5:前端去布局)逐节点规则(探针夹具): · 类型行面(MINT_OPS)∧ 原子行 ⇒ term ≥ 0 ∧ aux == 0 ∧ 项 = ATOM(b == 行号); · 类型行面 ∧ 复合行/越界/无类型 ⇒ term == -1 ∧ aux == 原码(码保真); - · 辅码面 ⇒ term == -1 ∧ aux == 原码——**含数值陷阱**:DEREF/STORE_PTR 的宽度 8、 - BOUNDS_CHECK 的旗标 1(两者数值上恰是合法类型行,只能靠分类表挡住); + · 辅码面 ⇒ term == -1 ∧ aux == 原码——**含数值陷阱**:BOUNDS_CHECK 的旗标 1 + (数值上恰是合法类型行 TI_DEX,只能靠分类表挡住); + · IR_DEREF/IR_STORE_PTR 不再携带访问宽度(Task 5:宽度由 corearch 从 + pointee 语义类型 + target 重建)⇒ 其 tk == 0 ∧ aux == 0; · 其余 op ⇒ term == -1 ∧ aux == 原码; · **派生码不变量(逐节点)**:term ≥ 0 ⇒ tk == b;aux ≠ 0 ⇒ tk == aux;否则 tk == 0。 正控逐行类型面:int/bool/str/dex_s 四个类型行的 AK_* 与行号 b 槽。""" @@ -1585,7 +1587,7 @@ def test_p4t4_tk_slot_rule_per_node(): assert hdr['aux_nonzero'] > 0, "vacuous: no node carries an aux code" saw = {'const_rows': set(), 'binary_rows': set()} - traps = {'deref_w8': 0, 'store_w8': 0, 'bounds_flag1': 0, 'bounds_flag0': 0, + traps = {'deref_no_width': 0, 'store_no_width': 0, 'bounds_flag1': 0, 'bounds_flag0': 0, 'call_row': 0, 'spawn_dyn': 0} for (n, op, tk, term, tag, a, b, c, aux) in rows: # ── 派生码不变量(D22-①,逐节点)── @@ -1615,10 +1617,10 @@ def test_p4t4_tk_slot_rule_per_node(): assert term == -1, \ f"node {n}: non-mint op {op} got term {term}" assert aux == tk, f"node {n}: non-mint op {op} aux {aux} != code {tk}" - if op == IR_DEREF and tk == 8: - traps['deref_w8'] += 1 - if op == IR_STORE_PTR and tk == 8: - traps['store_w8'] += 1 + if op == IR_DEREF and tk == 0: + traps['deref_no_width'] += 1 + if op == IR_STORE_PTR and tk == 0: + traps['store_no_width'] += 1 if op == IR_BOUNDS_CHECK and tk == 1: traps['bounds_flag1'] += 1 if op == IR_BOUNDS_CHECK and tk == 0: @@ -1634,9 +1636,9 @@ def test_p4t4_tk_slot_rule_per_node(): assert (8, AK_DEX) in saw['const_rows'], \ f"dex_s row (8) not mapped to AK_DEX: {saw['const_rows']}" assert saw['binary_rows'], "no binary node carries a term" - # 陷阱非空:宽度 8 / 旗标 1 / 旗标 0 必须逐条在夹具里出现过(否则用例空转)。 - # IR_CALL 已入类型行面(P5 T2)⇒ 不再计入陷阱。 - assert traps['deref_w8'] >= 1 and traps['store_w8'] >= 1, traps + # 陷阱非空:BOUNDS_CHECK 旗标 1/0 与「指针 op 无可宽度」必须逐条在夹具里 + # 出现过(否则用例空转)。IR_CALL 已入类型行面(P5 T2)⇒ 不再计入陷阱。 + assert traps['deref_no_width'] >= 1 and traps['store_no_width'] >= 1, traps assert traps['bounds_flag1'] >= 1 and traps['bounds_flag0'] >= 1, traps finally: _cleanup(src) diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 731055d6..40d212ce 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -351,7 +351,83 @@ def test_schema_has_no_target_layout_words(): # --------------------------------------------------------------------------- -# 12. end-to-end: corec writes v10, corearch rejects v9 and accepts v10 +# 12. static: layout belongs to the target projection, not the frontend +# --------------------------------------------------------------------------- + +# The semantic frontend must not ask for target byte sizes, alignments, slot +# strides, field byte offsets, or allocation byte extents. corearch +# reconstructs those from the semantic type ids plus the active target layout, +# so the same source produces the same v10 bytes on every target. +REPO_ROOT = Path(__file__).resolve().parent.parent.parent +FRONTEND_LAYOUT_FREE = ( + "src/compiler/ir_gen.cr", + "src/compiler/ptr_analysis.cr", + "src/compiler/provenance_verify.cr", + "src/compiler/ccr_lower.cr", +) +FRONTEND_LAYOUT_CALLS = ( + "mp_size", + "mp_align", + "mp_stride", + "mp_field_offset", + "mp_extent", + "mp_offset", +) +# Same target facts under new names are still target facts. The frontend must +# not grow a private copy of the target layout. +FRONTEND_TARGET_FACT_NAMES = ("word_bytes", "slot_size", "offset_bytes", "byte_offset") + + +def test_frontend_has_no_layout_calls(): + for path in FRONTEND_LAYOUT_FREE: + src = (REPO_ROOT / path).read_text(encoding="utf-8") + for name in FRONTEND_LAYOUT_CALLS + FRONTEND_TARGET_FACT_NAMES: + assert name not in src, f"{path} still calls {name}" + + +def test_layout_introspection_is_a_target_query(): + """@sizeOf/@alignOf/@field ask the target descriptor; the compiler does not + evaluate layout itself. Their results are program constants, so the same + source keeps byte-identical v10 output.""" + ir_gen = (REPO_ROOT / "src/compiler/ir_gen.cr").read_text(encoding="utf-8") + for name in ( + "target_introspect_size(", + "target_introspect_align(", + "target_introspect_field_offset(", + ): + assert name in ir_gen, f"ir_gen must ask the target descriptor for {name}" + + desc = (REPO_ROOT / "src/targets/x86_64/linux/elf/frontend_target.cr").read_text( + encoding="utf-8" + ) + for name in ( + "fn target_introspect_size(", + "fn target_introspect_align(", + "fn target_introspect_field_offset(", + ): + assert name in desc, f"target descriptor missing {name}" + + +def test_interp_has_no_direct_layout_calls(): + """`corec run` executes IR without a projection pass, so it asks a + target-owned hook for the element scale. It must not reach into the + compiler mapping interface or inline the x86 scale itself.""" + src = (REPO_ROOT / "src/compiler/interp.cr").read_text(encoding="utf-8") + for name in ("mp_offset", "mp_stride"): + assert name not in src, f"src/compiler/interp.cr still calls {name}" + + hook_path = REPO_ROOT / "src/arch/x86_64/x86_mapping.cr" + hook = hook_path.read_text(encoding="utf-8") + assert "fn interp_ptr_offset(" in hook, "missing target-owned interp offset hook" + assert "fn interp_ptr_stride(" in hook, "missing target-owned interp stride hook" + # The hook is a target-owned delegation, never a hardcoded x86 scale. + hook_body = hook.split("fn interp_ptr_offset(", 1)[1] + assert "mp_offset(" in hook_body, "interp layout hook must delegate to the target" + assert "* 8" not in hook_body, "interp layout hook hardcodes the x86 scale" + + +# --------------------------------------------------------------------------- +# 13. end-to-end: corec writes v10, corearch rejects v9 and accepts v10 # --------------------------------------------------------------------------- CORE_BASE = Path(__file__).resolve().parent.parent.parent diff --git a/tests/selfhost/test_compile.py b/tests/selfhost/test_compile.py index 856a7b9e..f7624cae 100644 --- a/tests/selfhost/test_compile.py +++ b/tests/selfhost/test_compile.py @@ -20,6 +20,12 @@ def concat_sources(): 'src/compiler/ast.cr', 'src/compiler/globals.cr', 'src/compiler/dyn_arr.cr', 'src/compiler/lexer.cr', 'src/compiler/parser.cr', 'src/compiler/checker.cr', + # 目标描述 + 目标布局(Task 5:前端去布局)。与 build_selfhost_native.py 的 + # corec 清单同源:frontend_target.cr 的 @sizeOf/@alignOf/@field 应答经 + # mapping.cr + x86_mapping.cr 落地;interp 的指针标度钩子亦在此。 + 'src/compiler/mapping.cr', + 'src/arch/x86_64/x86_mapping.cr', + 'src/targets/x86_64/linux/elf/frontend_target.cr', # R2 P0 类型项引擎(建层,与 build_selfhost_native.py 的 corec 清单同位): # 前端单元新增三文件的源码闭包条目。 'src/compiler/type_terms.cr', 'src/compiler/type_engine.cr', From 079a1117a38ca7a97390bbcff40b21290ace237f Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 01:38:12 +0800 Subject: [PATCH 48/58] ccr: finish removing frontend layout from interp and allocation stride --- src/arch/x86_64/backend_flags.cr | 1 + src/arch/x86_64/corearch.cr | 43 ++++ src/arch/x86_64/instr.cr | 14 +- src/arch/x86_64/regalloc.cr | 22 +- src/arch/x86_64/x86_mapping.cr | 4 + src/ci/run.sh | 2 +- src/compiler/ast.cr | 6 +- src/compiler/interp.cr | 86 ++++--- src/compiler/mapping.cr | 6 +- src/compiler/provenance_verify.cr | 21 +- src/compiler/ty_shadow.cr | 14 +- src/compiler/type_selftest.cr | 8 +- .../x86_64/linux/elf/frontend_target.cr | 5 +- src/targets/x86_64/linux/elf/main.cr | 2 + tests/selfhost/test_ccr_types.py | 46 +++- tests/selfhost/test_ccr_v10_format.py | 239 +++++++++++++++--- 16 files changed, 415 insertions(+), 104 deletions(-) diff --git a/src/arch/x86_64/backend_flags.cr b/src/arch/x86_64/backend_flags.cr index 7ef9c05d..529f36d2 100644 --- a/src/arch/x86_64/backend_flags.cr +++ b/src/arch/x86_64/backend_flags.cr @@ -24,6 +24,7 @@ fn register_backend_flags(): cli_flag_bool_debug("inject-coexist-oob", "", "Debug hook: probe entries_coexist out of bounds") cli_flag_bool_debug("dump-objects", "", "Debug: dump NOD/EDG objects") cli_flag_bool_debug("dump-nod-types", "", "Debug: dump NOD type_id/flags") + cli_flag_bool_debug("dump-alloc-strides", "", "Debug: dump ALLOC_ARRAY element type/stride") cli_flag_bool_debug("dump-types", "", "Debug: dump TYPE segment") cli_flag_bool_debug("dump-ifaces", "", "Debug: dump IFACE segment") diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index fe4f9a17..8f67f960 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -220,6 +220,43 @@ fn regalloc_debug_dispatch() -> int: // ent_kernel.cr. Each node prints its semantic fields, adjacency, and outgoing // edges. Edge traversal uses nod_inputs. The line format is parsed by // tests/selfhost/test_ccr_v7.py:parse_object_dump. +// --dump-alloc-strides: expose the element-type-driven IR_ALLOC_ARRAY stride. +// Read-only test channel. It runs after the TYPE segment is rebuilt, before +// linear reconstruction, and returns without emitting. +fn dump_alloc_strides(): + print("alloc-strides: ") + print_i(g_v7_nod_count) + println("") + ni : ., mut = 0 + loop: + if ni >= g_v7_nod_count: + break + if nod_op(ni) == IR_ALLOC_ARRAY: + dv := nod_dest(ni) + ti : ., mut = -1 + if dv >= 0: + ti = irv_type(dv) + kind : ., mut = -1 + elem : ., mut = -1 + if ti >= 0 && ti < g_type_count: + kind = get_type_kind(ti) + if kind == TYP_ARRAY: + elem = get_type_data(ti) + print("alloc ") + print_i(ni) + print(" dest ") + print_i(dv) + print(" ti ") + print_i(ti) + print(" kind ") + print_i(kind) + print(" elem ") + print_i(elem) + print(" stride ") + print_i(corearch_nod_alloc_stride(dv)) + println("") + ni = ni + 1 + fn dump_object_surface(): print("objects: ") print_i(g_v7_nod_count) @@ -394,6 +431,12 @@ fn corearch_main() -> int: dump_nod_types() return 0 + // Hidden channel: element type and reconstructed stride for each + // IR_ALLOC_ARRAY node. It returns before linear reconstruction. + if cli_has("dump-alloc-strides") != 0: + dump_alloc_strides() + return 0 + // The loader produces semantic objects only. The instance rebuilds the linear // g_ir_instrs stream with build_linear_schedule after a successful load and // before dispatch or emission. ELF emission and the allocation dumps all read diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 917d70dc..72af4e0b 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -1428,7 +1428,14 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: if op == IR_ALLOC_ARRAY: do2 := g2_slot(d) - sz := s1 * 8 + // s2 is the element stride reconstructed by build_linear_schedule from + // the destination variable's array element type. A missing value is a + // schedule bug: fall back explicitly to one target slot, never an + // inlined x86 constant. + st := s2 + if st <= 0: + st = mp_stride(TI_INT) + sz := s1 * st if sz > 0: cp = cp + e2_movabs_rdi(buf, pos+cp, sz) grow_alloc_patch(g_x86_alloc_patch_count + 1) @@ -1439,8 +1446,9 @@ fn emit_instr(instr_idx: int, buf: string, pos: int) -> int: cp = cp + 5 // call placeholder cp = cp + e2_st(buf, pos+cp, 0, do2) else if s1 > 0: - // s1*8 在 64 位内溢出为负(s1 > 2^61),不报错也不发射; - // 按 OOM 处理(alloc(-1) -> null),后续访问由 null 陷阱捕获。 + // The byte size overflowed to a negative 64-bit value (s1 * stride + // > 2^63 - 1). Do not reject: allocate(-1) returns null and later + // accesses hit the null trap. cp = cp + e2_movabs_rdi(buf, pos+cp, -1) grow_alloc_patch(g_x86_alloc_patch_count + 1) cell_store(g_x86_alloc_patch_pos, g_x86_alloc_patch_count * 8, pos + cp) diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index 8b87d2a5..57029d99 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -917,8 +917,24 @@ fn corearch_nod_access_width(n: int) -> int: return mp_stride(TI_INT) return w -// Element stride for IR_ALLOC_ARRAY. IR values occupy one target slot. -fn corearch_nod_alloc_stride() -> int: +// Element stride for IR_ALLOC_ARRAY, derived from the destination variable's +// semantic array element type. An uninitialised array declaration +// (`a : [T; N]`) stores a TYP_ARRAY row in the variable table, so the element +// row drives the stride. Compiler-internal buffers (the go-collect array, range +// result arrays) have a scalar/unit row and no element type to read; for those +// the explicit single-slot fallback applies. An unreadable type row also falls +// back explicitly instead of assuming every element is an int slot. +fn corearch_nod_alloc_stride(dest: int) -> int: + if dest < 0: + return mp_stride(TI_INT) + ti := irv_type(dest) + if ti < 0 || ti >= g_type_count: + return mp_stride(TI_INT) + if get_type_kind(ti) == TYP_ARRAY: + elem := get_type_data(ti) + if elem >= 0 && elem < g_type_count: + return mp_stride(elem) + // No semantic array element type: one IR value occupies one target slot. return mp_stride(TI_INT) // Byte extent of the allocation a runtime pointer bound refers to. The base @@ -995,7 +1011,7 @@ fn build_linear_schedule() -> int: // for pointer stores/loads is derived from the allocation node; a set // flag with no resolvable allocation is a malformed file (fail closed). if op == IR_ALLOC_ARRAY: - iri_set_s2(ni, corearch_nod_alloc_stride()) + iri_set_s2(ni, corearch_nod_alloc_stride(nod_dest(ni))) else if op == IR_DEREF || op == IR_STORE_PTR: iri_set_tk(ni, corearch_nod_access_width(ni)) bsz := corearch_nod_runtime_bound_size(ni) diff --git a/src/arch/x86_64/x86_mapping.cr b/src/arch/x86_64/x86_mapping.cr index 789672b1..80b4d209 100644 --- a/src/arch/x86_64/x86_mapping.cr +++ b/src/arch/x86_64/x86_mapping.cr @@ -116,3 +116,7 @@ fn interp_ptr_offset(index: int) -> int: // Element stride used when turning a pointer difference back into a count. fn interp_ptr_stride() -> int: return mp_stride(TI_INT) + +// Field/payload stride for struct fields and enum payload slots. +fn interp_field_stride() -> int: + return mp_stride(TI_INT) diff --git a/src/ci/run.sh b/src/ci/run.sh index 4e1de414..3f788600 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -131,7 +131,7 @@ case "$CI_JOB_NAME" in # 见 main.cr 硬名单)。新批次新增判定面行为**必须**同时给这三类证据之一,不得回退到 # 「与旧版逐字节同」(TODO #2026-09-11-9 口径)。 python3 tests/selfhost/test_compile.py - python3 tests/selfhost/test_type_engine.py # 类型项引擎自测通道(selftest-types **416 例**——P5 收官 404 + R2 P6 Task 2 增 11(`ts_t2_neg_run`:¬ 面同类原子身份判据的邻域覆盖——同身份 / μ 字面 / ⊤ₖ 双向 / 跨枚举同名 / 同枚举异变体 / 令牌字面 / AK_NAMED / 空链与链元素域外 / 参数化面 / REF mut 标记面;三例 `t3.*_over_claim` 为**重钉**(0→1,不增减);P0..P6 T2 一路漏挂) + python3 tests/selfhost/test_type_engine.py # 类型项引擎自测通道(selftest-types **417 例**——P5 收官 404 + R2 P6 Task 2 增 11(`ts_t2_neg_run`:¬ 面同类原子身份判据的邻域覆盖——同身份 / μ 字面 / ⊤ₖ 双向 / 跨枚举同名 / 同枚举异变体 / 令牌字面 / AK_NAMED / 空链与链元素域外 / 参数化面 / REF mut 标记面;三例 `t3.*_over_claim` 为**重钉**(0→1,不增减);P0..P6 T2 一路漏挂)+ ccr v10 Task 5 复核 +1(`t4.tk_store_ptr_width8_rejected`:旧宽度码 8 在 IR_STORE_PTR 上仍归辅码面) python3 tests/selfhost/test_iface_ops.py # R2 P2b Task 4/5/6:接口查表接线(算术/逻辑/条件三门 + 索引兜底门 + TY→TI 单表合一;正控/负控/登记面/站点域 76 例 + R2 P6 Task 4a 增 2(方法调用站点 never 透传 + 泛型方法登记钉)= 78 例) python3 tests/selfhost/test_purity.py # 效应/纯度批判据(真纯度 13 例 + 链语义 4 例的自测通道驱动) python3 tests/selfhost/test_ccr_v7.py # 效应/纯度批 Task 3:.ccr 层三条边集语义断言(取代「与旧版逐字节同」) diff --git a/src/compiler/ast.cr b/src/compiler/ast.cr index 1eeabfd3..53884848 100644 --- a/src/compiler/ast.cr +++ b/src/compiler/ast.cr @@ -616,9 +616,9 @@ IR_LABEL : int = 21 IR_PHI : int = 22 IR_LOAD_ENUM_TAG : int = 23 IR_SLICE : int = 24 // dest=slice_var, s1=arr_var, s2=low_var, src3=high_var; create slice ptr from range -IR_DEREF : int = 25 // dest=loaded_val, s1=ptr, s2=runtime_base, s3=alloc_size, type_kind=width -IR_STORE_PTR : int = 26 // dest=runtime_base, s1=ptr, s2=value, s3=alloc_size, type_kind=width -IR_ADDR_INDEX : int = 31 // dest=addr, s1=arr_var, s2=index_var, s3=scale; compute &arr[index] without loading +IR_DEREF : int = 25 // dest=loaded_val, s1=ptr, s2=runtime_base, s3=runtime-bound marker; access width is target-owned +IR_STORE_PTR : int = 26 // dest=runtime_base, s1=ptr, s2=value, s3=runtime-bound marker; access width is target-owned +IR_ADDR_INDEX : int = 31 // dest=addr, s1=arr_var, s2=index_var, s3=scale (target-owned, zero on the wire); compute &arr[index] without loading IR_SPAWN : int = 27 // dest=result_var, s1=first_arg, s2=arg_count, s3=fn_name_ni, type_kind=spawn_count (-1=dynamic) IR_YIELD : int = 28 // s1=value_var; emit value from flow to consumer channel IR_AWAIT : int = 29 // dest=value_var, s1=future_var; block until future ready, get value diff --git a/src/compiler/interp.cr b/src/compiler/interp.cr index ca3c9ad1..acaaaf8b 100644 --- a/src/compiler/interp.cr +++ b/src/compiler/interp.cr @@ -453,12 +453,14 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: // must implement them too, or an aggregate global read inside a callee // would return 0 while the main path works. if op2 == 8 && d2 >= 0: - // IR_ALLOC_ARRAY + // IR_ALLOC_ARRAY. The element stride is a target fact; the wire no + // longer carries it, so an absent s2 asks the target hook. The + // trailing slot reserves the same target-sized header word. cnt2 := t1 esz2 := t2 if esz2 <= 0: - esz2 = 8 - need3 := cnt2 * esz2 + 8 + esz2 = interp_ptr_stride() + need3 := cnt2 * esz2 + interp_ptr_stride() bp2 := alloc(need3) vi3 : ., mut = 0 loop: @@ -475,7 +477,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: cell_store(g_ir_vals, d2 * 8, str_load8(av2, t3)) else: unsafe: - cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), t3 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), t3 * interp_ptr_stride())) if op2 == 14 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX: t1=arr_var, t2=val_var av2 := cell_load(g_ir_vals, t1 * 8) @@ -483,7 +485,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: cell_store_byte(istr_get(av2), t3, cell_load(g_ir_vals, t2 * 8)) else: unsafe: - cell_store(@str_of(av2), t3 * 8, cell_load(g_ir_vals, t2 * 8)) + cell_store(@str_of(av2), t3 * interp_ptr_stride(), cell_load(g_ir_vals, t2 * 8)) if op2 == 15 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_LOAD_INDEX_VAR: t1=arr_var, t2=idx_var av2 := cell_load(g_ir_vals, t1 * 8) @@ -492,7 +494,7 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: cell_store(g_ir_vals, d2 * 8, str_load8(av2, ix2)) else: unsafe: - cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), ix2 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(av2), ix2 * interp_ptr_stride())) if op2 == 16 && d2 >= 0 && t1 >= 0 && t2 >= 0: // IR_STORE_INDEX_VAR: d2=val_var, t1=arr_var, t2=idx_var av2 := cell_load(g_ir_vals, t1 * 8) @@ -501,16 +503,17 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: cell_store_byte(istr_get(av2), ix2, cell_load(g_ir_vals, d2 * 8)) else: unsafe: - cell_store(@str_of(av2), ix2 * 8, cell_load(g_ir_vals, d2 * 8)) + cell_store(@str_of(av2), ix2 * interp_ptr_stride(), cell_load(g_ir_vals, d2 * 8)) if op2 == 31 && d2 >= 0 && t1 >= 0 && t2 >= 0: - // IR_ADDR_INDEX: &arr[i] - cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * 8) + // IR_ADDR_INDEX: &arr[i]. The element scale is a target fact. + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * interp_ptr_stride()) if op2 == 11 && d2 >= 0 && t1 >= 0: - // IR_LOAD_FIELD: t1=struct_var, t3=field_idx + // IR_LOAD_FIELD: t1=struct_var, t3=field_idx. Field offsets use the + // target field stride, not an inlined x86 constant. pf2 := cell_load(g_ir_vals, t1 * 8) if pf2 != 0: unsafe: - cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(pf2), t3 * 8)) + cell_store(g_ir_vals, d2 * 8, cell_load(@str_of(pf2), t3 * interp_field_stride())) else: cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) if op2 == 12 && t1 >= 0 && t2 >= 0: @@ -518,16 +521,17 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: pf2 := cell_load(g_ir_vals, t1 * 8) if pf2 != 0: unsafe: - cell_store(@str_of(pf2), t3 * 8, cell_load(g_ir_vals, t2 * 8)) + cell_store(@str_of(pf2), t3 * interp_field_stride(), cell_load(g_ir_vals, t2 * 8)) else: cell_store(g_ir_vals, t1 * 8, cell_load(g_ir_vals, t2 * 8)) if op2 == 7 && d2 >= 0: - // IR_ALLOC_STRUCT + // IR_ALLOC_STRUCT. Fields are target-stride spaced; the trailing + // word is the enum/tag header slot. si2 := find_struct(t3) fc2 : ., mut = 0 if si2 >= 0: fc2 = si_field_count(si2) - need3 := fc2 * 8 + 8 + need3 := fc2 * interp_field_stride() + interp_ptr_stride() bp2 := alloc(need3) vi3 : ., mut = 0 loop: @@ -545,12 +549,12 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if d2 >= 0 && t1 >= 0: cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8)) // The following opcode families mirror the main loop. - // IR_MAKE_ENUM: allocate 8 + 8*s2 bytes and store s1 (the variant-name - // index) in the first word. This matches the ELF layout [tag][payload]; - // IR_STORE_FIELD writes the payload. + // IR_MAKE_ENUM: allocate one tag slot plus s2 payload fields and store s1 + // (the variant-name index) in the first word. This matches the ELF layout + // [tag][payload]; IR_STORE_FIELD writes the payload. if op2 == 17: if d2 >= 0: - need3 := 8 + t2 * 8 + need3 := interp_ptr_stride() + t2 * interp_field_stride() bp2 := alloc(need3) vi3 : ., mut = 0 loop: @@ -576,10 +580,11 @@ fn ir_interp_run_fn(cfi: int, arg_base: int, argc: int) -> int: if op2 == 25: if d2 >= 0 && t1 >= 0: cell_store(g_ir_vals, d2 * 8, ir_interp_deref_read(cell_load(g_ir_vals, t1 * 8))) - // IR_SLICE: d = arr + low*8, matching the ELF encoding. + // IR_SLICE: d = arr + low elements, matching the ELF encoding. The + // element scale comes from the target hook. if op2 == 24: if d2 >= 0 && t1 >= 0 && t2 >= 0: - cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * 8) + cell_store(g_ir_vals, d2 * 8, cell_load(g_ir_vals, t1 * 8) + ir_interp_word(cell_load(g_ir_vals, t2 * 8)) * interp_ptr_stride()) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. A dynamic // limit uses ti = 1. An index below 0 or at least the limit aborts with -1. if op2 == 30 && t2 >= 0: @@ -838,13 +843,14 @@ fn ir_interpret() -> int: cell_store(g_ir_vals, d * 8, 0) // IR_ALLOC if op == 7: - // IR_ALLOC_STRUCT + // IR_ALLOC_STRUCT. Fields use the target field stride; the trailing + // word is the tag/header slot. if d >= 0: si := find_struct(s3) fc : ., mut = 0 if si >= 0: fc = si_field_count(si) - need2 := fc * 8 + 8 + need2 := fc * interp_field_stride() + interp_ptr_stride() bp := alloc(need2) vi2 : ., mut = 0 loop: @@ -855,13 +861,14 @@ fn ir_interpret() -> int: unsafe: cell_store(g_ir_vals, d * 8, @ptr_of(bp)) if op == 8: - // IR_ALLOC_ARRAY + // IR_ALLOC_ARRAY. s2 is zero on the wire; the element scale is + // asked from the target hook. if d >= 0: cnt := s1 esz := s2 if esz <= 0: - esz = 8 - need2 := cnt * esz + 8 + esz = interp_ptr_stride() + need2 := cnt * esz + interp_ptr_stride() bp := alloc(need2) vi2 : ., mut = 0 loop: @@ -888,7 +895,7 @@ fn ir_interpret() -> int: ptr := cell_load(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - cell_store(g_ir_vals, d * 8, cell_load(@str_of(ptr), s3 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(ptr), s3 * interp_field_stride())) else: cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8)) if op == 12: @@ -897,7 +904,7 @@ fn ir_interpret() -> int: ptr := cell_load(g_ir_vals, s1 * 8) if ptr != 0: unsafe: - cell_store(@str_of(ptr), s3 * 8, cell_load(g_ir_vals, s2 * 8)) + cell_store(@str_of(ptr), s3 * interp_field_stride(), cell_load(g_ir_vals, s2 * 8)) else: cell_store(g_ir_vals, s1 * 8, cell_load(g_ir_vals, s2 * 8)) if op == 13: @@ -908,7 +915,7 @@ fn ir_interpret() -> int: cell_store(g_ir_vals, d * 8, str_load8(arr_value, s3)) else: unsafe: - cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), s3 * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), s3 * interp_ptr_stride())) if op == 14: // IR_STORE_INDEX: s1=arr_var, s2=val_var, s3=literal_idx if s1 >= 0 && s2 >= 0: @@ -917,7 +924,7 @@ fn ir_interpret() -> int: cell_store_byte(istr_get(arr_value), s3, cell_load(g_ir_vals, s2 * 8)) else: unsafe: - cell_store(@str_of(arr_value), s3 * 8, cell_load(g_ir_vals, s2 * 8)) + cell_store(@str_of(arr_value), s3 * interp_ptr_stride(), cell_load(g_ir_vals, s2 * 8)) if op == 15: // IR_LOAD_INDEX_VAR: s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: @@ -927,7 +934,7 @@ fn ir_interpret() -> int: cell_store(g_ir_vals, d * 8, str_load8(arr_value, idx)) else: unsafe: - cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), idx * 8)) + cell_store(g_ir_vals, d * 8, cell_load(@str_of(arr_value), idx * interp_ptr_stride())) if op == 16: // IR_STORE_INDEX_VAR: d=val_var, s1=arr_var, s2=idx_var if d >= 0 && s1 >= 0 && s2 >= 0: @@ -937,13 +944,13 @@ fn ir_interpret() -> int: cell_store_byte(istr_get(arr_value), idx, cell_load(g_ir_vals, d * 8)) else: unsafe: - cell_store(@str_of(arr_value), idx * 8, cell_load(g_ir_vals, d * 8)) - // IR_MAKE_ENUM: allocate 8 + 8*s2 bytes and store s1 (the variant-name - // index) in the first word. This matches the ELF layout [tag][payload]; - // IR_STORE_FIELD writes the payload. + cell_store(@str_of(arr_value), idx * interp_ptr_stride(), cell_load(g_ir_vals, d * 8)) + // IR_MAKE_ENUM: allocate one tag slot plus s2 payload fields and store s1 + // (the variant-name index) in the first word. This matches the ELF layout + // [tag][payload]; IR_STORE_FIELD writes the payload. if op == 17: if d >= 0: - need2 := 8 + s2 * 8 + need2 := interp_ptr_stride() + s2 * interp_field_stride() bp := alloc(need2) vi2 : ., mut = 0 loop: @@ -970,10 +977,11 @@ fn ir_interpret() -> int: if d >= 0 && s1 >= 0: cell_store(g_ir_vals, d * 8, ir_interp_deref_read(cell_load(g_ir_vals, s1 * 8))) - // IR_SLICE: d = arr + low*8, matching the ELF encoding. + // IR_SLICE: d = arr + low elements, matching the ELF encoding. The + // element scale is a target fact. if op == 24: if d >= 0 && s1 >= 0 && s2 >= 0: - cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * 8) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * interp_ptr_stride()) // IR_BOUNDS_CHECK: s1 is the index variable and s2 the limit. An index // below 0 or at least the limit aborts with -1. @@ -1035,10 +1043,10 @@ fn ir_interpret() -> int: if s1 >= 0 && s2 >= 0: ir_interp_deref_write(cell_load(g_ir_vals, s1 * 8), cell_load(g_ir_vals, s2 * 8)) - // IR_ADDR_INDEX: d = slot s1 + 8 * slot s2 for &arr[i]. + // IR_ADDR_INDEX: d = slot s1 + stride * slot s2 for &arr[i]. if op == 31: if d >= 0 && s1 >= 0 && s2 >= 0: - cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * 8) + cell_store(g_ir_vals, d * 8, cell_load(g_ir_vals, s1 * 8) + ir_interp_word(cell_load(g_ir_vals, s2 * 8)) * interp_ptr_stride()) // IR_ARENA_NEW and IR_ARENA_RESET: the slot model has no scoped arena, so // these are approximations. if op == 32: diff --git a/src/compiler/mapping.cr b/src/compiler/mapping.cr index 1d1352ac..94fb01d6 100644 --- a/src/compiler/mapping.cr +++ b/src/compiler/mapping.cr @@ -1,8 +1,10 @@ // === mapping.cr === // Target-neutral mapping interface for value extents and slot strides. // The classic x86-64 implementation lives in -// src/arch/x86_64/x86_mapping.cr. The front end must not read target -// layout constants directly; it asks through these functions. +// src/arch/x86_64/x86_mapping.cr. This is the projection-side interface: +// corearch and the target descriptor call it. The semantic frontend +// (ir_gen / ptr_analysis / provenance_verify / ccr_lower) must not call it and +// does not carry widths, strides, or byte extents in the v10 wire. fn mp_size(ti: int) -> int: return x86_mp_size(ti) diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index 2b90627f..4625f861 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -47,12 +47,17 @@ fn get_alloc_extent(alloc_seq: int) -> int: // contiguous element sequences, structs cover their fields, a slice view is a // (data, len) pair, and every scalar occupies one cell. This is the IR value // model, not a target byte size. +// -1 = the type row is unavailable (negative or outside the loaded TYPE table, +// e.g. a warm cache that does not carry it). Unknown must stay unknown so the +// caller keeps the runtime bounds check instead of assuming one cell. fn pv_value_span(ti: int) -> int: if ti < 0 || ti >= g_type_count: - return 1 + return -1 k := get_type_kind(ti) if k == TYP_ARRAY: elem := pv_value_span(get_type_data(ti)) + if elem < 0: + return -1 cnt := get_type_extra(ti) if cnt <= 0: return 1 @@ -81,10 +86,13 @@ fn pv_value_span(ti: int) -> int: // Cell span of the value a dereference reads or writes, derived from the // pointer's pointee type. The frontend no longer stores an access width code. +// -1 = unknown: the pointer variable has no usable type row (negative or +// outside the loaded TYPE table) or its pointee row is unavailable. Callers +// then fall back to the runtime bounds check (fail closed, never widen). fn pv_access_span(ptr_var: int) -> int: ti := irv_type(ptr_var) - if ti < 0: - return 1 + if ti < 0 || ti >= g_type_count: + return -1 k := get_type_kind(ti) if k == TYP_PTR || k == TYP_REF: return pv_value_span(get_type_data(ti)) @@ -124,7 +132,8 @@ fn provenance_verify_func(nstart: int, ncount: int): ni = ni + 1 continue - // Access span in IR cells, from the pointee semantic type. + // Access span in IR cells, from the pointee semantic type. -1 means + // the type row is unavailable: keep the runtime bounds check. span := pv_access_span(s1) ptr_ti := irv_type(s1) @@ -155,7 +164,7 @@ fn provenance_verify_func(nstart: int, ncount: int): break if (pts / mask) % 2 != 0: alloc_extent := get_alloc_extent(bi) - if alloc_extent >= 0 && off >= 0: + if alloc_extent >= 0 && off >= 0 && span > 0: if span > alloc_extent || off > alloc_extent - span: check_error(EC_TK_INDEX, "pointer out of bounds: index " + int_str(off) + @@ -164,7 +173,7 @@ fn provenance_verify_func(nstart: int, ncount: int): " (IR cells)", 0, 0) else if alloc_extent >= 0: - if span > alloc_extent: + if span > 0 && span > alloc_extent: check_error(EC_TK_INDEX, "pointer access span " + int_str(span) + " exceeds allocation extent " + int_str(alloc_extent) + diff --git a/src/compiler/ty_shadow.cr b/src/compiler/ty_shadow.cr index 76b86151..e1fdd23e 100644 --- a/src/compiler/ty_shadow.cr +++ b/src/compiler/ty_shadow.cr @@ -387,13 +387,15 @@ fn sh_term_of_ti(ti: int) -> int: // IR_LOAD_ENUM_TAG has two call sites that pass literal 0, so it keeps no // type information. // · 辅码(face 1,tk 语义 = 非类型码 -> 原码进辅码槽):IR_BOUNDS_CHECK(0/1 -// 动态上限旗标,instr.cr:1340 `ti != 0`)/ IR_DEREF / IR_STORE_PTR(访问宽度码 -// 8/4/2/1,instr.cr 的 e2_ptr_bounds_check)/ IR_SPAWN(spawn_count;-1 = 动态)/ -// IR_HOTPATCH_ROUTE with explicit 0. +// 动态上限旗标,instr.cr:1340 `ti != 0`)/ IR_DEREF / IR_STORE_PTR(旧访问宽度 +// 码 8/4/2/1;v10 Task 5 起前端不再发射,宽度由 corearch 从 pointee 语义类型 +// 重建——分类规则保留:重引入的宽度码仍必须落辅码面)/ IR_SPAWN(spawn_count; +// -1 = 动态)/ IR_HOTPATCH_ROUTE with explicit 0. // · 无类型信息(face 2,tk 语义 = 无):其余全部 op。 -// Numeric coincidence: flag 1 equals the TI_DEX row and width 8 equals the -// TI_DEX_S row. Classification must be per opcode; guessing by row number would -// assign a dex item to a pointer dereference. +// Numeric coincidence: flag 1 equals the TI_DEX row and the legacy width code 8 +// equals the TI_DEX_S row. The frontend no longer emits that width, but the +// classifier must keep blocking the collision: classification is per opcode and +// guessing by row number would assign a dex item to a pointer dereference. // // 原子行 vs 复合行:类型信息只接受项可逆的行 // atom_of(term) == ti 当且仅当 term 是 TT_ATOM 且 b 槽就是 ti。指针、引用、 diff --git a/src/compiler/type_selftest.cr b/src/compiler/type_selftest.cr index 176b7b75..c931d372 100644 --- a/src/compiler/type_selftest.cr +++ b/src/compiler/type_selftest.cr @@ -2230,11 +2230,15 @@ fn type_selftest_run() -> int: total = total + 1 fails = fails + ts_check_b("t4.tk_bounds_flag1_rejected", (sh_tk_term_of_code(IR_BOUNDS_CHECK, 1) == -1 && sh_term_of_ti(1) >= 0), true) - // 反例 2:IR_DEREF 的 tk 是访问宽度(8 字节),数值 8 同时是 - // TI_DEX_S 记录。 + // 反例 2:IR_DEREF / IR_STORE_PTR 的旧辅码是访问宽度(8 字节),数值 8 + // 同时是 TI_DEX_S 记录。前端 v10 Task 5 起不再发射该宽度;此处保留分类器 + // 钉子:旧宽度码若被重引入,仍必须落辅码面(term = -1),不得当成类型行。 total = total + 1 fails = fails + ts_check_b("t4.tk_deref_width8_rejected", (sh_tk_term_of_code(IR_DEREF, 8) == -1 && sh_term_of_ti(TI_DEX_S) >= 0), true) + total = total + 1 + fails = fails + ts_check_b("t4.tk_store_ptr_width8_rejected", + (sh_tk_term_of_code(IR_STORE_PTR, 8) == -1 && sh_term_of_ti(TI_DEX_S) >= 0), true) // 反例 3:记录合法(0 = TI_INT),但 op 不属于任何类型或辅码类别。 // 不能因为 tk 恰好合法就放行。 total = total + 1 diff --git a/src/targets/x86_64/linux/elf/frontend_target.cr b/src/targets/x86_64/linux/elf/frontend_target.cr index 290abdd4..d4eab9c4 100644 --- a/src/targets/x86_64/linux/elf/frontend_target.cr +++ b/src/targets/x86_64/linux/elf/frontend_target.cr @@ -3,7 +3,10 @@ // // The three axes are explicit. The frontend asks this file for import roots, // the backend command, and runtime source locations; it does not embed target -// directory names or backend flags itself. +// directory names or backend flags itself. The layout-introspection functions +// below answer @sizeOf/@alignOf/@field; they are the only place where source +// code reaches a target layout value, and they never feed an IR width, stride, +// or byte extent. fn target_name() -> string: return "x86_64/linux/elf" diff --git a/src/targets/x86_64/linux/elf/main.cr b/src/targets/x86_64/linux/elf/main.cr index 119a841c..273a6183 100644 --- a/src/targets/x86_64/linux/elf/main.cr +++ b/src/targets/x86_64/linux/elf/main.cr @@ -64,6 +64,8 @@ fn backend_has_extended_flags() -> int: return 1 if cli_has("dump-nod-types") != 0: return 1 + if cli_has("dump-alloc-strides") != 0: + return 1 if cli_has("dump-types") != 0: return 1 if cli_has("dump-ifaces") != 0: diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index ebfb6e9b..1ed33ef2 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -1570,7 +1570,8 @@ def test_p4t4_tk_slot_rule_per_node(): · 辅码面 ⇒ term == -1 ∧ aux == 原码——**含数值陷阱**:BOUNDS_CHECK 的旗标 1 (数值上恰是合法类型行 TI_DEX,只能靠分类表挡住); · IR_DEREF/IR_STORE_PTR 不再携带访问宽度(Task 5:宽度由 corearch 从 - pointee 语义类型 + target 重建)⇒ 其 tk == 0 ∧ aux == 0; + pointee 语义类型 + target 重建)⇒ **每一个** DEREF/STORE_PTR 的 tk == 0 + ∧ aux == 0(重引入任何宽度码立即转红); · 其余 op ⇒ term == -1 ∧ aux == 原码; · **派生码不变量(逐节点)**:term ≥ 0 ⇒ tk == b;aux ≠ 0 ⇒ tk == aux;否则 tk == 0。 正控逐行类型面:int/bool/str/dex_s 四个类型行的 AK_* 与行号 b 槽。""" @@ -1587,7 +1588,8 @@ def test_p4t4_tk_slot_rule_per_node(): assert hdr['aux_nonzero'] > 0, "vacuous: no node carries an aux code" saw = {'const_rows': set(), 'binary_rows': set()} - traps = {'deref_no_width': 0, 'store_no_width': 0, 'bounds_flag1': 0, 'bounds_flag0': 0, + traps = {'deref_nodes': 0, 'store_nodes': 0, 'deref_with_width': 0, + 'store_with_width': 0, 'bounds_flag1': 0, 'bounds_flag0': 0, 'call_row': 0, 'spawn_dyn': 0} for (n, op, tk, term, tag, a, b, c, aux) in rows: # ── 派生码不变量(D22-①,逐节点)── @@ -1617,10 +1619,14 @@ def test_p4t4_tk_slot_rule_per_node(): assert term == -1, \ f"node {n}: non-mint op {op} got term {term}" assert aux == tk, f"node {n}: non-mint op {op} aux {aux} != code {tk}" - if op == IR_DEREF and tk == 0: - traps['deref_no_width'] += 1 - if op == IR_STORE_PTR and tk == 0: - traps['store_no_width'] += 1 + if op == IR_DEREF: + traps['deref_nodes'] += 1 + if tk != 0: + traps['deref_with_width'] += 1 + if op == IR_STORE_PTR: + traps['store_nodes'] += 1 + if tk != 0: + traps['store_with_width'] += 1 if op == IR_BOUNDS_CHECK and tk == 1: traps['bounds_flag1'] += 1 if op == IR_BOUNDS_CHECK and tk == 0: @@ -1636,14 +1642,36 @@ def test_p4t4_tk_slot_rule_per_node(): assert (8, AK_DEX) in saw['const_rows'], \ f"dex_s row (8) not mapped to AK_DEX: {saw['const_rows']}" assert saw['binary_rows'], "no binary node carries a term" - # 陷阱非空:BOUNDS_CHECK 旗标 1/0 与「指针 op 无可宽度」必须逐条在夹具里 - # 出现过(否则用例空转)。IR_CALL 已入类型行面(P5 T2)⇒ 不再计入陷阱。 - assert traps['deref_no_width'] >= 1 and traps['store_no_width'] >= 1, traps + # 陷阱非空(BOUNDS_CHECK 旗标 1/0)+ 指针 op **必须出现且不得携带任何宽度码**: + # Task 5 起 IR_DEREF/IR_STORE_PTR 的 tk 恒 0(宽度由 corearch 从 pointee + # 语义类型重建)。此处不是「存在一个无宽度样例」而是「每一个都不许有宽度」, + # 重新引入宽度码(8/4/1…)立刻转红。IR_CALL 已入类型行面(P5 T2)⇒ 不计陷阱。 + assert traps['deref_nodes'] >= 1 and traps['store_nodes'] >= 1, traps + assert traps['deref_with_width'] == 0, traps + assert traps['store_with_width'] == 0, traps assert traps['bounds_flag1'] >= 1 and traps['bounds_flag0'] >= 1, traps finally: _cleanup(src) +def test_deref_store_width_code_is_aux_face(): + """分类器单元检查(Task 5 复核):旧访问宽度码 8 在 IR_DEREF / IR_STORE_PTR + 上必须归**辅码面**(term = -1),不得因数值 8 恰是 TI_DEX_S 行而被当成类型。 + + 前端已不再发射宽度(见 test_p4t4_tk_slot_rule_per_node 的「每个节点 tk == 0」); + 本用例钉住分类规则本身,防止未来重引入宽度码时被静默当作类型行。 + `corec selftest-types` 的 t4 反例即该规则的执行面。 + """ + r = subprocess.run([COREC, 'selftest-types'], capture_output=True, text=True, + cwd=BASE, timeout=180) + out = r.stdout + r.stderr + assert r.returncode == 0, f"selftest-types rc={r.returncode}:\n{out[-2000:]}" + for case in ('t4.tk_deref_width8_rejected', + 't4.tk_store_ptr_width8_rejected'): + assert f"PASS {case}" in out, \ + f"classifier case {case} missing or failed:\n{out[-2000:]}" + + def test_p4t4_tk_slot_dex_binary_corpus(): """㉗ 语料面(dex_test.cr,仓内文件):IR_BINARY 的 TI_DEX 行(后端 `ti == TI_DEX` 分派 SSE2 的那支)⇒ 项 = dex 原子;IR_CONST 的 TI_DEX/DEX_S 两行都归 AK_DEX diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 40d212ce..2e70bfb8 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -385,45 +385,165 @@ def test_frontend_has_no_layout_calls(): assert name not in src, f"{path} still calls {name}" -def test_layout_introspection_is_a_target_query(): - """@sizeOf/@alignOf/@field ask the target descriptor; the compiler does not - evaluate layout itself. Their results are program constants, so the same - source keeps byte-identical v10 output.""" - ir_gen = (REPO_ROOT / "src/compiler/ir_gen.cr").read_text(encoding="utf-8") - for name in ( - "target_introspect_size(", - "target_introspect_align(", - "target_introspect_field_offset(", - ): - assert name in ir_gen, f"ir_gen must ask the target descriptor for {name}" - +def _defines_fn(text, name): + return f"fn {name}(" in text + + +def test_layout_introspection_is_target_owned(): + """@sizeOf/@alignOf/@field are answered by the target descriptor; the + compiler must not define its own layout evaluator. Their results are + program constants, so the same source keeps byte-identical v10 output.""" + introspect = ( + "target_introspect_size", + "target_introspect_align", + "target_introspect_field_offset", + ) desc = (REPO_ROOT / "src/targets/x86_64/linux/elf/frontend_target.cr").read_text( encoding="utf-8" ) - for name in ( - "fn target_introspect_size(", - "fn target_introspect_align(", - "fn target_introspect_field_offset(", + for name in introspect: + assert _defines_fn(desc, name), f"target descriptor missing fn {name}" + + ir_gen = (REPO_ROOT / "src/compiler/ir_gen.cr").read_text(encoding="utf-8") + for name in introspect: + assert not _defines_fn(ir_gen, name), f"ir_gen must not define fn {name}" + + +def _fn_body(text, name): + """Return the source of a top-level function up to the next top-level line.""" + marker = f"fn {name}(" + start = text.index(marker) + lines = text[start:].splitlines() + body = [lines[0]] + for line in lines[1:]: + if line and not line[0].isspace(): + break + body.append(line) + return "\n".join(body) + + +INTERP_LAYOUT_BRANCHES = { + "// IR_ALLOC_ARRAY": "interp_ptr_stride()", + "// IR_ALLOC_STRUCT": "interp_field_stride()", + "// IR_ADDR_INDEX": "interp_ptr_stride()", + "// IR_SLICE": "interp_ptr_stride()", + "// IR_LOAD_FIELD": "interp_field_stride()", + "// IR_STORE_FIELD": "interp_field_stride()", + "// IR_LOAD_INDEX:": "interp_ptr_stride()", + "// IR_LOAD_INDEX_VAR:": "interp_ptr_stride()", + "// IR_STORE_INDEX:": "interp_ptr_stride()", + "// IR_STORE_INDEX_VAR:": "interp_ptr_stride()", + "// IR_MAKE_ENUM": "interp_field_stride()", +} + +# Slot-cell access is the interpreter's own 8-byte value model +# (`cell_load(g_ir_vals, var * 8)`), not target layout. Strip those before +# forbidding hardcoded scales in a layout branch. +INTERP_SLOT_CELL_MUL = re.compile( + r"\b(?:g_ir_vals|g_label_poses|g_interp_stack|g_ir_func_var_start|" + r"g_ir_func_var_count|g_ir_func_node_start|g_ir_func_node_count|" + r"g_ir_func_name_idx|g_loop_region_enter|g_loop_region_exit)" + r"\s*,\s*[^)\n]*\*\s*8" +) + +# A hardcoded scale is a multiply/add by 8 or an assignment of 8, but `== 8`, +# `>= 8`, `<= 8`, `!= 8` are comparisons, not layout constants. +INTERP_HARDCODED_SCALE = re.compile( + r"(?:(?+\-*/])\*\s*8\b)|(?:(?+\-*/])\+\s*8\b)|(?:(?])=\s*8\b)" +) + + +def _line_indent(line): + return len(line) - len(line.lstrip()) + +def _op_guard(lines, marker_index): + """The `if op...` guard that owns the marker: either directly below the + marker comment, or the nearest enclosing guard above it.""" + base = _line_indent(lines[marker_index]) + j = marker_index + 1 + while j < len(lines) and ( + not lines[j].strip() or lines[j].lstrip().startswith("//") ): - assert name in desc, f"target descriptor missing {name}" + j += 1 + if ( + j < len(lines) + and _line_indent(lines[j]) == base + and lines[j].lstrip().startswith("if ") + ): + return j + k = marker_index - 1 + while k >= 0: + line = lines[k] + if line.strip() and _line_indent(line) < base and line.lstrip().startswith("if "): + return k + k -= 1 + return None + + +def _branch_blocks(text, marker): + """The whole `if op...` branch that contains `marker` (both interpreter paths).""" + lines = text.splitlines() + blocks = [] + for i, line in enumerate(lines): + if marker not in line: + continue + g = _op_guard(lines, i) + if g is None: + continue + guard_indent = _line_indent(lines[g]) + block = [lines[g]] + k = g + 1 + while k < len(lines): + cur = lines[k] + if cur.strip(): + ind = _line_indent(cur) + stripped = cur.lstrip() + if ind < guard_indent: + break + if ind == guard_indent and ( + stripped.startswith("if ") + or stripped.startswith("// IR_") + or stripped.startswith("fn ") + ): + break + block.append(cur) + k += 1 + blocks.append("\n".join(block)) + return blocks def test_interp_has_no_direct_layout_calls(): - """`corec run` executes IR without a projection pass, so it asks a - target-owned hook for the element scale. It must not reach into the - compiler mapping interface or inline the x86 scale itself.""" + """`corec run` executes IR without a projection pass, so every layout branch + asks a target-owned hook. The check walks the actual ADDR_INDEX / SLICE / + ALLOC_ARRAY / field / index / enum branches, not just names in the file: a + reintroduced `* 8` (or `= 8` / `+ 8`) fails even if it is spelled inline.""" src = (REPO_ROOT / "src/compiler/interp.cr").read_text(encoding="utf-8") - for name in ("mp_offset", "mp_stride"): + for name in ("mp_offset", "mp_stride", "mp_extent", "mp_field_offset"): assert name not in src, f"src/compiler/interp.cr still calls {name}" - hook_path = REPO_ROOT / "src/arch/x86_64/x86_mapping.cr" - hook = hook_path.read_text(encoding="utf-8") - assert "fn interp_ptr_offset(" in hook, "missing target-owned interp offset hook" - assert "fn interp_ptr_stride(" in hook, "missing target-owned interp stride hook" - # The hook is a target-owned delegation, never a hardcoded x86 scale. - hook_body = hook.split("fn interp_ptr_offset(", 1)[1] - assert "mp_offset(" in hook_body, "interp layout hook must delegate to the target" - assert "* 8" not in hook_body, "interp layout hook hardcodes the x86 scale" + seen = 0 + for marker, hook in INTERP_LAYOUT_BRANCHES.items(): + blocks = _branch_blocks(src, marker) + assert blocks, f"interp branch marker not found: {marker}" + for block in blocks: + seen += 1 + assert hook in block, f"{marker} must use {hook}" + cleaned = INTERP_SLOT_CELL_MUL.sub("", block) + found = INTERP_HARDCODED_SCALE.search(cleaned) + assert found is None, ( + f"{marker} still hardcodes the x86 layout ({found.group(0)!r}):\n{block}" + ) + assert seen >= 20, f"expected both interpreter paths, found {seen} branches" + + hooks = (REPO_ROOT / "src/arch/x86_64/x86_mapping.cr").read_text(encoding="utf-8") + for fname, delegates in ( + ("interp_ptr_offset", "mp_offset("), + ("interp_ptr_stride", "mp_stride("), + ("interp_field_stride", "mp_stride("), + ): + body = _fn_body(hooks, fname) + assert delegates in body, f"{fname} must delegate to the target mapping" + assert "* 8" not in body, f"{fname} hardcodes the x86 scale" # --------------------------------------------------------------------------- @@ -530,6 +650,67 @@ def test_v10_end_to_end(): assert s3 == 0, "ADDR_INDEX carries a scale in src3" +ALLOC_STRIDE_SOURCE = """fn main() -> int: + a : [bool; 2] + a[0] = true + a[1] = false + if a[0]: + return 7 + return 1 +""" + + +def test_alloc_stride_is_element_type_driven(): + """The wire carries no stride. corearch rebuilds it from the destination + variable's semantic array element type, not from an int-slot assumption. + + TYP_ARRAY == 2 and TI_BOOL == 2 in ast.cr; they are asserted explicitly so + the test fails loudly if either constant drifts. + """ + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_v10(tmp, ALLOC_STRIDE_SOURCE, "allocstride") + recs = [r for r in _nod_records(data) if r[0] == 8] + assert recs, "no IR_ALLOC_ARRAY node in the probe" + for rec in recs: + assert rec[3] == 0 and rec[4] == 0, "ALLOC_ARRAY carries a stride on the wire" + + ccr = tmp / "allocstride.ccr" + ccr.write_bytes(data) + r = _run([COREARCH, ccr, "--dump-alloc-strides"]) + assert r.returncode == 0, r.stdout + r.stderr + rows = [ln for ln in r.stdout.splitlines() if ln.startswith("alloc ")] + assert rows, f"no alloc-stride rows:\n{r.stdout}" + row = rows[0].split() + pairs = dict(zip(row[2::2], row[3::2])) + assert int(pairs["kind"]) == 2, f"dest type is not TYP_ARRAY: {pairs}" + assert int(pairs["elem"]) == 2, f"element row is not TI_BOOL: {pairs}" + assert int(pairs["stride"]) == 8, f"unexpected reconstructed stride: {pairs}" + + out = tmp / "allocstride.elf" + r = _run([COREARCH, ccr, "--elf", "--static", "-o", out]) + assert r.returncode == 0, r.stdout + r.stderr + run = subprocess.run([str(out)], capture_output=True, text=True, timeout=10) + assert run.returncode == 7, ( + f"bool-array probe returned {run.returncode}, stdout={run.stdout!r}" + ) + + +def test_provenance_unknown_type_span_fails_closed(): + """A warm cache may miss the type row a pointer variable refers to. The pass + must treat that as unknown (span -1) and keep the runtime bounds check, never + silently assume a one-cell access.""" + src = (REPO_ROOT / "src/compiler/provenance_verify.cr").read_text(encoding="utf-8") + for fname in ("pv_value_span", "pv_access_span"): + body = _fn_body(src, fname) + assert "ti < 0" in body and "ti >= g_type_count" in body, f"{fname} lacks range guard" + assert "return -1" in body, f"{fname} does not fail closed" + + verify = _fn_body(src, "provenance_verify_func") + assert "span > 0" in verify, "static proof must require a known span" + assert "runtime_targets = runtime_targets + 1" in verify, "runtime fallback was removed" + + def test_corearch_accepts_v10_and_rejects_v9(): with tempfile.TemporaryDirectory() as td: tmp = Path(td) From 231bb6464f1ac905d83093826b56d916754caf92 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 01:52:50 +0800 Subject: [PATCH 49/58] ccr: keep runtime pointer bound for unknown provenance span --- src/compiler/provenance_verify.cr | 15 +++++- tests/selfhost/test_ccr_v10_format.py | 77 +++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/compiler/provenance_verify.cr b/src/compiler/provenance_verify.cr index 4625f861..108dd27a 100644 --- a/src/compiler/provenance_verify.cr +++ b/src/compiler/provenance_verify.cr @@ -189,14 +189,25 @@ fn provenance_verify_func(nstart: int, ncount: int): mask = mask * 2 bi = bi + 1 - if off < 0 && runtime_targets == 1 && runtime_base >= 0: + // A constant offset plus a known span can be proven statically. A + // dynamic offset (off < 0) or an unknown span (span <= 0: missing or + // out-of-range type row, e.g. a warm cache without it) cannot. Both + // must keep the runtime bound, otherwise the backend would emit only + // a null check and the access would be fail-open. `span` is never 0 + // from pv_value_span/pv_access_span; treating 0 as unknown is + // intentionally conservative. + needs_runtime : ., mut = 0 + if off < 0 || span <= 0: + needs_runtime = 1 + + if needs_runtime != 0 && runtime_targets == 1 && runtime_base >= 0: cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S3, runtime_size) if op == IR_DEREF: cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_S2, runtime_base) else: // IR_STORE_PTR has no result, so dest carries the runtime base. cell_store(g_df_nodes, ni * ESZ_DFNODE + OFF_DF_DEST, runtime_base) - else if off < 0 && runtime_targets > 1: + else if needs_runtime != 0 && runtime_targets > 1: check_error(EC_TK_INDEX, "runtime pointer bounds check has multiple allocation targets", 0, 0) diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 2e70bfb8..ddb218b7 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -422,8 +422,36 @@ def _fn_body(text, name): return "\n".join(body) -INTERP_LAYOUT_BRANCHES = { - "// IR_ALLOC_ARRAY": "interp_ptr_stride()", +def _strip_crystal_comments(text): + """Drop `//` comments so a structural assertion cannot match a comment.""" + out = [] + for line in text.splitlines(): + cut = line.find("//") + out.append(line[:cut] if cut >= 0 else line) + return "\n".join(out) + + +def _guard_block(text, guard): + """The `if :` statement plus its body (comment-stripped input).""" + lines = text.splitlines() + for i, line in enumerate(lines): + if guard not in line: + continue + indent = _line_indent(line) + block = [line] + for cur in lines[i + 1:]: + if cur.strip(): + ind = _line_indent(cur) + if ind < indent: + break + if ind == indent and cur.lstrip().startswith("if "): + break + block.append(cur) + return "\n".join(block) + return "" + + +INTERP_LAYOUT_BRANCHES = { "// IR_ALLOC_ARRAY": "interp_ptr_stride()", "// IR_ALLOC_STRUCT": "interp_field_stride()", "// IR_ADDR_INDEX": "interp_ptr_stride()", "// IR_SLICE": "interp_ptr_stride()", @@ -698,9 +726,17 @@ def test_alloc_stride_is_element_type_driven(): def test_provenance_unknown_type_span_fails_closed(): """A warm cache may miss the type row a pointer variable refers to. The pass - must treat that as unknown (span -1) and keep the runtime bounds check, never - silently assume a one-cell access.""" - src = (REPO_ROOT / "src/compiler/provenance_verify.cr").read_text(encoding="utf-8") + must treat that as unknown (span -1) and keep the runtime bounds check even + with a *constant* offset, never silently assume a one-cell access. + + A runtime fixture is impractical here: the unknown span only arises when the + variable's TYPE row is outside the loaded table, which a plain source compile + cannot produce (the frontend allocates the row). provenance_verify runs in + corec before serialization, so corearch cannot re-run it on a doctored .ccr. + The condition is therefore pinned structurally on comment-stripped code. + """ + raw = (REPO_ROOT / "src/compiler/provenance_verify.cr").read_text(encoding="utf-8") + src = _strip_crystal_comments(raw) for fname in ("pv_value_span", "pv_access_span"): body = _fn_body(src, fname) assert "ti < 0" in body and "ti >= g_type_count" in body, f"{fname} lacks range guard" @@ -708,8 +744,39 @@ def test_provenance_unknown_type_span_fails_closed(): verify = _fn_body(src, "provenance_verify_func") assert "span > 0" in verify, "static proof must require a known span" + + # The runtime trigger must combine a dynamic offset with an unknown span, + # and must actually set the flag on the next statement. + vlines = verify.splitlines() + trigger = None + for i, line in enumerate(vlines): + if line.strip().startswith("if ") and ("span <= 0" in line or "span < 0" in line): + trigger = i + break + assert trigger is not None, "missing unknown-span runtime trigger" + assert "off < 0" in vlines[trigger], "runtime trigger drops the dynamic-offset case" + assert "||" in vlines[trigger], "runtime trigger must be a disjunction" + nxt = next(ln.strip() for ln in vlines[trigger + 1:] if ln.strip()) + assert nxt == "needs_runtime = 1", f"runtime trigger does not set the flag: {nxt!r}" + + guards = [ln.strip() for ln in vlines if "runtime_targets" in ln and "if " in ln] + single = [ln for ln in guards if "runtime_targets == 1" in ln] + multi = [ln for ln in guards if "runtime_targets > 1" in ln] + assert len(single) == 1 and len(multi) == 1, guards + assert "needs_runtime" in single[0], "single-target marker ignores unknown span" + assert "needs_runtime" in multi[0], "multiple-target error ignores unknown span" assert "runtime_targets = runtime_targets + 1" in verify, "runtime fallback was removed" + # The backend must emit a real bounds check (not only a null check) whenever + # the frontend marked a runtime bound in s3. + instr = _strip_crystal_comments( + (REPO_ROOT / "src/arch/x86_64/instr.cr").read_text(encoding="utf-8") + ) + for guard in ("if op == IR_DEREF && d >= 0:", "if op == IR_STORE_PTR:"): + block = _guard_block(instr, guard) + assert block, f"missing backend branch {guard}" + assert "s3 != 0" in block, f"{guard} does not emit a bounds check for s3 != 0" + def test_corearch_accepts_v10_and_rejects_v9(): with tempfile.TemporaryDirectory() as td: From f9820ae21736f57316b8956719f9e1bf6bd3ec92 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 01:57:34 +0800 Subject: [PATCH 50/58] ccr: make v10 loader consume canonical segment order --- src/compiler/ccr_io.cr | 1494 +++++++++++++------------ tests/selfhost/test_ccr_v10_stream.py | 305 +++++ 2 files changed, 1056 insertions(+), 743 deletions(-) create mode 100644 tests/selfhost/test_ccr_v10_stream.py diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index c99f5df0..1eff922f 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -9,6 +9,8 @@ // Header 16B: magic "CCR1", version 0x000A0000, segment count 9, flags. // Segment table: 9 entries of {tag u32, offset u32, size u32}. Tags are in // canonical order, offsets are contiguous and start at the end of the table. +// The loader consumes the bodies in this same order with a forward-only cursor; +// it uses the table entries only to validate bounds and contiguity. // // STR(1): [count][{len, data}]. // SYM(4): @@ -1153,13 +1155,16 @@ fn inject_var_shift() -> int: // Loader output is semantic objects plus guards. The linear NOD-to-g_ir_instrs // rebuild is outside this file: build_linear_schedule in regalloc.cr is called by // corearch.cr or src/targets/x86_64/linux/elf/main.cr after a successful load. -// Parse order (dependencies, not file order; segment offsets are random-access): -// STR -> SYM (globals and funcs rebuild the var namespace) -> REG (derive -// nstart/ncount and function instruction bounds) -> NOD (40B; eight semantic -// fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT (24B to the -// in-memory 24B table; drop version and convert half-open live_end back to -// closed) -> EDG (contiguous ranges and topology checks into g_v7_edges) -> -// TYPE/IFACE (rebuild type rows and terms, and interface tables) -> CASE. +// Parse order (canonical file order, forward-only cursor): +// STR -> TYPE (rebuild type rows/terms and the term index) -> IFACE (rebuild the +// interface tables from STR indices and TYPE/term domains) -> SYM (globals and +// funcs rebuild the var namespace; fn_meta stays local for ENT/REG) -> NOD (40B; +// eight semantic fields into g_v7_nod_sem, adjacency into g_v7_nod_meta) -> ENT +// (24B to the in-memory table; version dropped, half-open live_end closed) -> REG +// (derive nstart/ncount and function instruction bounds) -> EDG (contiguous +// ranges and topology checks into g_v7_edges) -> CASE. +// The segment table validates boundaries and contiguity only. Every body is +// consumed from the running cursor; there is no seek back to a table offset. // Guards use only NOD counts, adjacency, and REG-derived bounds; the loader does // not depend on the linear stream. @@ -1351,8 +1356,10 @@ fn load_ccr(data: string, fsize: int) -> int: g_impl_for_count = 0 g_method_count = 0 + // The table walk consumed exactly 16 + seg_cnt*12 bytes, so `pos` is + // already at the first body (tag STR). Every block below continues from this + // running cursor; the segment table only supplies boundary ends. // === STR: strings === - pos = seg_off1 if !ccr_has_bytes(pos, 4, seg_end1): return -1 str_cnt := buf_read_u32(data, pos) @@ -1379,498 +1386,720 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store_byte(s, sl, 0) str_intern(s) si = si + 1 + if pos != seg_end1: + return -1 - // === SYM: globals(16B each: name u32, type u32, init_val i64)=== - // var 命名空间重建(流式):全局行 = 记录序 0..G-1(行名/型随本记录携带, - // var rows are the same source); id = row order. Function declaration areas - // follow in row order. - pos = seg_off4 - if !ccr_has_bytes(pos, 4, seg_end4): + // === TYPE(2):类型信息=== + // 字节:row_count -> 记录表(24B/条)-> term_count -> 项 DAG(40B/条;哈希 + // 不落盘、本处 tt_hash5 重算)。重建 = g_types 行 + g_type_terms 项 + + // g_tt_index 索引重放(grow_tt_index -> tt_reindex)。 + // 逐条校验(违规 = 拒绝,不得当作空表或截断表,判定约定 C.5-3): + // 1 计数/长度自洽(计数 × 记录尺寸 <= 段余量);2 段体恰两小节(行走完 + // == seg_end2,无尾随字节);3 tag in 0..10(TT_*)+ a..d >= -1;4 子项引用 + // -1 或 < 自身行号(拓扑无环;标注槽按 tag 分派跳过,字段表见 + // ccr_type_term_ref_ok,ATOM 的 b = 自类型表记录编号可达数千,不得误拒)。 + // 复位:记录表/项层(含引擎预算/memo/lits,见 tt_layer_reset 注记)全部作废 + // 重建(旧行号语义的缓存一并清;记录表大小随段内容,此处为唯一复位点)。 + // corearch 侧无桥接/checker 层:记录表 = 数据(消费者 = --dump-types 通道 + + // 未来判定原语),本处不做语义解释。 + g_type_count = 0 + tt_layer_reset() + if !ccr_has_bytes(pos, 4, seg_end2): return -1 - gc := buf_read_u32(data, pos) + trow_cnt := buf_read_u32(data, pos) pos = pos + 4 - if gc > (seg_end4 - seg_off4) / ESZ_GLOBAL_DISK: + if trow_cnt > (seg_end2 - pos) / ESZ_TYPE_ROW: return -1 - grow_ir_globals(gc) - gi : ., mut = 0 + grow_types(trow_cnt) + trow : ., mut = 0 loop: - if gi >= gc: + if trow >= trow_cnt: break - if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end4): + if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end2): return -1 - gname_ni := buf_read_u32(data, pos) - pos = pos + 4 - gtype := buf_read_u32(data, pos) - pos = pos + 4 - ginit_val := buf_read_i64(data, pos) + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)) pos = pos + 8 - grow_ir_vars(g_ir_var_count + 1) - gvar := g_ir_var_count - irv_set_name(gvar, gname_ni) - irv_set_id(gvar, gvar) - irv_set_type(gvar, gtype) - g_ir_var_count = gvar + 1 - cell_store(g_ir_globals, gi * 24, gname_ni) - cell_store(g_ir_globals, gi * 24 + 8, gvar) - cell_store(g_ir_globals, gi * 24 + 16, ginit_val) - g_ir_global_count = gi + 1 - gi = gi + 1 - - // === SYM: funcs(24B 头 + param_ents + var 声明区)=== - // 头字段 = spec 3.2:name/param_count/ret_type/root_region(REG 记录 id)/ - // first_ent/last_ent(本函数条目文件范围)。instr/var 范围不落盘:instr = - // root_region span(REG 段解析后回填),var_start = 声明区记录顺序游标。 - if !ccr_has_bytes(pos, 4, seg_end4): + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_DATA, buf_read_i64(data, pos)) + pos = pos + 8 + cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_EXTRA, buf_read_i64(data, pos)) + pos = pos + 8 + trow = trow + 1 + g_type_count = trow_cnt + if !ccr_has_bytes(pos, 4, seg_end2): return -1 - func_cnt := buf_read_u32(data, pos) + tterm_cnt := buf_read_u32(data, pos) pos = pos + 4 - if func_cnt > (seg_end4 - pos) / ESZ_FUNC_HEAD_DISK: + if tterm_cnt > (seg_end2 - pos) / ESZ_TYPE_TERM_DISK: return -1 - grow_ir_func_meta(func_cnt) - // func 记录临时元数据(REG 段回填/ENT 块界校验用):{root i64, first i64, last i64} - fn_meta := alloc((func_cnt + 8) * 24) - fi : ., mut = 0 + grow_type_terms(tterm_cnt + 1) + tj : ., mut = 0 loop: - if fi >= func_cnt: + if tj >= tterm_cnt: break - if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end4): - return -1 - fname := buf_read_u32(data, pos) - pos = pos + 4 - fpc := buf_read_u32(data, pos) - pos = pos + 4 - fret := buf_read_u32(data, pos) - pos = pos + 4 - froot := buf_read_i32(data, pos) - pos = pos + 4 - ffe := buf_read_i32(data, pos) - pos = pos + 4 - fle := buf_read_i32(data, pos) - pos = pos + 4 - if froot < 0: - return -1 - // 每函数必有根 region(df_begin_func) - if ffe < -1 || fle < -1 || (ffe == -1) != (fle == -1): - return -1 - cell_store(g_ir_func_name_idx, fi * 8, fname) - cell_store(g_ir_func_param_count, fi * 8, fpc) - cell_store(g_ir_func_ret_type, fi * 8, fret) - cell_store(fn_meta, fi * 24, froot) - cell_store(fn_meta, fi * 24 + 8, ffe) - cell_store(fn_meta, fi * 24 + 16, fle) - // param_ents(i32 each;内存无消费者,只做越界拒绝) - pj : ., mut = 0 - loop: - if pj >= fpc: - break - if !ccr_has_bytes(pos, 4, seg_end4): - return -1 - pid := buf_read_i32(data, pos) - pos = pos + 4 - if pid < -1: - return -1 - pj = pj + 1 - // var 声明区(记录顺序 = 创建序,前 param_count 个 = 参数) - if !ccr_has_bytes(pos, 4, seg_end4): + if !ccr_has_bytes(pos, ESZ_TYPE_TERM_DISK, seg_end2): return -1 - fvc := buf_read_u32(data, pos) - pos = pos + 4 - if fpc > fvc: + ttag := buf_read_i64(data, pos) + pos = pos + 8 + ta := buf_read_i64(data, pos) + pos = pos + 8 + tb := buf_read_i64(data, pos) + pos = pos + 8 + tc2 := buf_read_i64(data, pos) + pos = pos + 8 + td := buf_read_i64(data, pos) + pos = pos + 8 + if ttag < 0 || ttag > TT_CONS: return -1 - if fvc > (seg_end4 - pos) / ESZ_VARDECL_DISK: + if ta < -1 || tb < -1 || tc2 < -1 || td < -1: return -1 - fvs2 := g_ir_var_count - cell_store(g_ir_func_var_start, fi * 8, fvs2) - dj : ., mut = 0 - loop: - if dj >= fvc: - break - if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end4): - return -1 - dname := buf_read_u32(data, pos) - pos = pos + 4 - dtype := buf_read_u32(data, pos) - pos = pos + 4 - grow_ir_vars(g_ir_var_count + 1) - drow := g_ir_var_count - irv_set_name(drow, dname) - irv_set_id(drow, drow) - irv_set_type(drow, dtype) - g_ir_var_count = drow + 1 - dj = dj + 1 - if g_ir_var_count - fvs2 != fvc: + if ccr_type_term_ref_ok(ttag, ta, tb, tc2, td, tj) == 0: return -1 - cell_store(g_ir_func_var_count, fi * 8, fvc) - g_ir_func_count = fi + 1 - fi = fi + 1 + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_TAG, ttag) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_A, ta) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_B, tb) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_C, tc2) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_D, td) + cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)) + g_type_term_count = tj + 1 + tj = tj + 1 + if pos != seg_end2: + return -1 + // 段体恰两小节(无尾随字节) + grow_tt_index(g_type_term_count + 1) // 索引重建(cap 已清零 -> 必走重建) - // === SYM: string constants(4B each)=== - if !ccr_has_bytes(pos, 4, seg_end4): + // === IFACE(3):接口数据=== + // 重建 = g_iface_entries(条目表;并置 g_iface_registry_ok = 1,段 = corearch + // 侧真源,防常量表覆盖段内容)+ g_iface_shape_names/terms + g_ifaces + // (ESZ_IFACEINFO 布局;corearch 侧无 AST,因此节点槽恒 -1、项槽 = 段值) + // + g_impl_for + g_methods。 + // 逐条校验(违规 = 拒绝;判定约定 C.5-3,不得当作空表或截断表): + // 1 五小节计数/长度自洽(计数 × 记录尺寸 <= 段余量)+ 行走完 == seg_end3(无尾随); + // 2 native_count == IFACE_ENTRY_COUNT(表内容 = 常量表,计数漂移 = 损坏); + // 3 跨段引用域:name_ni/type_ni/method_ni/mangled_ni in [0, g_str_count)(STR + // 段先于本段解析);ti_row in {-1} ∪ [0, g_type_count);term in {-1} ∪ [0, tt_count()) + // (TYPE(2) 段先于本段解析,因此两域已建立); + // 4 结构域:ak in 0..AK_NULL、method_count <= MAX_IFACE_METHODS、 + // param_count <= MAX_IFACE_METHOD_PARAMS、self_mode in 0..3。 + // IFACE 段 = 纯信息(不参与 ELF 发射);本段解析失败 = 整体拒绝(load_ccr -1)。 + if !ccr_has_bytes(pos, 4, seg_end3): return -1 - str_const_cnt := buf_read_u32(data, pos) + ifc_nat := buf_read_u32(data, pos) pos = pos + 4 - if str_const_cnt > (seg_end4 - seg_off4) / 4: + if ifc_nat != IFACE_ENTRY_COUNT: return -1 - grow_ir_str_consts(str_const_cnt) - sci : ., mut = 0 + if !ccr_has_bytes(pos, ifc_nat * 24, seg_end3): + return -1 + g_iface_entries = alloc(ifc_nat * ESZ_IFACE_ENTRY) + ifr : ., mut = 0 loop: - if sci >= str_const_cnt: + if ifr >= ifc_nat: break - if !ccr_has_bytes(pos, 4, seg_end4): - return -1 - scv := buf_read_u32(data, pos) + fak := buf_read_i32(data, pos) pos = pos + 4 - cell_store(g_ir_str_consts, sci * 8, scv) - g_ir_str_const_count = sci + 1 - sci = sci + 1 + fti := buf_read_i32(data, pos) + pos = pos + 4 + fname := buf_read_i32(data, pos) + pos = pos + 4 + flit := buf_read_i32(data, pos) + pos = pos + 4 + fops := buf_read_i64(data, pos) + pos = pos + 8 + if fak < 0 || fak > AK_NULL: + return -1 + // 原子类域 + if fti < -1 || fti >= g_type_count: + return -1 + // 类型行引用域(跨段) + if fname < 0 || fname >= g_str_count: + return -1 + // 名字引用域(跨段) + if flit < -1: + return -1 + fo2 := ifr * ESZ_IFACE_ENTRY + cell_store(g_iface_entries, fo2 + OFF_IE_AK, fak) + cell_store(g_iface_entries, fo2 + OFF_IE_TI, fti) + cell_store(g_iface_entries, fo2 + OFF_IE_NAME, fname) + cell_store(g_iface_entries, fo2 + OFF_IE_LIT, flit) + cell_store(g_iface_entries, fo2 + OFF_IE_OPS, fops) + ifr = ifr + 1 + g_iface_entry_count = ifc_nat + g_iface_registry_ok = 1 - // === SYM: structs === - if !ccr_has_bytes(pos, 4, seg_end4): + // 2 横切形状(名 ni + 形状项) + if !ccr_has_bytes(pos, 4, seg_end3): return -1 - struct_cnt := buf_read_u32(data, pos) + ifc_shn := buf_read_u32(data, pos) pos = pos + 4 - if struct_cnt > (seg_end4 - seg_off4) / 8: + if ifc_shn > (seg_end3 - pos) / 8: return -1 - grow_structs(struct_cnt) - g_struct_count = struct_cnt - sti : ., mut = 0 + iface_shape_grow(ifc_shn) + ifs : ., mut = 0 loop: - if sti >= struct_cnt: + if ifs >= ifc_shn: break - if !ccr_has_bytes(pos, 8, seg_end4): - return -1 - sname_ni := buf_read_u32(data, pos) + sname := buf_read_i32(data, pos) pos = pos + 4 - fc := buf_read_u32(data, pos) + sterm := buf_read_i32(data, pos) pos = pos + 4 - // 字段数无硬上限(侧表承载),因此旧 MAX_STRUCT_FIELDS - // 读回检查与 16 槽清零循环一并删除;段界核算保留(fc 越段,因此拒绝,判定约定)。 - if fc > (seg_end4 - pos) / 8: + if sname < 0 || sname >= g_str_count: return -1 - cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni) - cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) - si_set_field_base(sti, g_si_f_used) - // Zero generic slots - cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 0) - zgi : ., mut = 0 - loop: - if zgi >= 4: - break - cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + zgi * 8, 0) - zgi = zgi + 1 - // Write field data(侧表;磁盘布局逐字段变长,格式不变) - fi2 : ., mut = 0 - loop: - if fi2 >= fc: - break - fn_ni := buf_read_u32(data, pos) - pos = pos + 4 - ft := buf_read_u32(data, pos) - pos = pos + 4 - si_set_field_name(sti, fi2, fn_ni) - si_set_field_type(sti, fi2, ft) - fi2 = fi2 + 1 - si_commit_fields(sti, fc) - sti = sti + 1 + if sterm < 0 || sterm >= tt_count(): + return -1 + // 项引用域(跨段) + cell_store(g_iface_shape_names, ifs * 8, sname) + cell_store(g_iface_shape_terms, ifs * 8, sterm) + ifs = ifs + 1 + g_iface_shape_count = ifc_shn - // === SYM: enums === - if !ccr_has_bytes(pos, 4, seg_end4): + // 3 用户接口(头 16B + 方法 80B × method_count) + if !ccr_has_bytes(pos, 4, seg_end3): return -1 - enum_cnt := buf_read_u32(data, pos) + ifc_usn := buf_read_u32(data, pos) pos = pos + 4 - if enum_cnt > (seg_end4 - seg_off4) / 8: + if ifc_usn > (seg_end3 - pos) / 16: return -1 - grow_enums(enum_cnt) - g_enum_count = enum_cnt - ei : ., mut = 0 + grow_ifaces(ifc_usn) + ifu : ., mut = 0 loop: - if ei >= enum_cnt: + if ifu >= ifc_usn: break - if !ccr_has_bytes(pos, 8, seg_end4): + if !ccr_has_bytes(pos, 16, seg_end3): return -1 - ename_ni := buf_read_u32(data, pos) + uname := buf_read_i32(data, pos) pos = pos + 4 - vc := buf_read_u32(data, pos) + umc := buf_read_i32(data, pos) pos = pos + 4 - // 变体/携带值数无硬上限(侧表),因此两处旧检查与 16 槽清零 - // 循环删除;段界核算保留(判定约定) - if vc > (seg_end4 - pos) / 8: + ugc := buf_read_i32(data, pos) + pos = pos + 4 + pos = pos + 4 // pad(预留) + if uname < 0 || uname >= g_str_count: return -1 - cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni) - cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) - ei_set_variant_base(ei, g_ei_v_used) - cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 0) - zgi2 : ., mut = 0 + if umc < 0 || umc > MAX_IFACE_METHODS: + return -1 + if ugc < 0: + return -1 + if !ccr_has_bytes(pos, umc * 80, seg_end3): + return -1 + ub := ifu * ESZ_IFACEINFO + // 先清零整条记录(防上轮残留;节点槽/项槽的初值 = -1 由下方逐槽写) + uz : ., mut = 0 loop: - if zgi2 >= 4: + if uz >= ESZ_IFACEINFO: break - cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + zgi2 * 8, 0) - zgi2 = zgi2 + 1 - // Write variant data - vi3 : ., mut = 0 + cell_store8(g_ifaces, ub + uz, 0) + uz = uz + 1 + cell_store(g_ifaces, ub + OFF_IF_NAME, uname) + cell_store(g_ifaces, ub + OFF_IF_METHOD_COUNT, umc) + cell_store(g_ifaces, ub + OFF_IF_GENERIC_COUNT, ugc) + ium : ., mut = 0 loop: - if vi3 >= vc: + if ium >= umc: break - vni := buf_read_u32(data, pos) + mb := ub + OFF_IF_METHODS + ium * ESZ_IFMETHOD + mname := buf_read_i32(data, pos) pos = pos + 4 - tc := buf_read_u32(data, pos) + mpc := buf_read_i32(data, pos) pos = pos + 4 - if tc > (seg_end4 - pos) / 4: + msm := buf_read_i32(data, pos) + pos = pos + 4 + mrt := buf_read_i32(data, pos) + pos = pos + 4 + if mname < 0 || mname >= g_str_count: return -1 - ei_set_variant_name(ei, vi3, vni) - ei_set_variant_type_count(ei, vi3, tc) - tf : ., mut = 0 + if mpc < 0 || mpc > MAX_IFACE_METHOD_PARAMS: + return -1 + if msm < 0 || msm > 3: + return -1 + if mrt < -1 || mrt >= tt_count(): + return -1 + // 项引用域(跨段) + cell_store(g_ifaces, mb + OFF_IFM_NAME, mname) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_COUNT, mpc) + cell_store(g_ifaces, mb + OFF_IFM_SELF_MODE, msm) + cell_store(g_ifaces, mb + OFF_IFM_RET_TERM, mrt) + cell_store(g_ifaces, mb + OFF_IFM_RET_NODE, -1) // corearch 无 AST,因此节点槽恒 -1 + mq : ., mut = 0 loop: - if tf >= tc: + if mq >= MAX_IFACE_METHOD_PARAMS: break - tval := buf_read_u32(data, pos) + mt := buf_read_i32(data, pos) pos = pos + 4 - ei_set_variant_type(ei, vi3, tf, tval) - tf = tf + 1 - vi3 = vi3 + 1 - ei = ei + 1 + if mt < -1 || mt >= tt_count(): + return -1 + // 项引用域(跨段) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_TERMS + mq * 8, mt) + cell_store(g_ifaces, mb + OFF_IFM_PARAM_NODES + mq * 8, -1) // 节点槽恒 -1 + mq = mq + 1 + mp : ., mut = 0 + loop: + if mp >= MAX_IFACE_METHOD_PARAMS: + break + mc2 := buf_read_i32(data, pos) + pos = pos + 4 // 裸码(信息) + if mc2 < -1: + return -1 + cell_store(g_ifaces, mb + OFF_IFM_PARAM_TYPES + mp * 8, mc2) + mp = mp + 1 + ium = ium + 1 + ifu = ifu + 1 + g_iface_count = ifc_usn - // === SYM: opt_meta === - if !ccr_has_bytes(pos, 4, seg_end4): + // 4 impl 边(g_impl_for:{trait_ni, type_ni},声明元数据) + if !ccr_has_bytes(pos, 4, seg_end3): return -1 - mc := buf_read_u32(data, pos) + ifc_imp := buf_read_u32(data, pos) pos = pos + 4 - mi : ., mut = 0 + if ifc_imp > (seg_end3 - pos) / 8: + return -1 + grow_impl_for(ifc_imp) + ifi : ., mut = 0 loop: - if mi >= mc: + if ifi >= ifc_imp: break - if !ccr_has_bytes(pos, 8, seg_end4): - return -1 - mk := buf_read_u32(data, pos) + itr := buf_read_i32(data, pos) pos = pos + 4 - md_len := buf_read_u32(data, pos) + ity := buf_read_i32(data, pos) pos = pos + 4 - if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end4): + if itr < 0 || itr >= g_str_count: return -1 - // Allocate and store entry - grow_opt_meta(mi + 1) - mo := mi * OPT_META_STRIDE - cell_store32(g_opt_meta, mo, mk) - cell_store32(g_opt_meta, mo + 4, md_len) - di : ., mut = 0 - loop: - if di >= md_len: - break - cell_store_byte(g_opt_meta, mo + 8 + di, cell_load_byte(data, pos)) - pos = pos + 1 - di = di + 1 - g_opt_meta_count = mi + 1 - mi = mi + 1 + if ity < 0 || ity >= g_str_count: + return -1 + cell_store(g_impl_for, ifi * 16, itr) + cell_store(g_impl_for, ifi * 16 + 8, ity) + ifi = ifi + 1 + g_impl_for_count = ifc_imp - // === REG: region(24B each,spec 3.5 字段序 {kind, parent, enter_nod, - // exit_nod, first_ent, last_ent})=== - // nstart and ncount are not stored; the loader derives nstart = enter and - // ncount = exit - enter. A record with exit < enter is unclosed and has no - // valid representation, so the file is rejected. - // first/last 校验:SG_FUNC 根记录(记录顺序第 k 个 = 函数 k)的区内条目范围必须 - // 与 SYM func 记录 first/last 一致(同信息双写,失配 = 格式不一致)。 - // 函数指令边界重建:func root_region(SYM)-> REG span = [enter, exit)。 - pos = seg_off7 - if !ccr_has_bytes(pos, 4, seg_end7): + // 5 方法表(g_methods:{type_ni, method_ni, mangled_ni},iface_find_method 数据源) + if !ccr_has_bytes(pos, 4, seg_end3): return -1 - sg_n := buf_read_u32(data, pos) + ifc_met := buf_read_u32(data, pos) pos = pos + 4 - if sg_n > (seg_end7 - seg_off7) / CCR_V10_REG_DISK: + if ifc_met > (seg_end3 - pos) / 12: return -1 - sg_i : ., mut = 0 - rfcnt : ., mut = 0 // 已见 SG_FUNC 根记录数(记录顺序 = 函数序 1:1) + grow_methods(ifc_met) + ifm : ., mut = 0 loop: - if sg_i >= sg_n: + if ifm >= ifc_met: break - grow_sg(sg_i + 1) - f := sg_i * ESZ_SG - rk := buf_read_i32(data, pos) - pos = pos + 4 - rp := buf_read_i32(data, pos) - pos = pos + 4 - ren := buf_read_i32(data, pos) - pos = pos + 4 - rex := buf_read_i32(data, pos) + mtn := buf_read_i32(data, pos) pos = pos + 4 - rfe := buf_read_i32(data, pos) + mmn := buf_read_i32(data, pos) pos = pos + 4 - rle := buf_read_i32(data, pos) + mmg := buf_read_i32(data, pos) pos = pos + 4 - if rex < ren: + if mtn < 0 || mtn >= g_str_count: return -1 - // 未闭合 -> ncount 不可派生 - if rfe < -1 || rle < -1 || (rfe == -1) != (rle == -1): + if mmn < 0 || mmn >= g_str_count: return -1 - cell_store(g_sgs, f + OFF_SG_KIND, rk) - cell_store(g_sgs, f + OFF_SG_ENTER, ren) - cell_store(g_sgs, f + OFF_SG_EXIT, rex) - cell_store(g_sgs, f + OFF_SG_PARENT, rp) - cell_store(g_sgs, f + OFF_SG_NSTART, ren) - cell_store(g_sgs, f + OFF_SG_NCOUNT, rex - ren) - if rk == SG_FUNC: - if rfcnt >= func_cnt: - return -1 - // 根记录多于函数记录 - if rfe != cell_load(fn_meta, rfcnt * 24 + 8) || rle != cell_load(fn_meta, rfcnt * 24 + 16): - return -1 // REG 根记录条目范围 != SYM func 记录 - rfcnt = rfcnt + 1 - sg_i = sg_i + 1 - if rfcnt != func_cnt: + if mmg < 0 || mmg >= g_str_count: + return -1 + cell_store(g_methods, ifm * 24, mtn) + cell_store(g_methods, ifm * 24 + 8, mmn) + cell_store(g_methods, ifm * 24 + 16, mmg) + ifm = ifm + 1 + g_method_count = ifc_met + + if pos != seg_end3: return -1 - // 根记录少于函数记录(每函数必有根) - g_sg_count = sg_n + // 五小节行走完 == 段体(无尾随字节) - // 函数指令边界回填:instr_start = root_region enter, instr_count = exit − enter - // The root region span is the function node range and is the single source for - // function instruction bounds. - bfi : ., mut = 0 - loop: - if bfi >= func_cnt: - break - rid := cell_load(fn_meta, bfi * 24) - if rid < 0 || rid >= sg_n: - return -1 - fr := rid * ESZ_SG - if cell_load(g_sgs, fr + OFF_SG_KIND) != SG_FUNC: - return -1 - ren := cell_load(g_sgs, fr + OFF_SG_ENTER) - rex := cell_load(g_sgs, fr + OFF_SG_EXIT) - if rex < ren: + // === SYM: globals(16B each: name u32, type u32, init_val i64)=== + // var 命名空间重建(流式):全局行 = 记录序 0..G-1(行名/型随本记录携带, + // var rows are the same source); id = row order. Function declaration areas + // follow in row order. + if !ccr_has_bytes(pos, 4, seg_end4): + return -1 + gc := buf_read_u32(data, pos) + pos = pos + 4 + if gc > (seg_end4 - seg_off4) / ESZ_GLOBAL_DISK: + return -1 + grow_ir_globals(gc) + gi : ., mut = 0 + loop: + if gi >= gc: + break + if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end4): return -1 - cell_store(g_ir_func_instr_start, bfi * 8, ren) - cell_store(g_ir_func_instr_count, bfi * 8, rex - ren) - bfi = bfi + 1 + gname_ni := buf_read_u32(data, pos) + pos = pos + 4 + gtype := buf_read_u32(data, pos) + pos = pos + 4 + ginit_val := buf_read_i64(data, pos) + pos = pos + 8 + grow_ir_vars(g_ir_var_count + 1) + gvar := g_ir_var_count + irv_set_name(gvar, gname_ni) + irv_set_id(gvar, gvar) + irv_set_type(gvar, gtype) + g_ir_var_count = gvar + 1 + cell_store(g_ir_globals, gi * 24, gname_ni) + cell_store(g_ir_globals, gi * 24 + 8, gvar) + cell_store(g_ir_globals, gi * 24 + 16, ginit_val) + g_ir_global_count = gi + 1 + gi = gi + 1 - // === NOD: instructions(40B each)=== - // loader 产出 = 对象载入 + 检查,32B - // Semantic fields go to the kernel object buffer g_v7_nod_sem (the in-memory - // record mirrors the disk record without adjacency, with the same offsets). - // type_id is a TYPE row (-1 = none); flags is the semantic feature word. - // src2/src3 are opaque auxiliary ids: the producer never writes a width, - // stride, or byte extent there. Adjacency first_edge/edge_count go to - // g_v7_nod_meta. - // (EDG 段校验 + 对象数据 nod_edge_first/count 消费)。NOD->g_ir_instrs - // 线性重建段已移出本函数(调度重建 = 实例事务 build_linear_schedule, - // regalloc.cr,load 返回后、发射/分派前由入口调用);g_ir_instrs/ - // g_ir_instr_count 本段不再触碰(重建段原写点全清)。 - pos = seg_off5 - if !ccr_has_bytes(pos, 4, seg_end5): + // === SYM: funcs(24B 头 + param_ents + var 声明区)=== + // 头字段 = spec 3.2:name/param_count/ret_type/root_region(REG 记录 id)/ + // first_ent/last_ent(本函数条目文件范围)。instr/var 范围不落盘:instr = + // root_region span(REG 段解析后回填),var_start = 声明区记录顺序游标。 + if !ccr_has_bytes(pos, 4, seg_end4): return -1 - instr_cnt := buf_read_u32(data, pos) + func_cnt := buf_read_u32(data, pos) pos = pos + 4 - if instr_cnt > (seg_end5 - seg_off5) / CCR_V10_NOD_DISK: + if func_cnt > (seg_end4 - pos) / ESZ_FUNC_HEAD_DISK: return -1 - g_v7_nod_sem = alloc((instr_cnt + 8) * ESZ_NOD_SEM) - g_v7_nod_meta = alloc((instr_cnt + 8) * ESZ_NOD_META) - ii : ., mut = 0 + grow_ir_func_meta(func_cnt) + // func 记录临时元数据(REG 段回填/ENT 块界校验用):{root i64, first i64, last i64} + fn_meta := alloc((func_cnt + 8) * 24) + fi : ., mut = 0 loop: - if ii >= instr_cnt: + if fi >= func_cnt: break - if !ccr_has_bytes(pos, CCR_V10_NOD_DISK, seg_end5): + if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end4): return -1 - opcode := buf_read_u32(data, pos) + fname := buf_read_u32(data, pos) pos = pos + 4 - dest := buf_read_i32(data, pos) + fpc := buf_read_u32(data, pos) pos = pos + 4 - s1 := buf_read_i64(data, pos) - pos = pos + 8 // 修复 14:s1 64 位 - s2 := buf_read_i32(data, pos) + fret := buf_read_u32(data, pos) pos = pos + 4 - s3 := buf_read_i32(data, pos) + froot := buf_read_i32(data, pos) pos = pos + 4 - tid2 := buf_read_i32(data, pos) - pos = pos + 4 // type_id(TYPE row;-1 = 无) - fl2 := buf_read_u32(data, pos) - pos = pos + 4 // flags(语义特性;非目标事实) - // src2/src3 may not carry v9 target facts. The writer normalizes them; - // the loader rejects old/foreign files instead of silently accepting. - if opcode == IR_ALLOC_ARRAY: - if s2 != 0 || s3 != 0: - return -1 - else if opcode == IR_ADDR_INDEX: - if s3 != 0: + ffe := buf_read_i32(data, pos) + pos = pos + 4 + fle := buf_read_i32(data, pos) + pos = pos + 4 + if froot < 0: + return -1 + // 每函数必有根 region(df_begin_func) + if ffe < -1 || fle < -1 || (ffe == -1) != (fle == -1): + return -1 + cell_store(g_ir_func_name_idx, fi * 8, fname) + cell_store(g_ir_func_param_count, fi * 8, fpc) + cell_store(g_ir_func_ret_type, fi * 8, fret) + cell_store(fn_meta, fi * 24, froot) + cell_store(fn_meta, fi * 24 + 8, ffe) + cell_store(fn_meta, fi * 24 + 16, fle) + // param_ents(i32 each;内存无消费者,只做越界拒绝) + pj : ., mut = 0 + loop: + if pj >= fpc: + break + if !ccr_has_bytes(pos, 4, seg_end4): return -1 - else if opcode == IR_DEREF || opcode == IR_STORE_PTR: - if s3 != 0: + pid := buf_read_i32(data, pos) + pos = pos + 4 + if pid < -1: return -1 - else if ccr_nod_src3_semantic(opcode) == 0: - if s3 != 0: + pj = pj + 1 + // var 声明区(记录顺序 = 创建序,前 param_count 个 = 参数) + if !ccr_has_bytes(pos, 4, seg_end4): + return -1 + fvc := buf_read_u32(data, pos) + pos = pos + 4 + if fpc > fvc: + return -1 + if fvc > (seg_end4 - pos) / ESZ_VARDECL_DISK: + return -1 + fvs2 := g_ir_var_count + cell_store(g_ir_func_var_start, fi * 8, fvs2) + dj : ., mut = 0 + loop: + if dj >= fvc: + break + if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end4): return -1 - nfe := buf_read_u32(data, pos) - pos = pos + 4 // first_edge(邻接索引) - nec := buf_read_u32(data, pos) - pos = pos + 4 // edge_count - // Reserved flag bits must be zero. A future feature bit needs a reader, - // so rejecting here keeps old loaders from silently ignoring meaning. - if fl2 > CCR_NOD_FLAG_KNOWN_MASK: + dname := buf_read_u32(data, pos) + pos = pos + 4 + dtype := buf_read_u32(data, pos) + pos = pos + 4 + grow_ir_vars(g_ir_var_count + 1) + drow := g_ir_var_count + irv_set_name(drow, dname) + irv_set_id(drow, drow) + irv_set_type(drow, dtype) + g_ir_var_count = drow + 1 + dj = dj + 1 + if g_ir_var_count - fvs2 != fvc: return -1 - // 对象缓冲写(语义字段 = 盘字节镜像,w32/w64 低 32/64 位存储,存取 - // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest) - cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TYPE_ID, tid2) - cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_FLAGS, fl2) - cell_store(g_v7_nod_meta, ii * ESZ_NOD_META, nfe) - cell_store(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec) - ii = ii + 1 - g_v7_nod_count = instr_cnt + cell_store(g_ir_func_var_count, fi * 8, fvc) + g_ir_func_count = fi + 1 + fi = fi + 1 - // Header SYSTEM/CONCURRENT bits must agree with the graph. This needs NOD - // only, so it runs here (before ENT/EDG/TYPE consumption). - if ccr_validate_header_flags(rsv) != 0: + // === SYM: string constants(4B each)=== + if !ccr_has_bytes(pos, 4, seg_end4): return -1 - - // REG root span upper-bound check. Function instruction bounds (root_region - // span) are filled when REG is parsed, but instr_cnt is only known at NOD. At - // that point only rex >= ren was checked, not the upper bound. An out-of-range - // span would make ELF emission read outside the NOD space. For each function, - // check instr_start + count <= instr_cnt and reject negative values. This is - // the last reject point before payload consumption. - rfi : ., mut = 0 + str_const_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if str_const_cnt > (seg_end4 - seg_off4) / 4: + return -1 + grow_ir_str_consts(str_const_cnt) + sci : ., mut = 0 loop: - if rfi >= func_cnt: + if sci >= str_const_cnt: break - ris := cell_load(g_ir_func_instr_start, rfi * 8) - ric := cell_load(g_ir_func_instr_count, rfi * 8) - if ris < 0 || ric < 0 || ris + ric > instr_cnt: + if !ccr_has_bytes(pos, 4, seg_end4): return -1 - rfi = rfi + 1 + scv := buf_read_u32(data, pos) + pos = pos + 4 + cell_store(g_ir_str_consts, sci * 8, scv) + g_ir_str_const_count = sci + 1 + sci = sci + 1 - // === ENT: entries(24B each -> 内存 24B 表)=== - // 盘上半开 live_end -> 内存闭区间 −1;version 字段不入内存(组内序可推)。 - // v10 has no home field. The in-memory slot stays -1 until Task 8 retires it; - // the loader never reads that slot from disk. - if have6 != 0: - pos = seg_off6 - if !ccr_has_bytes(pos, 4, seg_end6): + // === SYM: structs === + if !ccr_has_bytes(pos, 4, seg_end4): + return -1 + struct_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if struct_cnt > (seg_end4 - seg_off4) / 8: + return -1 + grow_structs(struct_cnt) + g_struct_count = struct_cnt + sti : ., mut = 0 + loop: + if sti >= struct_cnt: + break + if !ccr_has_bytes(pos, 8, seg_end4): return -1 - ent_cnt := buf_read_u32(data, pos) + sname_ni := buf_read_u32(data, pos) pos = pos + 4 - if ent_cnt > (seg_end6 - seg_off6) / CCR_V10_ENT_DISK: + fc := buf_read_u32(data, pos) + pos = pos + 4 + // 字段数无硬上限(侧表承载),因此旧 MAX_STRUCT_FIELDS + // 读回检查与 16 槽清零循环一并删除;段界核算保留(fc 越段,因此拒绝,判定约定)。 + if fc > (seg_end4 - pos) / 8: return -1 - grow_entries(ent_cnt) - eii : ., mut = 0 + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_NAME, sname_ni) + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_FIELD_COUNT, fc) + si_set_field_base(sti, g_si_f_used) + // Zero generic slots + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_COUNT, 0) + zgi : ., mut = 0 loop: - if eii >= ent_cnt: + if zgi >= 4: break - if !ccr_has_bytes(pos, CCR_V10_ENT_DISK, seg_end6): - return -1 - ev := buf_read_i32(data, pos) - pos = pos + 4 // var_id - evr := buf_read_u32(data, pos) - pos = pos + 4 // version(盘上仅校验用) - ed := buf_read_i32(data, pos) - pos = pos + 4 // def_nod - els := buf_read_u32(data, pos) - pos = pos + 4 // live_start(含定值) - ele := buf_read_u32(data, pos) - pos = pos + 4 // live_end(半开) - efl := buf_read_u32(data, pos) - pos = pos + 4 // flags - // Semantic validation: reject out-of-range values. + cell_store(g_structs, sti * ESZ_STRUCTINFO + OFF_SI_GENERIC_NAMES + zgi * 8, 0) + zgi = zgi + 1 + // Write field data(侧表;磁盘布局逐字段变长,格式不变) + fi2 : ., mut = 0 + loop: + if fi2 >= fc: + break + fn_ni := buf_read_u32(data, pos) + pos = pos + 4 + ft := buf_read_u32(data, pos) + pos = pos + 4 + si_set_field_name(sti, fi2, fn_ni) + si_set_field_type(sti, fi2, ft) + fi2 = fi2 + 1 + si_commit_fields(sti, fc) + sti = sti + 1 + + // === SYM: enums === + if !ccr_has_bytes(pos, 4, seg_end4): + return -1 + enum_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if enum_cnt > (seg_end4 - seg_off4) / 8: + return -1 + grow_enums(enum_cnt) + g_enum_count = enum_cnt + ei : ., mut = 0 + loop: + if ei >= enum_cnt: + break + if !ccr_has_bytes(pos, 8, seg_end4): + return -1 + ename_ni := buf_read_u32(data, pos) + pos = pos + 4 + vc := buf_read_u32(data, pos) + pos = pos + 4 + // 变体/携带值数无硬上限(侧表),因此两处旧检查与 16 槽清零 + // 循环删除;段界核算保留(判定约定) + if vc > (seg_end4 - pos) / 8: + return -1 + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_NAME, ename_ni) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_VARIANT_COUNT, vc) + ei_set_variant_base(ei, g_ei_v_used) + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_COUNT, 0) + zgi2 : ., mut = 0 + loop: + if zgi2 >= 4: + break + cell_store(g_enums, ei * ESZ_ENUMINFO + OFF_EI_GENERIC_NAMES + zgi2 * 8, 0) + zgi2 = zgi2 + 1 + // Write variant data + vi3 : ., mut = 0 + loop: + if vi3 >= vc: + break + vni := buf_read_u32(data, pos) + pos = pos + 4 + tc := buf_read_u32(data, pos) + pos = pos + 4 + if tc > (seg_end4 - pos) / 4: + return -1 + ei_set_variant_name(ei, vi3, vni) + ei_set_variant_type_count(ei, vi3, tc) + tf : ., mut = 0 + loop: + if tf >= tc: + break + tval := buf_read_u32(data, pos) + pos = pos + 4 + ei_set_variant_type(ei, vi3, tf, tval) + tf = tf + 1 + vi3 = vi3 + 1 + ei = ei + 1 + + // === SYM: opt_meta === + if !ccr_has_bytes(pos, 4, seg_end4): + return -1 + mc := buf_read_u32(data, pos) + pos = pos + 4 + mi : ., mut = 0 + loop: + if mi >= mc: + break + if !ccr_has_bytes(pos, 8, seg_end4): + return -1 + mk := buf_read_u32(data, pos) + pos = pos + 4 + md_len := buf_read_u32(data, pos) + pos = pos + 4 + if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end4): + return -1 + // Allocate and store entry + grow_opt_meta(mi + 1) + mo := mi * OPT_META_STRIDE + cell_store32(g_opt_meta, mo, mk) + cell_store32(g_opt_meta, mo + 4, md_len) + di : ., mut = 0 + loop: + if di >= md_len: + break + cell_store_byte(g_opt_meta, mo + 8 + di, cell_load_byte(data, pos)) + pos = pos + 1 + di = di + 1 + g_opt_meta_count = mi + 1 + mi = mi + 1 + if pos != seg_end4: + return -1 + + // === NOD: instructions(40B each)=== + // loader 产出 = 对象载入 + 检查,32B + // Semantic fields go to the kernel object buffer g_v7_nod_sem (the in-memory + // record mirrors the disk record without adjacency, with the same offsets). + // type_id is a TYPE row (-1 = none); flags is the semantic feature word. + // src2/src3 are opaque auxiliary ids: the producer never writes a width, + // stride, or byte extent there. Adjacency first_edge/edge_count go to + // g_v7_nod_meta. + // (EDG 段校验 + 对象数据 nod_edge_first/count 消费)。NOD->g_ir_instrs + // 线性重建段已移出本函数(调度重建 = 实例事务 build_linear_schedule, + // regalloc.cr,load 返回后、发射/分派前由入口调用);g_ir_instrs/ + // g_ir_instr_count 本段不再触碰(重建段原写点全清)。 + if !ccr_has_bytes(pos, 4, seg_end5): + return -1 + instr_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if instr_cnt > (seg_end5 - seg_off5) / CCR_V10_NOD_DISK: + return -1 + g_v7_nod_sem = alloc((instr_cnt + 8) * ESZ_NOD_SEM) + g_v7_nod_meta = alloc((instr_cnt + 8) * ESZ_NOD_META) + ii : ., mut = 0 + loop: + if ii >= instr_cnt: + break + if !ccr_has_bytes(pos, CCR_V10_NOD_DISK, seg_end5): + return -1 + opcode := buf_read_u32(data, pos) + pos = pos + 4 + dest := buf_read_i32(data, pos) + pos = pos + 4 + s1 := buf_read_i64(data, pos) + pos = pos + 8 // 修复 14:s1 64 位 + s2 := buf_read_i32(data, pos) + pos = pos + 4 + s3 := buf_read_i32(data, pos) + pos = pos + 4 + tid2 := buf_read_i32(data, pos) + pos = pos + 4 // type_id(TYPE row;-1 = 无) + fl2 := buf_read_u32(data, pos) + pos = pos + 4 // flags(语义特性;非目标事实) + // src2/src3 may not carry v9 target facts. The writer normalizes them; + // the loader rejects old/foreign files instead of silently accepting. + if opcode == IR_ALLOC_ARRAY: + if s2 != 0 || s3 != 0: + return -1 + else if opcode == IR_ADDR_INDEX: + if s3 != 0: + return -1 + else if opcode == IR_DEREF || opcode == IR_STORE_PTR: + if s3 != 0: + return -1 + else if ccr_nod_src3_semantic(opcode) == 0: + if s3 != 0: + return -1 + nfe := buf_read_u32(data, pos) + pos = pos + 4 // first_edge(邻接索引) + nec := buf_read_u32(data, pos) + pos = pos + 4 // edge_count + // Reserved flag bits must be zero. A future feature bit needs a reader, + // so rejecting here keeps old loaders from silently ignoring meaning. + if fl2 > CCR_NOD_FLAG_KNOWN_MASK: + return -1 + // 对象缓冲写(语义字段 = 盘字节镜像,w32/w64 低 32/64 位存储,存取 + // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest) + cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S2, s2) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S3, s3) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_TYPE_ID, tid2) + cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_FLAGS, fl2) + cell_store(g_v7_nod_meta, ii * ESZ_NOD_META, nfe) + cell_store(g_v7_nod_meta, ii * ESZ_NOD_META + 8, nec) + ii = ii + 1 + g_v7_nod_count = instr_cnt + if pos != seg_end5: + return -1 + + // Header SYSTEM/CONCURRENT bits must agree with the graph. This needs NOD + // only, so it runs here (TYPE is already rebuilt; ENT/REG/EDG are not). + if ccr_validate_header_flags(rsv) != 0: + return -1 + + // === ENT: entries(24B each -> 内存 24B 表)=== + // 盘上半开 live_end -> 内存闭区间 −1;version 字段不入内存(组内序可推)。 + // v10 has no home field. The in-memory slot stays -1 until Task 8 retires it; + // the loader never reads that slot from disk. + if have6 != 0: + if !ccr_has_bytes(pos, 4, seg_end6): + return -1 + ent_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if ent_cnt > (seg_end6 - seg_off6) / CCR_V10_ENT_DISK: + return -1 + grow_entries(ent_cnt) + eii : ., mut = 0 + loop: + if eii >= ent_cnt: + break + if !ccr_has_bytes(pos, CCR_V10_ENT_DISK, seg_end6): + return -1 + ev := buf_read_i32(data, pos) + pos = pos + 4 // var_id + evr := buf_read_u32(data, pos) + pos = pos + 4 // version(盘上仅校验用) + ed := buf_read_i32(data, pos) + pos = pos + 4 // def_nod + els := buf_read_u32(data, pos) + pos = pos + 4 // live_start(含定值) + ele := buf_read_u32(data, pos) + pos = pos + 4 // live_end(半开) + efl := buf_read_u32(data, pos) + pos = pos + 4 // flags + // Semantic validation: reject out-of-range values. if evr < 1: return -1 if efl > 0xF: @@ -1935,35 +2164,130 @@ fn load_ccr(data: string, fsize: int) -> int: if pe != g_entry_count: return -1 - // === EDG: edges(v7 必落;8B each {to_nod u32, kind u32},所属节点 = - // 邻接隐含:节点 i 出边连续段 [first_edge, first_edge+edge_count),first_edge - // = 前缀累计(spec 3.3 邻接约定)。校验(spec 4 + 检查强度保持): - // 1 连续段与 NOD 邻接域逐节点对照(first_edge == 前缀累计;段界不出 EDG) - // 2 Σ edge_count == edg_count(规则 3;行走完 == 段体大小) - // 3 每条边 to_nod < instr_cnt(NOD 引用界内)+ to_nod > 所属节点 - // (规则 1 拓扑不变量,数据/state 边前向;自环/后向 = 文件损坏拒绝) - // 4 kind <= 1(0=数据、1=state;2+ 预留 = 未知边类拒绝) - // 校验通过后边表入 g_v7_edges 缓冲(文件序连续段,图语义消费通道/调试)。 - // branch/jump 目标 = 操作数引用非边(v7 4 边界声明),NOD 操作数不参与 - // 本段校验。 - pos = seg_off8 - if !ccr_has_bytes(pos, 4, seg_end8): + // === REG: region(24B each,spec 3.5 字段序 {kind, parent, enter_nod, + // exit_nod, first_ent, last_ent})=== + // nstart and ncount are not stored; the loader derives nstart = enter and + // ncount = exit - enter. A record with exit < enter is unclosed and has no + // valid representation, so the file is rejected. + // first/last 校验:SG_FUNC 根记录(记录顺序第 k 个 = 函数 k)的区内条目范围必须 + // 与 SYM func 记录 first/last 一致(同信息双写,失配 = 格式不一致)。 + // 函数指令边界重建:func root_region(SYM)-> REG span = [enter, exit)。 + if !ccr_has_bytes(pos, 4, seg_end7): return -1 - edg_cnt := buf_read_u32(data, pos) + sg_n := buf_read_u32(data, pos) pos = pos + 4 - if edg_cnt > (seg_end8 - seg_off8) / CCR_V10_EDG_DISK: + if sg_n > (seg_end7 - seg_off7) / CCR_V10_REG_DISK: return -1 - v7_buf : string, mut = "" - if edg_cnt > 0: - v7_buf = alloc(edg_cnt * 8) - g_v7_edges = v7_buf - g_v7_edge_count = edg_cnt - ei4 : ., mut = 0 - run_off : ., mut = 0 + sg_i : ., mut = 0 + rfcnt : ., mut = 0 // 已见 SG_FUNC 根记录数(记录顺序 = 函数序 1:1) loop: - if ei4 >= instr_cnt: + if sg_i >= sg_n: break - // 邻接域读内核对象缓冲 g_v7_nod_meta(原 nod_edge_meta loader 局部, + grow_sg(sg_i + 1) + f := sg_i * ESZ_SG + rk := buf_read_i32(data, pos) + pos = pos + 4 + rp := buf_read_i32(data, pos) + pos = pos + 4 + ren := buf_read_i32(data, pos) + pos = pos + 4 + rex := buf_read_i32(data, pos) + pos = pos + 4 + rfe := buf_read_i32(data, pos) + pos = pos + 4 + rle := buf_read_i32(data, pos) + pos = pos + 4 + if rex < ren: + return -1 + // 未闭合 -> ncount 不可派生 + if rfe < -1 || rle < -1 || (rfe == -1) != (rle == -1): + return -1 + cell_store(g_sgs, f + OFF_SG_KIND, rk) + cell_store(g_sgs, f + OFF_SG_ENTER, ren) + cell_store(g_sgs, f + OFF_SG_EXIT, rex) + cell_store(g_sgs, f + OFF_SG_PARENT, rp) + cell_store(g_sgs, f + OFF_SG_NSTART, ren) + cell_store(g_sgs, f + OFF_SG_NCOUNT, rex - ren) + if rk == SG_FUNC: + if rfcnt >= func_cnt: + return -1 + // 根记录多于函数记录 + if rfe != cell_load(fn_meta, rfcnt * 24 + 8) || rle != cell_load(fn_meta, rfcnt * 24 + 16): + return -1 // REG 根记录条目范围 != SYM func 记录 + rfcnt = rfcnt + 1 + sg_i = sg_i + 1 + if rfcnt != func_cnt: + return -1 + // 根记录少于函数记录(每函数必有根) + g_sg_count = sg_n + + // 函数指令边界回填:instr_start = root_region enter, instr_count = exit − enter + // The root region span is the function node range and is the single source for + // function instruction bounds. + bfi : ., mut = 0 + loop: + if bfi >= func_cnt: + break + rid := cell_load(fn_meta, bfi * 24) + if rid < 0 || rid >= sg_n: + return -1 + fr := rid * ESZ_SG + if cell_load(g_sgs, fr + OFF_SG_KIND) != SG_FUNC: + return -1 + ren := cell_load(g_sgs, fr + OFF_SG_ENTER) + rex := cell_load(g_sgs, fr + OFF_SG_EXIT) + if rex < ren: + return -1 + cell_store(g_ir_func_instr_start, bfi * 8, ren) + cell_store(g_ir_func_instr_count, bfi * 8, rex - ren) + bfi = bfi + 1 + + // REG root span upper-bound check. Function instruction bounds (root_region + // span) are backfilled above, and instr_cnt is already known because NOD is + // consumed before REG. Only rex >= ren was checked per record, not the upper + // bound. An out-of-range span would make ELF emission read outside the NOD + // space. For each function, check instr_start + count <= instr_cnt and reject + // negative values. This is the last reject point before payload consumption. + rfi : ., mut = 0 + loop: + if rfi >= func_cnt: + break + ris := cell_load(g_ir_func_instr_start, rfi * 8) + ric := cell_load(g_ir_func_instr_count, rfi * 8) + if ris < 0 || ric < 0 || ris + ric > instr_cnt: + return -1 + rfi = rfi + 1 + if pos != seg_end7: + return -1 + + // === EDG: edges(v7 必落;8B each {to_nod u32, kind u32},所属节点 = + // 邻接隐含:节点 i 出边连续段 [first_edge, first_edge+edge_count),first_edge + // = 前缀累计(spec 3.3 邻接约定)。校验(spec 4 + 检查强度保持): + // 1 连续段与 NOD 邻接域逐节点对照(first_edge == 前缀累计;段界不出 EDG) + // 2 Σ edge_count == edg_count(规则 3;行走完 == 段体大小) + // 3 每条边 to_nod < instr_cnt(NOD 引用界内)+ to_nod > 所属节点 + // (规则 1 拓扑不变量,数据/state 边前向;自环/后向 = 文件损坏拒绝) + // 4 kind <= 1(0=数据、1=state;2+ 预留 = 未知边类拒绝) + // 校验通过后边表入 g_v7_edges 缓冲(文件序连续段,图语义消费通道/调试)。 + // branch/jump 目标 = 操作数引用非边(v7 4 边界声明),NOD 操作数不参与 + // 本段校验。 + if !ccr_has_bytes(pos, 4, seg_end8): + return -1 + edg_cnt := buf_read_u32(data, pos) + pos = pos + 4 + if edg_cnt > (seg_end8 - seg_off8) / CCR_V10_EDG_DISK: + return -1 + v7_buf : string, mut = "" + if edg_cnt > 0: + v7_buf = alloc(edg_cnt * 8) + g_v7_edges = v7_buf + g_v7_edge_count = edg_cnt + ei4 : ., mut = 0 + run_off : ., mut = 0 + loop: + if ei4 >= instr_cnt: + break + // 邻接域读内核对象缓冲 g_v7_nod_meta(原 nod_edge_meta loader 局部, // 对象 = 内核持有物,EDG 检查 // 与对象数据 nod_edge_first/count 同源;检查逻辑零改动) nfe := cell_load(g_v7_nod_meta, ei4 * ESZ_NOD_META) @@ -2004,326 +2328,9 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 // 2 行走完 == 段体大小 - // === TYPE(7):类型信息=== - // 字节:row_count -> 记录表(24B/条)-> term_count -> 项 DAG(40B/条;哈希 - // 不落盘、本处 tt_hash5 重算)。重建 = g_types 行 + g_type_terms 项 + - // g_tt_index 索引重放(grow_tt_index -> tt_reindex)。 - // 逐条校验(违规 = 拒绝,不得当作空表或截断表,判定约定 C.5-3): - // 1 计数/长度自洽(计数 × 记录尺寸 <= 段余量);2 段体恰两小节(行走完 - // == seg_end2,无尾随字节);3 tag in 0..10(TT_*)+ a..d >= -1;4 子项引用 - // -1 或 < 自身行号(拓扑无环;标注槽按 tag 分派跳过,字段表见 - // ccr_type_term_ref_ok,ATOM 的 b = 自类型表记录编号可达数千,不得误拒)。 - // 复位:记录表/项层(含引擎预算/memo/lits,见 tt_layer_reset 注记)全部作废 - // 重建(旧行号语义的缓存一并清;记录表大小随段内容,此处为唯一复位点)。 - // corearch 侧无桥接/checker 层:记录表 = 数据(消费者 = --dump-types 通道 + - // 未来判定原语),本处不做语义解释。 - g_type_count = 0 - tt_layer_reset() - pos = seg_off2 - if !ccr_has_bytes(pos, 4, seg_end2): - return -1 - trow_cnt := buf_read_u32(data, pos) - pos = pos + 4 - if trow_cnt > (seg_end2 - pos) / ESZ_TYPE_ROW: - return -1 - grow_types(trow_cnt) - trow : ., mut = 0 - loop: - if trow >= trow_cnt: - break - if !ccr_has_bytes(pos, ESZ_TYPE_ROW, seg_end2): - return -1 - cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_KIND, buf_read_i64(data, pos)) - pos = pos + 8 - cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_DATA, buf_read_i64(data, pos)) - pos = pos + 8 - cell_store(g_types, trow * ESZ_TYPE_ROW + OFF_TR_EXTRA, buf_read_i64(data, pos)) - pos = pos + 8 - trow = trow + 1 - g_type_count = trow_cnt - if !ccr_has_bytes(pos, 4, seg_end2): - return -1 - tterm_cnt := buf_read_u32(data, pos) - pos = pos + 4 - if tterm_cnt > (seg_end2 - pos) / ESZ_TYPE_TERM_DISK: - return -1 - grow_type_terms(tterm_cnt + 1) - tj : ., mut = 0 - loop: - if tj >= tterm_cnt: - break - if !ccr_has_bytes(pos, ESZ_TYPE_TERM_DISK, seg_end2): - return -1 - ttag := buf_read_i64(data, pos) - pos = pos + 8 - ta := buf_read_i64(data, pos) - pos = pos + 8 - tb := buf_read_i64(data, pos) - pos = pos + 8 - tc2 := buf_read_i64(data, pos) - pos = pos + 8 - td := buf_read_i64(data, pos) - pos = pos + 8 - if ttag < 0 || ttag > TT_CONS: - return -1 - if ta < -1 || tb < -1 || tc2 < -1 || td < -1: - return -1 - if ccr_type_term_ref_ok(ttag, ta, tb, tc2, td, tj) == 0: - return -1 - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_TAG, ttag) - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_A, ta) - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_B, tb) - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_C, tc2) - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_D, td) - cell_store(g_type_terms, tj * ESZ_TYPE_TERM + OFF_TT_HASH, tt_hash5(ttag, ta, tb, tc2, td)) - g_type_term_count = tj + 1 - tj = tj + 1 - if pos != seg_end2: - return -1 - // 段体恰两小节(无尾随字节) - grow_tt_index(g_type_term_count + 1) // 索引重建(cap 已清零 -> 必走重建) - - // === IFACE(8):接口数据=== - // 重建 = g_iface_entries(条目表;并置 g_iface_registry_ok = 1,段 = corearch - // 侧真源,防常量表覆盖段内容)+ g_iface_shape_names/terms + g_ifaces - // (ESZ_IFACEINFO 布局;corearch 侧无 AST,因此节点槽恒 -1、项槽 = 段值) - // + g_impl_for + g_methods。 - // 逐条校验(违规 = 拒绝;判定约定 C.5-3,不得当作空表或截断表): - // 1 五小节计数/长度自洽(计数 × 记录尺寸 <= 段余量)+ 行走完 == seg_end3(无尾随); - // 2 native_count == IFACE_ENTRY_COUNT(表内容 = 常量表,计数漂移 = 损坏); - // 3 跨段引用域:name_ni/type_ni/method_ni/mangled_ni in [0, g_str_count)(STR - // 段先于本段解析);ti_row in {-1} ∪ [0, g_type_count);term in {-1} ∪ [0, tt_count()) - // (TYPE(7) 段先于本段解析,因此两域已建立); - // 4 结构域:ak in 0..AK_NULL、method_count <= MAX_IFACE_METHODS、 - // param_count <= MAX_IFACE_METHOD_PARAMS、self_mode in 0..3。 - // IFACE 段 = 纯信息(不参与 ELF 发射);本段解析失败 = 整体拒绝(load_ccr -1)。 - pos = seg_off3 - if !ccr_has_bytes(pos, 4, seg_end3): - return -1 - ifc_nat := buf_read_u32(data, pos) - pos = pos + 4 - if ifc_nat != IFACE_ENTRY_COUNT: - return -1 - if !ccr_has_bytes(pos, ifc_nat * 24, seg_end3): - return -1 - g_iface_entries = alloc(ifc_nat * ESZ_IFACE_ENTRY) - ifr : ., mut = 0 - loop: - if ifr >= ifc_nat: - break - fak := buf_read_i32(data, pos) - pos = pos + 4 - fti := buf_read_i32(data, pos) - pos = pos + 4 - fname := buf_read_i32(data, pos) - pos = pos + 4 - flit := buf_read_i32(data, pos) - pos = pos + 4 - fops := buf_read_i64(data, pos) - pos = pos + 8 - if fak < 0 || fak > AK_NULL: - return -1 - // 原子类域 - if fti < -1 || fti >= g_type_count: - return -1 - // 类型行引用域(跨段) - if fname < 0 || fname >= g_str_count: - return -1 - // 名字引用域(跨段) - if flit < -1: - return -1 - fo2 := ifr * ESZ_IFACE_ENTRY - cell_store(g_iface_entries, fo2 + OFF_IE_AK, fak) - cell_store(g_iface_entries, fo2 + OFF_IE_TI, fti) - cell_store(g_iface_entries, fo2 + OFF_IE_NAME, fname) - cell_store(g_iface_entries, fo2 + OFF_IE_LIT, flit) - cell_store(g_iface_entries, fo2 + OFF_IE_OPS, fops) - ifr = ifr + 1 - g_iface_entry_count = ifc_nat - g_iface_registry_ok = 1 - - // 2 横切形状(名 ni + 形状项) - if !ccr_has_bytes(pos, 4, seg_end3): - return -1 - ifc_shn := buf_read_u32(data, pos) - pos = pos + 4 - if ifc_shn > (seg_end3 - pos) / 8: - return -1 - iface_shape_grow(ifc_shn) - ifs : ., mut = 0 - loop: - if ifs >= ifc_shn: - break - sname := buf_read_i32(data, pos) - pos = pos + 4 - sterm := buf_read_i32(data, pos) - pos = pos + 4 - if sname < 0 || sname >= g_str_count: - return -1 - if sterm < 0 || sterm >= tt_count(): - return -1 - // 项引用域(跨段) - cell_store(g_iface_shape_names, ifs * 8, sname) - cell_store(g_iface_shape_terms, ifs * 8, sterm) - ifs = ifs + 1 - g_iface_shape_count = ifc_shn - - // 3 用户接口(头 16B + 方法 80B × method_count) - if !ccr_has_bytes(pos, 4, seg_end3): - return -1 - ifc_usn := buf_read_u32(data, pos) - pos = pos + 4 - if ifc_usn > (seg_end3 - pos) / 16: - return -1 - grow_ifaces(ifc_usn) - ifu : ., mut = 0 - loop: - if ifu >= ifc_usn: - break - if !ccr_has_bytes(pos, 16, seg_end3): - return -1 - uname := buf_read_i32(data, pos) - pos = pos + 4 - umc := buf_read_i32(data, pos) - pos = pos + 4 - ugc := buf_read_i32(data, pos) - pos = pos + 4 - pos = pos + 4 // pad(预留) - if uname < 0 || uname >= g_str_count: - return -1 - if umc < 0 || umc > MAX_IFACE_METHODS: - return -1 - if ugc < 0: - return -1 - if !ccr_has_bytes(pos, umc * 80, seg_end3): - return -1 - ub := ifu * ESZ_IFACEINFO - // 先清零整条记录(防上轮残留;节点槽/项槽的初值 = -1 由下方逐槽写) - uz : ., mut = 0 - loop: - if uz >= ESZ_IFACEINFO: - break - cell_store8(g_ifaces, ub + uz, 0) - uz = uz + 1 - cell_store(g_ifaces, ub + OFF_IF_NAME, uname) - cell_store(g_ifaces, ub + OFF_IF_METHOD_COUNT, umc) - cell_store(g_ifaces, ub + OFF_IF_GENERIC_COUNT, ugc) - ium : ., mut = 0 - loop: - if ium >= umc: - break - mb := ub + OFF_IF_METHODS + ium * ESZ_IFMETHOD - mname := buf_read_i32(data, pos) - pos = pos + 4 - mpc := buf_read_i32(data, pos) - pos = pos + 4 - msm := buf_read_i32(data, pos) - pos = pos + 4 - mrt := buf_read_i32(data, pos) - pos = pos + 4 - if mname < 0 || mname >= g_str_count: - return -1 - if mpc < 0 || mpc > MAX_IFACE_METHOD_PARAMS: - return -1 - if msm < 0 || msm > 3: - return -1 - if mrt < -1 || mrt >= tt_count(): - return -1 - // 项引用域(跨段) - cell_store(g_ifaces, mb + OFF_IFM_NAME, mname) - cell_store(g_ifaces, mb + OFF_IFM_PARAM_COUNT, mpc) - cell_store(g_ifaces, mb + OFF_IFM_SELF_MODE, msm) - cell_store(g_ifaces, mb + OFF_IFM_RET_TERM, mrt) - cell_store(g_ifaces, mb + OFF_IFM_RET_NODE, -1) // corearch 无 AST,因此节点槽恒 -1 - mq : ., mut = 0 - loop: - if mq >= MAX_IFACE_METHOD_PARAMS: - break - mt := buf_read_i32(data, pos) - pos = pos + 4 - if mt < -1 || mt >= tt_count(): - return -1 - // 项引用域(跨段) - cell_store(g_ifaces, mb + OFF_IFM_PARAM_TERMS + mq * 8, mt) - cell_store(g_ifaces, mb + OFF_IFM_PARAM_NODES + mq * 8, -1) // 节点槽恒 -1 - mq = mq + 1 - mp : ., mut = 0 - loop: - if mp >= MAX_IFACE_METHOD_PARAMS: - break - mc2 := buf_read_i32(data, pos) - pos = pos + 4 // 裸码(信息) - if mc2 < -1: - return -1 - cell_store(g_ifaces, mb + OFF_IFM_PARAM_TYPES + mp * 8, mc2) - mp = mp + 1 - ium = ium + 1 - ifu = ifu + 1 - g_iface_count = ifc_usn - - // 4 impl 边(g_impl_for:{trait_ni, type_ni},声明元数据) - if !ccr_has_bytes(pos, 4, seg_end3): - return -1 - ifc_imp := buf_read_u32(data, pos) - pos = pos + 4 - if ifc_imp > (seg_end3 - pos) / 8: - return -1 - grow_impl_for(ifc_imp) - ifi : ., mut = 0 - loop: - if ifi >= ifc_imp: - break - itr := buf_read_i32(data, pos) - pos = pos + 4 - ity := buf_read_i32(data, pos) - pos = pos + 4 - if itr < 0 || itr >= g_str_count: - return -1 - if ity < 0 || ity >= g_str_count: - return -1 - cell_store(g_impl_for, ifi * 16, itr) - cell_store(g_impl_for, ifi * 16 + 8, ity) - ifi = ifi + 1 - g_impl_for_count = ifc_imp - - // 5 方法表(g_methods:{type_ni, method_ni, mangled_ni},iface_find_method 数据源) - if !ccr_has_bytes(pos, 4, seg_end3): - return -1 - ifc_met := buf_read_u32(data, pos) - pos = pos + 4 - if ifc_met > (seg_end3 - pos) / 12: - return -1 - grow_methods(ifc_met) - ifm : ., mut = 0 - loop: - if ifm >= ifc_met: - break - mtn := buf_read_i32(data, pos) - pos = pos + 4 - mmn := buf_read_i32(data, pos) - pos = pos + 4 - mmg := buf_read_i32(data, pos) - pos = pos + 4 - if mtn < 0 || mtn >= g_str_count: - return -1 - if mmn < 0 || mmn >= g_str_count: - return -1 - if mmg < 0 || mmg >= g_str_count: - return -1 - cell_store(g_methods, ifm * 24, mtn) - cell_store(g_methods, ifm * 24 + 8, mmn) - cell_store(g_methods, ifm * 24 + 16, mmg) - ifm = ifm + 1 - g_method_count = ifc_met - - if pos != seg_end3: - return -1 - // 五小节行走完 == 段体(无尾随字节) - // === CASE: empty table for now === // The loader requires case_count == 0 until match lowering writes CASE. Any // other count is an unknown shape, so reject instead of treating it as empty. - pos = seg_off9 if !ccr_has_bytes(pos, 4, seg_end9): return -1 case_cnt := buf_read_u32(data, pos) @@ -2334,9 +2341,10 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 // === NOD type_id domain check === - // This runs after TYPE because it needs the row count. NOD is parsed before - // TYPE in dependency order, so the check cannot run there. type_id is -1 or a - // TYPE row; anything else is an unknown type domain and rejects the file. + // TYPE is consumed before NOD, so the row domain is already rebuilt. The + // check stays at the end as one summary gate over g_v7_nod_sem. type_id is + // -1 or a TYPE row; anything else is an unknown type domain and rejects the + // file. ni : ., mut = 0 loop: if ni >= g_v7_nod_count: diff --git a/tests/selfhost/test_ccr_v10_stream.py b/tests/selfhost/test_ccr_v10_stream.py new file mode 100644 index 00000000..27730471 --- /dev/null +++ b/tests/selfhost/test_ccr_v10_stream.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 +"""Task 6: the v10 loader consumes segment bodies in canonical order, forward only. + +Wire contract (docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md §3): + + STR -> TYPE -> IFACE -> SYM -> NOD -> ENT -> REG -> EDG -> CASE + +The segment table may validate boundaries and contiguity, but the parser cursor +starts at the first body and only moves forward. There is no second +random-access entry point in this repository: `load_ccr` in src/compiler/ccr_io.cr +is the only .ccr reader (corearch and the ELF main both call it). + +The negative fixtures below are built from a real `.ccr` emitted by `corec`: + + 1. canonical image loads and the emitted program still runs; + 2. bodies physically permuted to the legacy dependency order are rejected; + 3. a body truncated while the segment table stays contiguous is rejected; + 4. offsets that are a contiguous partition but place bodies in legacy order + are rejected (the table is not the parse order). + +Runs standalone and is also pytest-discoverable (stdlib only). +""" + +import re +import struct +import subprocess +import tempfile +from pathlib import Path + +from ccr_v10_model import ( + BODY_START, + MAGIC, + SEG_COUNT, + SEG_TAGS, + VERSION, + encode, + read_segment_table, +) + +BASE = Path(__file__).resolve().parent.parent.parent +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" +CCR_IO = BASE / "src" / "compiler" / "ccr_io.cr" + +CANONICAL_ORDER = ("STR", "TYPE", "IFACE", "SYM", "NOD", "ENT", "REG", "EDG", "CASE") +# Pre-v10 loader dependency order (the order this task removes). +LEGACY_ORDER = ("STR", "SYM", "REG", "NOD", "ENT", "EDG", "TYPE", "IFACE", "CASE") + +TAG_OF = {name: tag for tag, name in SEG_TAGS.items()} + +# Exercises globals, function calls, arrays, and pointer ops so every segment +# body is non-trivial and distinguishable. +SOURCE = """fn add(a: int, b: int) -> int: + return a + b + +fn main() -> int: + arr : ., mut = [10, 20, 30] + i := 1 + arr[i] = 9 + p := &arr[1] + *p = 11 + return add(*p + arr[0] + arr[2], 0) +""" + +EXPECTED_EXIT = 51 # add(11 + 10 + 30, 0) + + +# --------------------------------------------------------------------------- +# helpers +# --------------------------------------------------------------------------- + + +def _run(cmd, timeout=180): + return subprocess.run( + [str(part) for part in cmd], + capture_output=True, + text=True, + cwd=str(BASE), + timeout=timeout, + ) + + +def _build_ccr(tmp): + src = tmp / "stream_probe.cr" + ccr = tmp / "stream_probe.ccr" + src.write_text(SOURCE, encoding="utf-8") + r = _run([COREC, "ccr", src, "-o", ccr]) + assert r.returncode == 0, f"corec ccr failed: {r.stdout}\n{r.stderr}" + return ccr.read_bytes() + + +def _split(data): + bodies = {} + for tag, offset, size in read_segment_table(data): + bodies[tag] = data[offset : offset + size] + return bodies + + +def _elf_result(data, tmp, name): + ccr = tmp / f"{name}.ccr" + out = tmp / f"{name}.elf" + ccr.write_bytes(data) + return _run([COREARCH, ccr, "--elf", "--static", "-o", out]), out + + +def _assert_rejected(data, tmp, name): + r, _out = _elf_result(data, tmp, name) + assert r.returncode != 0, f"{name}: corearch accepted a non-canonical image" + assert "invalid" in (r.stdout + r.stderr), f"{name}: missing diagnostic: {r.stdout}{r.stderr}" + + +def _bodies_in_physical_order(data, order): + """Re-emit the image with bodies placed in `order`, table tags canonical. + + `encode` always writes table entries 1..9 with ascending contiguous + offsets, so the result has a canonical table and permuted body content. + """ + bodies = _split(data) + # Physical slot i (table tag i+1) carries the body of the i-th name in + # `order`; sizes follow the body, so the table stays contiguous. + return encode({tag: bodies[TAG_OF[order[tag - 1]]] for tag in SEG_TAGS}) + + +def _truncated_nod(data, cut=4): + bodies = _split(data) + assert len(bodies[TAG_OF["NOD"]]) > cut + 40 + bodies[TAG_OF["NOD"]] = bodies[TAG_OF["NOD"]][:-cut] + return encode(bodies) + + +def _permuted_offset_image(data): + """Legacy body placement with a contiguous partition of offsets. + + Tags stay canonical (1..9), every size is truthful, and the offset set + covers [124, EOF) with no gap or overlap, but offsets are assigned by body + location instead of table order. This is the "structurally valid table, + old body order" shape: a random-access reader could follow it, a forward + cursor cannot. + """ + bodies = _split(data) + placed = [] + offset = BODY_START + offsets = {} + for name in LEGACY_ORDER: + tag = TAG_OF[name] + offsets[tag] = offset + placed.append(bodies[tag]) + offset += len(bodies[tag]) + + header = struct.pack(" ".join(order) + ) + assert _BACKWARD_SEEK.search(body) is None, ( + "load_ccr still seeks to a segment-table offset: " + + repr(_BACKWARD_SEEK.search(body).group(0)) + ) + + +# --------------------------------------------------------------------------- +# 2. canonical file loads and behaves exactly as before +# --------------------------------------------------------------------------- + + +def test_canonical_file_loads(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_ccr(tmp) + r, out = _elf_result(data, tmp, "canonical") + assert r.returncode == 0, f"canonical v10 rejected: {r.stdout}\n{r.stderr}" + assert out.exists(), "corearch produced no ELF" + + run = subprocess.run([str(out)], capture_output=True, text=True, timeout=10) + assert run.returncode == EXPECTED_EXIT, ( + f"canonical ELF returned {run.returncode}, expected {EXPECTED_EXIT}" + ) + + +# --------------------------------------------------------------------------- +# 3. non-canonical body order rejected +# --------------------------------------------------------------------------- + + +def test_legacy_body_order_rejected(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_ccr(tmp) + bad = _bodies_in_physical_order(data, LEGACY_ORDER) + assert bad != data, "fixture did not permute any body" + table = read_segment_table(bad) + assert [tag for tag, _o, _s in table] == list(SEG_TAGS) + expected = BODY_START + for _tag, offset, size in table: + assert offset == expected, "fixture table is not contiguous" + expected += size + assert expected == len(bad) + _assert_rejected(bad, tmp, "legacy_order") + + +# --------------------------------------------------------------------------- +# 4. truncated segment body rejected (table stays contiguous) +# --------------------------------------------------------------------------- + + +def test_truncated_segment_body_rejected(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_ccr(tmp) + bad = _truncated_nod(data) + table = read_segment_table(bad) + expected = BODY_START + for _tag, offset, size in table: + assert offset == expected, "fixture table is not contiguous" + expected += size + assert expected == len(bad) + _assert_rejected(bad, tmp, "truncated_nod") + + +# --------------------------------------------------------------------------- +# 5. contiguous offsets but legacy body order rejected +# --------------------------------------------------------------------------- + + +def test_contiguous_offsets_with_legacy_body_order_rejected(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_ccr(tmp) + bad = _permuted_offset_image(data) + + table = read_segment_table(bad) + assert [tag for tag, _o, _s in table] == list(SEG_TAGS) + # Offsets are a contiguous partition of the body area, just not in + # table order: a tag->offset random reader could walk it. + spans = sorted((offset, offset + size) for _tag, offset, size in table) + assert spans[0][0] == BODY_START + for (start, end), (next_start, _next_end) in zip(spans, spans[1:]): + assert start < end + assert end == next_start, "fixture offsets have a gap/overlap" + assert spans[-1][1] == len(bad) + + _assert_rejected(bad, tmp, "permuted_offsets") + + +# --------------------------------------------------------------------------- +# standalone runner +# --------------------------------------------------------------------------- + + +def main(): + tests = [ + value + for name, value in sorted(globals().items()) + if name.startswith("test_") and callable(value) + ] + failed = 0 + for test in tests: + try: + test() + except Exception as exc: # noqa: BLE001 - report every failure shape + failed += 1 + print(f"FAIL {test.__name__}: {exc!r}") + else: + print(f"PASS {test.__name__}") + print(f"{len(tests) - failed}/{len(tests)} passed") + return 1 if failed else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 1b8fd153d01fcbda426c1744183e467123692b5a Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 02:05:31 +0800 Subject: [PATCH 51/58] ccr: harden streaming loader test evidence --- src/compiler/ccr_io.cr | 4 +- tests/selfhost/test_ccr_v10_stream.py | 188 ++++++++++++++++++++------ 2 files changed, 152 insertions(+), 40 deletions(-) diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 1eff922f..9670f903 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -2000,6 +2000,8 @@ fn load_ccr(data: string, fsize: int) -> int: // g_ir_instr_count 本段不再触碰(重建段原写点全清)。 if !ccr_has_bytes(pos, 4, seg_end5): return -1 + // instr_cnt consumers: ENT (live_end domain), REG (root span upper bound), + // EDG (to_nod domain). It stays a local until those segments are consumed. instr_cnt := buf_read_u32(data, pos) pos = pos + 4 if instr_cnt > (seg_end5 - seg_off5) / CCR_V10_NOD_DISK: @@ -2247,7 +2249,7 @@ fn load_ccr(data: string, fsize: int) -> int: // consumed before REG. Only rex >= ren was checked per record, not the upper // bound. An out-of-range span would make ELF emission read outside the NOD // space. For each function, check instr_start + count <= instr_cnt and reject - // negative values. This is the last reject point before payload consumption. + // negative values. EDG and CASE are still consumed after this gate. rfi : ., mut = 0 loop: if rfi >= func_cnt: diff --git a/tests/selfhost/test_ccr_v10_stream.py b/tests/selfhost/test_ccr_v10_stream.py index 27730471..2f5e7516 100644 --- a/tests/selfhost/test_ccr_v10_stream.py +++ b/tests/selfhost/test_ccr_v10_stream.py @@ -10,13 +10,25 @@ random-access entry point in this repository: `load_ccr` in src/compiler/ccr_io.cr is the only .ccr reader (corearch and the ELF main both call it). -The negative fixtures below are built from a real `.ccr` emitted by `corec`: +The negative fixtures below are built from a real `.ccr` emitted by `corec`, but +they test different layers: 1. canonical image loads and the emitted program still runs; - 2. bodies physically permuted to the legacy dependency order are rejected; - 3. a body truncated while the segment table stays contiguous is rejected; - 4. offsets that are a contiguous partition but place bodies in legacy order - are rejected (the table is not the parse order). + 2. a wire-valid table whose slot contents are permuted is rejected during body + parsing (tag/content mismatch); + 3. a body truncated while the segment table stays contiguous is rejected during + body parsing; + 4. offsets that are a contiguous partition but not in table order are rejected + by the pre-existing table monotonicity check, before any body is parsed. + +None of 2-4 can distinguish a random-access reader from a forward-only reader. +Under the v10 table rule (tags in canonical order, offsets contiguous and +ascending) the physical body order is forced to be canonical, so an image that +preserves the table rule cannot express legacy body order at all. A permuted +table bypasses the rule but is rejected before parsing. The discriminating gate +is therefore structural: `test_loader_parse_structure_is_canonical_forward_only` +checks the actual cursor reads (segment-end order, every `pos` assignment +advances, and every read uses the cursor), not comments. Runs standalone and is also pytest-discoverable (stdlib only). """ @@ -29,12 +41,14 @@ from ccr_v10_model import ( BODY_START, + CcrV10Error, MAGIC, SEG_COUNT, SEG_TAGS, VERSION, encode, read_segment_table, + validate, ) BASE = Path(__file__).resolve().parent.parent.parent @@ -103,17 +117,38 @@ def _elf_result(data, tmp, name): return _run([COREARCH, ccr, "--elf", "--static", "-o", out]), out -def _assert_rejected(data, tmp, name): +def _assert_rejected(data, tmp, name, wire_valid): + """Assert rejection at the expected layer. + + `wire_valid` means the v10 table itself is well formed (canonical tags, + contiguous ascending offsets, exact file length), so the rejection must come + from body parsing. Otherwise the wire table check must reject before parse. + """ + if wire_valid: + validate(data) + else: + try: + validate(data) + except CcrV10Error: + pass + else: + raise AssertionError(f"{name}: expected the wire table check to reject") + r, _out = _elf_result(data, tmp, name) + out = r.stdout + r.stderr assert r.returncode != 0, f"{name}: corearch accepted a non-canonical image" - assert "invalid" in (r.stdout + r.stderr), f"{name}: missing diagnostic: {r.stdout}{r.stderr}" + # corearch prints this exact text when load_ccr returns -1; the size gate + # prints a different message and would mean the fixture never reached it. + assert "error: invalid .ccr file" in out, f"{name}: missing loader diagnostic: {out}" -def _bodies_in_physical_order(data, order): - """Re-emit the image with bodies placed in `order`, table tags canonical. +def _tag_content_mismatch_image(data, order): + """Wire-valid table, slot contents permuted to `order`. - `encode` always writes table entries 1..9 with ascending contiguous - offsets, so the result has a canonical table and permuted body content. + The table says slot 2 is TYPE but the bytes there are another segment body. + This is a tag/content mismatch: a random-access reader that seeks to the + slot also rejects it. It is retained as structural validation, not as proof + of forward consumption. """ bodies = _split(data) # Physical slot i (table tag i+1) carries the body of the i-th name in @@ -129,13 +164,14 @@ def _truncated_nod(data, cut=4): def _permuted_offset_image(data): - """Legacy body placement with a contiguous partition of offsets. + """Legacy body placement with offsets that form a contiguous partition. Tags stay canonical (1..9), every size is truthful, and the offset set - covers [124, EOF) with no gap or overlap, but offsets are assigned by body - location instead of table order. This is the "structurally valid table, - old body order" shape: a random-access reader could follow it, a forward - cursor cannot. + covers [124, EOF) with no gap or overlap, but offsets are not ascending in + table order. The loader's pre-existing `soff != cursor` check rejects this + image before any body is parsed, so it is not evidence for the streaming + cursor. Under the v10 table rule an ascending table forces canonical body + order, so no wire-valid legacy-order image exists. """ bodies = _split(data) placed = [] @@ -157,10 +193,18 @@ def _permuted_offset_image(data): _FN_START = re.compile(r"^fn load_ccr\(", re.MULTILINE) _NEXT_FN = re.compile(r"^fn ", re.MULTILINE) -_SECTION = re.compile( - r"^\s*// === (STR|TYPE|IFACE|SYM|NOD|ENT|REG|EDG|CASE)(?:\(|:)", re.MULTILINE +# A body read bound at segment N: ccr_has_bytes(pos, ..., seg_endN). +_BODY_CHECK = re.compile(r"ccr_has_bytes\(\s*pos\s*,[^)]*?seg_end([1-9])\)") +_POS_ASSIGN = re.compile(r"^\s*pos = (.+)$", re.MULTILINE) +_READ_POS = re.compile( + r"\b(?:buf_read_u32|buf_read_i32|buf_read_i64|cell_load_byte)\(data,\s*([^)\n]+)\)" ) -_BACKWARD_SEEK = re.compile(r"^\s*pos = seg_off\d", re.MULTILINE) +_CCR_HAS_POS = re.compile(r"ccr_has_bytes\(\s*([^,\n]+),") +_TEMP_FROM_BOUND = re.compile( + r"^\s*(?!pos\b)([A-Za-z_]\w*)\s*(?::[^=\n]+)?=\s*seg_(?:off|end)[1-9]\b", + re.MULTILINE, +) +_COMMENT = re.compile(r"//.*$") def _load_ccr_source(): @@ -174,22 +218,72 @@ def _load_ccr_source(): # --------------------------------------------------------------------------- -# 1. parse order is the canonical order, forward only (source evidence) +# 1. parse structure: canonical segment-end order, one forward cursor # --------------------------------------------------------------------------- -def test_loader_parses_canonical_order_forward_only(): +def test_loader_parse_structure_is_canonical_forward_only(): body = _load_ccr_source() - order = [] - for name in _SECTION.findall(body): - if not order or order[-1] != name: - order.append(name) - assert order == list(CANONICAL_ORDER), ( - "load_ccr parse order is not canonical: " + " -> ".join(order) + + # 1. The first body read bound at each seg_endN must appear in canonical + # order. This inspects real ccr_has_bytes guards, not comments. + first_seen = {} + for match in _BODY_CHECK.finditer(body): + first_seen.setdefault(int(match.group(1)), match.start()) + assert sorted(first_seen) == list(range(1, 10)), ( + f"load_ccr does not guard all nine segment bodies: {sorted(first_seen)}" ) - assert _BACKWARD_SEEK.search(body) is None, ( - "load_ccr still seeks to a segment-table offset: " - + repr(_BACKWARD_SEEK.search(body).group(0)) + read_order = [ + seg for seg, _pos in sorted(first_seen.items(), key=lambda item: item[1]) + ] + assert read_order == list(range(1, 10)), ( + "load_ccr first reads segment ends out of canonical order: " + + " -> ".join(f"seg_end{n}" for n in read_order) + ) + + # 2. Every cursor assignment must advance from the cursor itself. The only + # non-advancing value allowed is an initial zero before the first body; + # the current function declares `pos : ., mut = 0` and has no such + # assignment, so any bare `pos = 0`, `pos = seg_offN`, `pos = seg_endN`, + # or `pos = tmp` is rejected. + first_body = body.index("ccr_has_bytes(pos,") + assignments = 0 + for match in _POS_ASSIGN.finditer(body): + rhs = _COMMENT.sub("", match.group(1)).strip() + if rhs == "0" and match.start() < first_body: + continue + assert re.fullmatch(r"pos \+ .+", rhs), ( + f"load_ccr assigns pos without advancing the cursor: {rhs!r}" + ) + assignments += 1 + assert assignments > 0, "no cursor assignments found in load_ccr" + + # 3. Every byte read and bound check must use the cursor. A temporary + # variable seek would read from something other than `pos`. + for match in _READ_POS.finditer(body): + expr = _COMMENT.sub("", match.group(1)).strip() + assert expr == "pos", f"load_ccr reads from a non-cursor offset: {expr!r}" + for match in _CCR_HAS_POS.finditer(body): + expr = match.group(1).strip() + assert expr == "pos", f"load_ccr bounds a read at a non-cursor offset: {expr!r}" + + # 4. Segment-table offsets may only feed bound math of the same segment, + # never a parse position, and no temp may capture a segment bound. + for line in body.splitlines(): + stripped = line.strip() + if "seg_off" not in stripped: + continue + ok = ( + re.fullmatch(r"seg_off[1-9] : \., mut = 0", stripped) + or re.fullmatch(r"seg_off[1-9] = soff", stripped) + or re.fullmatch( + r".*\(seg_end([1-9]) - seg_off\1\) / .*", stripped + ) + ) + assert ok, f"load_ccr uses a segment-table offset outside bound math: {stripped}" + assert _TEMP_FROM_BOUND.search(body) is None, ( + "load_ccr captures a segment bound in a temporary: " + + repr(_TEMP_FROM_BOUND.search(body).group(0)) ) @@ -213,15 +307,22 @@ def test_canonical_file_loads(): # --------------------------------------------------------------------------- -# 3. non-canonical body order rejected +# 3. tag/content mismatch (wire-valid table, body parse rejects) # --------------------------------------------------------------------------- -def test_legacy_body_order_rejected(): +def test_tag_content_mismatch_rejected(): + """Slot contents are permuted, so a slot's tag disagrees with its bytes. + + The table is wire-valid (canonical tags, ascending contiguous offsets), so + rejection happens in body parsing. This is not proof of forward-only + consumption: a random-access reader that seeks to the slot sees the same + mismatch. Forward order is proven by the structural test above. + """ with tempfile.TemporaryDirectory() as td: tmp = Path(td) data = _build_ccr(tmp) - bad = _bodies_in_physical_order(data, LEGACY_ORDER) + bad = _tag_content_mismatch_image(data, LEGACY_ORDER) assert bad != data, "fixture did not permute any body" table = read_segment_table(bad) assert [tag for tag, _o, _s in table] == list(SEG_TAGS) @@ -230,7 +331,7 @@ def test_legacy_body_order_rejected(): assert offset == expected, "fixture table is not contiguous" expected += size assert expected == len(bad) - _assert_rejected(bad, tmp, "legacy_order") + _assert_rejected(bad, tmp, "tag_content_mismatch", wire_valid=True) # --------------------------------------------------------------------------- @@ -239,6 +340,7 @@ def test_legacy_body_order_rejected(): def test_truncated_segment_body_rejected(): + """Wire-valid table, short body: rejection comes from body parsing.""" with tempfile.TemporaryDirectory() as td: tmp = Path(td) data = _build_ccr(tmp) @@ -249,15 +351,21 @@ def test_truncated_segment_body_rejected(): assert offset == expected, "fixture table is not contiguous" expected += size assert expected == len(bad) - _assert_rejected(bad, tmp, "truncated_nod") + _assert_rejected(bad, tmp, "truncated_nod", wire_valid=True) # --------------------------------------------------------------------------- -# 5. contiguous offsets but legacy body order rejected +# 5. permuted table offsets (rejected by the table check before body parse) # --------------------------------------------------------------------------- -def test_contiguous_offsets_with_legacy_body_order_rejected(): +def test_permuted_offsets_rejected_before_body_parse(): + """Offsets are a contiguous partition but not ascending in table order. + + The loader's `soff != cursor` check rejects this image during table + validation, before any body is parsed. It documents why a legacy-order + image cannot also keep a valid v10 table; it is not streaming evidence. + """ with tempfile.TemporaryDirectory() as td: tmp = Path(td) data = _build_ccr(tmp) @@ -266,7 +374,7 @@ def test_contiguous_offsets_with_legacy_body_order_rejected(): table = read_segment_table(bad) assert [tag for tag, _o, _s in table] == list(SEG_TAGS) # Offsets are a contiguous partition of the body area, just not in - # table order: a tag->offset random reader could walk it. + # table order. spans = sorted((offset, offset + size) for _tag, offset, size in table) assert spans[0][0] == BODY_START for (start, end), (next_start, _next_end) in zip(spans, spans[1:]): @@ -274,7 +382,9 @@ def test_contiguous_offsets_with_legacy_body_order_rejected(): assert end == next_start, "fixture offsets have a gap/overlap" assert spans[-1][1] == len(bad) - _assert_rejected(bad, tmp, "permuted_offsets") + _assert_rejected( + bad, tmp, "permuted_offsets", wire_valid=False + ) # --------------------------------------------------------------------------- From f77d1953472835bb928f3d00727360729c13680e Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 02:07:08 +0800 Subject: [PATCH 52/58] docs: scope ccrb distribution to external packer --- .../plans/2026-10-23-ccr-v10-migration.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md index a8f0d5cd..5ff1b2fd 100644 --- a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -457,24 +457,26 @@ jj commit -m "ccr: make v10 loader consume canonical segment order" ### Task 7: `.ccrb` 分发和 `.coi` 边界 **Files:** -- Create: `src/dist/ccrb.cr`(或现有分发层中的对应文件) +- Create: `tools/ccrb.py`(分发/安装层;不进入 corearch) - Modify: `docs/maintainer/design/coi-optimization-knowledge-sidecar.md` - Test: `tests/selfhost/test_ccrb_bundle.py` **Interfaces:** - Consumes: raw v10 `.ccr`。 - Produces: - - `ccrb_pack(raw_ccr: string, coi: string) -> string`,输出 zstd(deterministic tar)。 - - `ccrb_unpack(bundle: string) -> int`,解出 `manifest`、`program.ccr`、可选 `coi/.coi`。 + - `tools/ccrb.py pack --ccr --out [--coi ]`,输出 zstd(deterministic tar)。 + - `tools/ccrb.py unpack --bundle --outdir `,解出 `manifest`、`program.ccr`、可选 `coi/.coi`。 - corearch 只接收解压后的 raw `.ccr`,不依赖 zstd。 +**Ruling:** `.ccr` v10 规格只要求分发层输出 `.ccrb`;corearch 不实现 zstd。仓库没有 Core 侧 zstd,本任务把 `.ccrb` 放在外部 `tools/ccrb.py`,使用系统 `zstd` 和确定性 tar。`src/compiler/coi_*.cr` 的完整 `.coi` 格式属于独立计划 `docs/superpowers/plans/2026-09-23-coi-implementation.md`,本任务只保证 `.coi` 是可选、不透明、可删除的旁挂 payload。 + - [ ] **Step 1: 写失败测试** -测试固定 tar 顺序和元数据;同一输入两次打包逐字节相同;删除 `.coi` 后 raw `.ccr` 字节不变;缺失 `.coi` 时加载/投影不变。 +测试固定 tar 顺序和元数据;同一输入两次打包逐字节相同;删除 `.coi` 后 raw `.ccr` 字节不变;缺失 `.coi` 时加载/投影不变;manifest 只含 bundle 版本、raw `.ccr` 哈希、可选 `.coi` 目标和哈希。 - [ ] **Step 2: 实现打包/解包** -manifest 只含 bundle 版本、raw `.ccr` 哈希、`.coi` 目标列表和哈希。`.coi` 不能包含语义、布局或正确性条件。 +用固定顺序 `manifest` -> `program.ccr` -> `coi/.coi`、固定 tar 元数据(uid/gid=0、mtime=0、mode 0644)、zstd 单帧输出。`.coi` 不解析,只作为可选不透明 payload;解包后 raw `.ccr` 必须逐字节等于输入。 - [ ] **Step 3: 运行测试** From bf74dcda580bafd95c5a8c82d9beb3502fded4b0 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 02:10:03 +0800 Subject: [PATCH 53/58] dist: add ccrb bundle with optional coi sidecar --- .../coi-optimization-knowledge-sidecar.md | 6 + tests/selfhost/test_ccrb_bundle.py | 456 ++++++++++++++++++ tools/ccrb.py | 396 +++++++++++++++ 3 files changed, 858 insertions(+) create mode 100644 tests/selfhost/test_ccrb_bundle.py create mode 100644 tools/ccrb.py diff --git a/docs/maintainer/design/coi-optimization-knowledge-sidecar.md b/docs/maintainer/design/coi-optimization-knowledge-sidecar.md index 1351ebb1..1ef3c916 100644 --- a/docs/maintainer/design/coi-optimization-knowledge-sidecar.md +++ b/docs/maintainer/design/coi-optimization-knowledge-sidecar.md @@ -10,6 +10,12 @@ 设计状态:定稿,尚未实现。`g_opt_meta` 的现状核对、格式展开、检查细节和待定事项见实现记录 `docs/superpowers/implementation/2026-09-23-coi-revision-audit.md`。 +## 分发边界:`.ccrb` + +`.ccrb` 是外部分发包装,不是新的程序格式。它由一个 zstd 帧包裹确定性 tar,固定包含 `manifest` 和 `program.ccr`,可选携带 `coi/.coi`。打包和解包由 `tools/ccrb.py` 完成;`corearch` 只读取解包后的 raw `.ccr`,不实现 zstd,也不读取 `.ccrb`。 + +在这个包装层里,`.coi` 只是可选、不透明、可删除的旁挂 payload。本文件不定义 `.coi` 记录的内容;完整 `.coi` 语义仍属于独立计划 `docs/superpowers/plans/2026-09-23-coi-implementation.md`。删除 `.coi` 条目不改变 `program.ccr` 的字节。 + ## 核心规则 删除全部 `.coi` 文件,程序仍然正确、仍然可读。删除只允许增加编译成本或降低优化质量。 diff --git a/tests/selfhost/test_ccrb_bundle.py b/tests/selfhost/test_ccrb_bundle.py new file mode 100644 index 00000000..3befe9f2 --- /dev/null +++ b/tests/selfhost/test_ccrb_bundle.py @@ -0,0 +1,456 @@ +#!/usr/bin/env python3 +"""Task 7: `.ccrb` is an external distribution wrapper around a raw v10 `.ccr`. + +The bundle payload is a deterministic PAX tar: + + manifest + program.ccr + coi/.coi (optional, opaque) + +One zstd frame wraps the tar. `corearch` never sees the bundle; it only reads +the extracted `program.ccr`, so the runtime has no zstd dependency. + +Runs standalone and is also pytest-discoverable (stdlib only). +""" + +import hashlib +import io +import json +import shutil +import subprocess +import sys +import tarfile +import tempfile +from pathlib import Path + +BASE = Path(__file__).resolve().parent.parent.parent +COREC = BASE / "build" / "corec" +COREARCH = BASE / "build" / "corearch" +CCRB = BASE / "tools" / "ccrb.py" +ZSTD = shutil.which("zstd") or "/usr/bin/zstd" + +DEFAULT_TARGET = "x86_64/linux/elf" +SOURCE = """fn main() -> int: + return 7 +""" + + +# --------------------------------------------------------------------------- +# helpers +# --------------------------------------------------------------------------- + + +def _run(cmd, timeout=180, input_bytes=None): + return subprocess.run( + [str(part) for part in cmd], + input=input_bytes, + capture_output=True, + cwd=str(BASE), + timeout=timeout, + ) + + +def _build_ccr(tmp): + src = tmp / "bundle_probe.cr" + ccr = tmp / "bundle_probe.ccr" + src.write_text(SOURCE, encoding="utf-8") + r = _run([COREC, "ccr", src, "-o", ccr]) + assert r.returncode == 0, f"corec ccr failed: {r.stdout}\n{r.stderr}" + return ccr.read_bytes() + + +def _pack(tmp, ccr_path, name, coi=None, target=None): + out = tmp / name + cmd = [sys.executable, str(CCRB), "pack", "--ccr", str(ccr_path), "--out", str(out)] + if coi is not None: + cmd += ["--coi", str(coi)] + if target is not None: + cmd += ["--target", target] + return _run(cmd), out + + +def _unpack(bundle, outdir): + return _run( + [sys.executable, str(CCRB), "unpack", "--bundle", str(bundle), "--outdir", str(outdir)] + ) + + +def _decompress(bundle): + r = subprocess.run( + [ZSTD, "-q", "-d", "--no-progress", "-c", str(bundle)], + capture_output=True, + cwd=str(BASE), + timeout=120, + ) + assert r.returncode == 0, f"zstd -d failed: {r.stdout!r}\n{r.stderr!r}" + return r.stdout + + +def _compress(payload): + r = subprocess.run( + [ZSTD, "-q", "-19", "--no-progress", "-T0", "-c"], + input=payload, + capture_output=True, + cwd=str(BASE), + timeout=120, + ) + assert r.returncode == 0, f"zstd failed: {r.stdout!r}\n{r.stderr!r}" + return r.stdout + + +def _tar_entries(payload): + with tarfile.open(fileobj=io.BytesIO(payload), mode="r:") as tf: + entries = [] + for member in tf.getmembers(): + extracted = tf.extractfile(member) + assert extracted is not None, f"no data for tar entry {member.name}" + entries.append((member, extracted.read())) + return entries + + +def _write_tar(entries): + buf = io.BytesIO() + with tarfile.open(fileobj=buf, mode="w", format=tarfile.PAX_FORMAT) as tf: + for name, data in entries: + info = tarfile.TarInfo(name) + info.size = len(data) + info.mode = 0o644 + info.uid = 0 + info.gid = 0 + info.uname = "" + info.gname = "" + info.mtime = 0 + info.pax_headers = {} + tf.addfile(info, io.BytesIO(data)) + return buf.getvalue() + + +def _bundle_entries(bundle): + return [(member.name, data) for member, data in _tar_entries(_decompress(bundle))] + + +def _rewrite_bundle(bundle, entries): + bundle.write_bytes(_compress(_write_tar(entries))) + + +def _manifest(entries): + return json.loads(entries[0][1].decode("utf-8")) + + +def _sha256(data): + return hashlib.sha256(data).hexdigest() + + +def _text(result): + return (result.stdout + result.stderr).decode("utf-8", "replace") + + +def _assert_fixed_metadata(member): + assert member.isfile(), f"{member.name}: expected a regular file" + assert member.uid == 0, f"{member.name}: uid={member.uid}" + assert member.gid == 0, f"{member.name}: gid={member.gid}" + assert member.uname == "", f"{member.name}: uname={member.uname!r}" + assert member.gname == "", f"{member.name}: gname={member.gname!r}" + assert member.mtime == 0, f"{member.name}: mtime={member.mtime}" + assert member.mode == 0o644, f"{member.name}: mode={oct(member.mode)}" + assert member.pax_headers == {}, f"{member.name}: pax_headers={member.pax_headers!r}" + + +def _assert_pax_format(payload): + # POSIX ustar header used by tarfile.PAX_FORMAT, with version "00". + assert payload[257:263] == b"ustar\x00", payload[257:263] + assert payload[263:265] == b"00", payload[263:265] + + +def _assert_corearch_loads(ccr_path, tmp, name): + elf = tmp / f"{name}.elf" + r = _run([COREARCH, str(ccr_path), "--elf", "--static", "-o", str(elf)]) + assert r.returncode == 0, f"corearch rejected raw .ccr: {r.stdout}\n{r.stderr}" + assert elf.is_file() and elf.stat().st_size > 0, "corearch produced no ELF" + + +# --------------------------------------------------------------------------- +# 1. no sidecar: deterministic bytes, fixed layout, round trip +# --------------------------------------------------------------------------- + + +def test_pack_without_coi_is_deterministic_and_fixed(): + with tempfile.TemporaryDirectory(dir=BASE) as td: + tmp = Path(td) + ccr_bytes = _build_ccr(tmp) + ccr_path = tmp / "bundle_probe.ccr" + + first, first_path = _pack(tmp, ccr_path, "plain-a.ccrb") + second, second_path = _pack(tmp, ccr_path, "plain-b.ccrb") + assert first.returncode == 0, f"pack failed: {first.stdout}\n{first.stderr}" + assert second.returncode == 0, f"pack failed: {second.stdout}\n{second.stderr}" + assert first_path.read_bytes() == second_path.read_bytes(), ( + "two packs of the same inputs are not byte-identical" + ) + + payload = _decompress(first_path) + _assert_pax_format(payload) + entries = _tar_entries(payload) + assert [member.name for member, _data in entries] == ["manifest", "program.ccr"] + for member, _data in entries: + _assert_fixed_metadata(member) + + manifest_bytes = entries[0][1] + manifest = _manifest([(member, data) for member, data in entries]) + assert set(manifest) == {"bundle_version", "format", "program"} + assert manifest["bundle_version"] == 1 + assert manifest["format"] == "ccr-v10" + assert manifest["program"] == { + "path": "program.ccr", + "sha256": _sha256(ccr_bytes), + "size": len(ccr_bytes), + } + assert entries[1][1] == ccr_bytes, "packed program.ccr differs from input" + + outdir = tmp / "plain-out" + r = _unpack(first_path, outdir) + assert r.returncode == 0, f"unpack failed: {r.stdout}\n{r.stderr}" + assert (outdir / "program.ccr").read_bytes() == ccr_bytes + assert (outdir / "manifest").read_bytes() == manifest_bytes + assert not (outdir / "coi").exists(), "plain bundle produced a coi payload" + _assert_corearch_loads(outdir / "program.ccr", tmp, "plain") + + +# --------------------------------------------------------------------------- +# 2. optional sidecar: opaque payload, unchanged program, deletable +# --------------------------------------------------------------------------- + + +def test_pack_with_coi_is_deterministic_and_sidecar_is_optional(): + with tempfile.TemporaryDirectory(dir=BASE) as td: + tmp = Path(td) + ccr_bytes = _build_ccr(tmp) + ccr_path = tmp / "bundle_probe.ccr" + coi_bytes = b"\x00COI\x01" + bytes(range(64)) + b"opaque-sidecar" + coi_path = tmp / "sidecar.coi" + coi_path.write_bytes(coi_bytes) + + first, first_path = _pack(tmp, ccr_path, "with-coi-a.ccrb", coi=coi_path) + second, second_path = _pack(tmp, ccr_path, "with-coi-b.ccrb", coi=coi_path) + assert first.returncode == 0, f"pack failed: {first.stdout}\n{first.stderr}" + assert second.returncode == 0, f"pack failed: {second.stdout}\n{second.stderr}" + assert first_path.read_bytes() == second_path.read_bytes(), ( + "two sidecar packs of the same inputs are not byte-identical" + ) + + entries = _bundle_entries(first_path) + expected_name = f"coi/{DEFAULT_TARGET}.coi" + assert [name for name, _data in entries] == ["manifest", "program.ccr", expected_name] + for member, _data in _tar_entries(_decompress(first_path)): + _assert_fixed_metadata(member) + + manifest = _manifest(entries) + assert set(manifest) == {"bundle_version", "format", "program", "coi"} + assert manifest["coi"] == { + "target": DEFAULT_TARGET, + "path": expected_name, + "sha256": _sha256(coi_bytes), + "size": len(coi_bytes), + } + assert entries[1][1] == ccr_bytes + assert entries[2][1] == coi_bytes + + outdir = tmp / "with-coi-out" + r = _unpack(first_path, outdir) + assert r.returncode == 0, f"unpack failed: {r.stdout}\n{r.stderr}" + assert (outdir / "program.ccr").read_bytes() == ccr_bytes + sidecar_out = outdir / manifest["coi"]["path"] + assert sidecar_out.read_bytes() == coi_bytes + + # Deleting the sidecar after unpack must not touch the raw program. + sidecar_out.unlink() + assert (outdir / "program.ccr").read_bytes() == ccr_bytes + _assert_corearch_loads(outdir / "program.ccr", tmp, "with-coi-deleted") + + # The same raw .ccr packaged without a sidecar stays byte-identical. + plain, plain_path = _pack(tmp, ccr_path, "with-coi-removed.ccrb") + assert plain.returncode == 0, f"pack failed: {plain.stdout}\n{plain.stderr}" + plain_entries = _bundle_entries(plain_path) + assert [name for name, _data in plain_entries] == ["manifest", "program.ccr"] + assert plain_entries[1][1] == ccr_bytes + plain_out = tmp / "with-coi-removed-out" + r = _unpack(plain_path, plain_out) + assert r.returncode == 0, f"unpack failed: {r.stdout}\n{r.stderr}" + assert (plain_out / "program.ccr").read_bytes() == ccr_bytes + _assert_corearch_loads(plain_out / "program.ccr", tmp, "with-coi-removed") + + +def test_explicit_target_controls_sidecar_path(): + with tempfile.TemporaryDirectory(dir=BASE) as td: + tmp = Path(td) + ccr_bytes = _build_ccr(tmp) + coi_path = tmp / "sidecar.coi" + coi_path.write_bytes(b"target-scoped-opaque-bytes") + target = "aarch64/linux/elf" + r, bundle = _pack(tmp, tmp / "bundle_probe.ccr", "target.ccrb", coi=coi_path, target=target) + assert r.returncode == 0, f"pack failed: {r.stdout}\n{r.stderr}" + + entries = _bundle_entries(bundle) + assert [name for name, _data in entries] == [ + "manifest", + "program.ccr", + f"coi/{target}.coi", + ] + manifest = _manifest(entries) + assert manifest["coi"]["target"] == target + assert manifest["coi"]["path"] == f"coi/{target}.coi" + assert entries[1][1] == ccr_bytes + + outdir = tmp / "target-out" + r = _unpack(bundle, outdir) + assert r.returncode == 0, f"unpack failed: {r.stdout}\n{r.stderr}" + assert (outdir / f"coi/{target}.coi").read_bytes() == b"target-scoped-opaque-bytes" + + +# --------------------------------------------------------------------------- +# 3. path traversal targets are rejected before anything is written +# --------------------------------------------------------------------------- + + +def test_pack_rejects_unsafe_target_names(): + with tempfile.TemporaryDirectory(dir=BASE) as td: + tmp = Path(td) + ccr = tmp / "program.ccr" + ccr.write_bytes(b"raw-ccr-placeholder") + coi = tmp / "sidecar.coi" + coi.write_bytes(b"opaque") + + for index, target in enumerate( + [ + "../evil", + "../../evil", + "/absolute/evil", + "x/../../evil", + "x//evil", + "x/./evil", + ".", + "", + "x/", + "x\\evil", + ] + ): + r, out = _pack( + tmp, + ccr, + f"bad-target-{index}.ccrb", + coi=coi, + target=target, + ) + assert r.returncode != 0, f"unsafe target accepted: {target!r}" + assert not out.exists(), f"unsafe target wrote a bundle: {target!r}" + assert "error" in _text(r).lower() + + +# --------------------------------------------------------------------------- +# 4. tampered manifest/body and unsafe bundle paths are rejected +# --------------------------------------------------------------------------- + + +def test_unpack_rejects_tampered_body_and_manifest(): + with tempfile.TemporaryDirectory(dir=BASE) as td: + tmp = Path(td) + ccr_bytes = _build_ccr(tmp) + coi_bytes = b"opaque-coi-bytes" + coi_path = tmp / "sidecar.coi" + coi_path.write_bytes(coi_bytes) + r, bundle = _pack(tmp, tmp / "bundle_probe.ccr", "tamper.ccrb", coi=coi_path) + assert r.returncode == 0, f"pack failed: {r.stdout}\n{r.stderr}" + good_entries = _bundle_entries(bundle) + + # Body bit flip: same size, different sha256. + bad_body = list(good_entries) + corrupted = bytearray(bad_body[1][1]) + corrupted[-1] ^= 0xFF + bad_body[1] = ("program.ccr", bytes(corrupted)) + _rewrite_bundle(bundle, bad_body) + outdir = tmp / "bad-body-out" + r = _unpack(bundle, outdir) + assert r.returncode != 0, "tampered program body was accepted" + combined = _text(r).lower() + assert "sha256" in combined or "hash" in combined, combined + assert not (outdir / "program.ccr").exists(), "failed unpack wrote program.ccr" + + # Manifest hash mismatch: body untouched, manifest lies. + manifest = _manifest(good_entries) + manifest["program"]["sha256"] = "0" * 64 + bad_manifest = list(good_entries) + bad_manifest[0] = ( + "manifest", + json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode("utf-8"), + ) + _rewrite_bundle(bundle, bad_manifest) + r = _unpack(bundle, tmp / "bad-manifest-out") + assert r.returncode != 0, "tampered manifest hash was accepted" + combined = _text(r).lower() + assert "sha256" in combined or "hash" in combined, combined + + # Declared sidecar removed from the tar: manifest is no longer satisfied. + _rewrite_bundle(bundle, list(good_entries[:2])) + r = _unpack(bundle, tmp / "missing-coi-out") + assert r.returncode != 0, "manifest-declared coi was allowed to disappear" + assert not (tmp / "missing-coi-out" / "program.ccr").exists() + + # A null sidecar record is malformed rather than an absent sidecar. + null_coi = _manifest(good_entries) + null_coi["coi"] = None + null_entries = list(good_entries) + null_entries[0] = ( + "manifest", + json.dumps(null_coi, sort_keys=True, separators=(",", ":")).encode("utf-8"), + ) + _rewrite_bundle(bundle, null_entries) + r = _unpack(bundle, tmp / "null-coi-out") + assert r.returncode != 0, "null coi record was accepted" + assert not (tmp / "null-coi-out" / "program.ccr").exists() + + # Unsafe sidecar path in both the manifest and the tar entry. + traversal_manifest = _manifest(good_entries) + traversal_manifest["coi"]["target"] = "../evil" + traversal_manifest["coi"]["path"] = "coi/../evil.coi" + traversal_entries = [ + ( + "manifest", + json.dumps(traversal_manifest, sort_keys=True, separators=(",", ":")).encode( + "utf-8" + ), + ), + ("program.ccr", ccr_bytes), + ("coi/../evil.coi", coi_bytes), + ] + _rewrite_bundle(bundle, traversal_entries) + r = _unpack(bundle, tmp / "traversal-out") + assert r.returncode != 0, "path-traversal bundle was accepted" + assert not (tmp / "traversal-out" / "program.ccr").exists() + + +# --------------------------------------------------------------------------- +# standalone runner +# --------------------------------------------------------------------------- + + +def main(): + tests = [ + value + for name, value in sorted(globals().items()) + if name.startswith("test_") and callable(value) + ] + failed = 0 + for test in tests: + try: + test() + except Exception as exc: # noqa: BLE001 - report every failure shape + failed += 1 + print(f"FAIL {test.__name__}: {exc!r}") + else: + print(f"PASS {test.__name__}") + print(f"{len(tests) - failed}/{len(tests)} passed") + return 1 if failed else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/ccrb.py b/tools/ccrb.py new file mode 100644 index 00000000..0fbbd90d --- /dev/null +++ b/tools/ccrb.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python3 +"""Pack and unpack `.ccrb` distribution bundles. + +`.ccrb` is an external distribution wrapper, not a compiler input format. The +payload is a deterministic POSIX/PAX tar inside one zstd frame: + + manifest JSON, UTF-8, sorted keys, compact separators + program.ccr raw v10 `.ccr`, byte-for-byte unchanged + coi/.coi optional opaque sidecar + +`corearch` never reads a bundle and never needs zstd: it only reads the raw +`program.ccr` extracted from one. `.coi` contents are intentionally opaque here; +their semantics belong to the independent `.coi` plan. + +Usage: + + tools/ccrb.py pack --ccr --out [--coi ] [--target ] + tools/ccrb.py unpack --bundle --outdir + +The packer uses the system `zstd` CLI and the Python standard library only. +""" + +from __future__ import annotations + +import argparse +import hashlib +import io +import json +import re +import shutil +import subprocess +import sys +import tarfile +from pathlib import Path, PurePosixPath + +MANIFEST_NAME = "manifest" +PROGRAM_NAME = "program.ccr" +COI_DIR = "coi" +DEFAULT_TARGET = "x86_64/linux/elf" +BUNDLE_VERSION = 1 +FORMAT = "ccr-v10" +FIXED_MODE = 0o644 +ZSTD_LEVEL = "-19" + +_SHA256_RE = re.compile(r"^[0-9a-f]{64}$") + + +class BundleError(Exception): + """Raised for an invalid input, unsafe path, or failed validation.""" + + +# --------------------------------------------------------------------------- +# zstd +# --------------------------------------------------------------------------- + + +def _zstd_binary(): + exe = shutil.which("zstd") + if exe is None: + raise BundleError("zstd CLI not found; tools/ccrb.py requires it for distribution") + return exe + + +def _compress(payload): + """Compress `payload` into a single deterministic zstd frame.""" + cmd = [_zstd_binary(), "-q", ZSTD_LEVEL, "--no-progress", "-T0", "-c"] + result = subprocess.run(cmd, input=payload, capture_output=True) + if result.returncode != 0: + detail = result.stderr.decode("utf-8", "replace").strip() + raise BundleError(f"zstd compression failed: {detail or result.returncode}") + return result.stdout + + +def _decompress(bundle_path): + cmd = [_zstd_binary(), "-q", "-d", "--no-progress", "-c", str(bundle_path)] + result = subprocess.run(cmd, capture_output=True) + if result.returncode != 0: + detail = result.stderr.decode("utf-8", "replace").strip() + raise BundleError(f"zstd decompression failed: {detail or result.returncode}") + return result.stdout + + +# --------------------------------------------------------------------------- +# deterministic tar +# --------------------------------------------------------------------------- + + +def _sha256(data): + return hashlib.sha256(data).hexdigest() + + +def _canonical_json(value): + return json.dumps( + value, + sort_keys=True, + separators=(",", ":"), + ensure_ascii=False, + ).encode("utf-8") + + +def _add_tar_entry(tf, name, data): + info = tarfile.TarInfo(name) + info.size = len(data) + info.mode = FIXED_MODE + info.uid = 0 + info.gid = 0 + info.uname = "" + info.gname = "" + info.mtime = 0 + info.type = tarfile.REGTYPE + info.pax_headers = {} + tf.addfile(info, io.BytesIO(data)) + + +def _build_tar(manifest_bytes, program_bytes, coi_bytes=None, target=None): + buf = io.BytesIO() + with tarfile.open( + fileobj=buf, + mode="w", + format=tarfile.PAX_FORMAT, + encoding="utf-8", + ) as tf: + _add_tar_entry(tf, MANIFEST_NAME, manifest_bytes) + _add_tar_entry(tf, PROGRAM_NAME, program_bytes) + if coi_bytes is not None: + _add_tar_entry(tf, f"{COI_DIR}/{target}.coi", coi_bytes) + return buf.getvalue() + + +def _parse_tar(payload): + try: + tf = tarfile.open(fileobj=io.BytesIO(payload), mode="r:") + entries = [] + with tf: + for member in tf.getmembers(): + _check_member_path(member.name) + _check_member_metadata(member) + extracted = tf.extractfile(member) + if extracted is None: + raise BundleError(f"tar entry has no data: {member.name}") + entries.append((member.name, extracted.read())) + except BundleError: + raise + except (tarfile.TarError, OSError, ValueError, UnicodeError) as exc: + raise BundleError(f"bundle payload is not a valid tar archive: {exc}") from exc + if not entries: + raise BundleError("bundle tar is empty") + return entries + + +def _check_member_metadata(member): + if not member.isfile(): + raise BundleError(f"unsupported tar entry type: {member.name}") + if member.uid != 0 or member.gid != 0: + raise BundleError(f"unexpected tar ownership on {member.name}") + if member.uname != "" or member.gname != "": + raise BundleError(f"unexpected tar user/group name on {member.name}") + if member.mtime != 0: + raise BundleError(f"unexpected tar mtime on {member.name}") + if member.mode != FIXED_MODE: + raise BundleError(f"unexpected tar mode on {member.name}") + + +def _check_member_path(name): + """Reject absolute and traversal names without trusting tarfile sanitizing.""" + if not name: + raise BundleError("tar entry has an empty name") + if name.startswith("/") or "\\" in name or "\x00" in name: + raise BundleError(f"unsafe tar entry path: {name!r}") + if any(ord(ch) < 0x20 for ch in name): + raise BundleError(f"unsafe tar entry path: {name!r}") + parts = name.split("/") + if any(part in ("", ".", "..") for part in parts): + raise BundleError(f"unsafe tar entry path: {name!r}") + if str(PurePosixPath(name)) != name: + raise BundleError(f"non-canonical tar entry path: {name!r}") + + +def _validate_target(target): + """A target is a safe relative POSIX path such as `x86_64/linux/elf`.""" + if not isinstance(target, str) or not target: + raise BundleError("coi target must be a non-empty relative path") + if target.startswith("/") or "\\" in target or "\x00" in target: + raise BundleError(f"unsafe coi target: {target!r}") + if any(ord(ch) < 0x20 for ch in target): + raise BundleError(f"unsafe coi target: {target!r}") + parts = target.split("/") + if any(part in ("", ".", "..") for part in parts): + raise BundleError(f"unsafe coi target: {target!r}") + if str(PurePosixPath(target)) != target: + raise BundleError(f"non-canonical coi target: {target!r}") + + +# --------------------------------------------------------------------------- +# manifest +# --------------------------------------------------------------------------- + + +def _program_record(program_bytes): + return { + "path": PROGRAM_NAME, + "sha256": _sha256(program_bytes), + "size": len(program_bytes), + } + + +def _coi_record(coi_bytes, target): + return { + "target": target, + "path": f"{COI_DIR}/{target}.coi", + "sha256": _sha256(coi_bytes), + "size": len(coi_bytes), + } + + +def _parse_manifest(raw): + try: + manifest = json.loads(raw.decode("utf-8")) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise BundleError(f"manifest is not valid UTF-8 JSON: {exc}") from exc + + if not isinstance(manifest, dict): + raise BundleError("manifest must be a JSON object") + + allowed = {"bundle_version", "format", "program", "coi"} + unknown = set(manifest) - allowed + if unknown: + raise BundleError(f"manifest has unexpected keys: {sorted(unknown)}") + required = {"bundle_version", "format", "program"} + missing = required - set(manifest) + if missing: + raise BundleError(f"manifest is missing keys: {sorted(missing)}") + + version = manifest["bundle_version"] + if type(version) is not int or version != BUNDLE_VERSION: + raise BundleError(f"unsupported bundle_version: {version!r}") + if manifest["format"] != FORMAT: + raise BundleError(f"unsupported bundle format: {manifest['format']!r}") + + _check_record(manifest["program"], "program", {"path", "sha256", "size"}) + if manifest["program"]["path"] != PROGRAM_NAME: + raise BundleError(f"program path must be {PROGRAM_NAME!r}") + + if "coi" in manifest: + coi = manifest["coi"] + _check_record(coi, "coi", {"target", "path", "sha256", "size"}) + _validate_target(coi["target"]) + expected = f"{COI_DIR}/{coi['target']}.coi" + if coi["path"] != expected: + raise BundleError(f"coi path must be {expected!r}") + return manifest + + +def _check_record(record, label, keys): + if not isinstance(record, dict): + raise BundleError(f"manifest {label} must be an object") + if set(record) != keys: + raise BundleError(f"manifest {label} has unexpected keys: {sorted(record)}") + size = record["size"] + if type(size) is not int or size < 0: + raise BundleError(f"manifest {label} has an invalid size: {size!r}") + digest = record["sha256"] + if not isinstance(digest, str) or not _SHA256_RE.fullmatch(digest): + raise BundleError(f"manifest {label} has an invalid sha256: {digest!r}") + + +def _verify_payload(label, data, record): + if len(data) != record["size"]: + raise BundleError( + f"{label} size mismatch: manifest says {record['size']}, payload is {len(data)}" + ) + actual = _sha256(data) + if actual != record["sha256"]: + raise BundleError( + f"{label} sha256 mismatch: manifest says {record['sha256']}, payload is {actual}" + ) + + +# --------------------------------------------------------------------------- +# pack / unpack +# --------------------------------------------------------------------------- + + +def pack(args): + ccr_path = Path(args.ccr) + program_bytes = ccr_path.read_bytes() + + coi_bytes = None + target = None + if args.coi is not None: + coi_bytes = Path(args.coi).read_bytes() + target = args.target if args.target is not None else DEFAULT_TARGET + _validate_target(target) + elif args.target is not None: + raise BundleError("--target requires --coi") + + manifest = { + "bundle_version": BUNDLE_VERSION, + "format": FORMAT, + "program": _program_record(program_bytes), + } + if coi_bytes is not None: + manifest["coi"] = _coi_record(coi_bytes, target) + + tar_bytes = _build_tar( + _canonical_json(manifest), + program_bytes, + coi_bytes=coi_bytes, + target=target, + ) + bundle = _compress(tar_bytes) + + out = Path(args.out) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_bytes(bundle) + return out + + +def unpack(args): + bundle_path = Path(args.bundle) + payload = _decompress(bundle_path) + entries = _parse_tar(payload) + + if entries[0][0] != MANIFEST_NAME: + raise BundleError("bundle must start with the manifest entry") + if len(entries) < 2 or entries[1][0] != PROGRAM_NAME: + raise BundleError(f"bundle must contain {PROGRAM_NAME!r} after the manifest") + + manifest = _parse_manifest(entries[0][1]) + expected_names = [MANIFEST_NAME, PROGRAM_NAME] + if "coi" in manifest: + expected_names.append(manifest["coi"]["path"]) + actual_names = [name for name, _data in entries] + if actual_names != expected_names: + raise BundleError( + f"unexpected bundle entries: expected {expected_names}, got {actual_names}" + ) + + program_bytes = entries[1][1] + _verify_payload(PROGRAM_NAME, program_bytes, manifest["program"]) + + coi_bytes = None + if "coi" in manifest: + coi_bytes = entries[2][1] + _verify_payload(manifest["coi"]["path"], coi_bytes, manifest["coi"]) + + outdir = Path(args.outdir) + outdir.mkdir(parents=True, exist_ok=True) + (outdir / MANIFEST_NAME).write_bytes(entries[0][1]) + (outdir / PROGRAM_NAME).write_bytes(program_bytes) + if coi_bytes is not None: + coi_out = outdir / manifest["coi"]["path"] + coi_out.parent.mkdir(parents=True, exist_ok=True) + coi_out.write_bytes(coi_bytes) + return outdir + + +def _parser(): + parser = argparse.ArgumentParser( + prog="ccrb.py", + description="Pack or unpack a .ccrb distribution bundle.", + ) + sub = parser.add_subparsers(dest="command", required=True) + + pack_cmd = sub.add_parser("pack", help="wrap a raw .ccr in a .ccrb bundle") + pack_cmd.add_argument("--ccr", required=True, help="raw v10 .ccr input") + pack_cmd.add_argument("--out", required=True, help="bundle output path") + pack_cmd.add_argument("--coi", help="optional opaque .coi sidecar") + pack_cmd.add_argument( + "--target", + help=f"sidecar target path (default: {DEFAULT_TARGET})", + ) + pack_cmd.set_defaults(func=pack) + + unpack_cmd = sub.add_parser("unpack", help="extract a .ccrb bundle") + unpack_cmd.add_argument("--bundle", required=True, help="bundle input path") + unpack_cmd.add_argument("--outdir", required=True, help="extraction directory") + unpack_cmd.set_defaults(func=unpack) + return parser + + +def main(argv=None): + args = _parser().parse_args(argv) + try: + args.func(args) + except BundleError as exc: + print(f"ccrb: error: {exc}", file=sys.stderr) + return 1 + except OSError as exc: + print(f"ccrb: error: {exc}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 1c9edbaa64d185079a2c116c62bf4d09e3a77698 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 02:16:57 +0800 Subject: [PATCH 54/58] docs: scope ccr v10 cleanup and keep raw ABI explicit --- .../plans/2026-10-23-ccr-v10-migration.md | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md index 5ff1b2fd..33c576a6 100644 --- a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -493,25 +493,34 @@ jj commit -m "dist: add ccrb bundle with optional coi sidecar" --- -### Task 8: 删除残留旧面并重锁 +### Task 8: 删除残留 v9 面并重锁 **Files:** - Modify: `src/arch/x86_64/x86_frontend_abi.cr` - Modify: `src/compiler/ccr_io.cr` - Modify: `src/compiler/globals.cr` +- Modify: `src/compiler/dyn_arr.cr` - Modify: `docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md`(标注 v9 历史) -- Modify: `tools/baseline/canary_values.tsv` +- Modify: `tools/baseline/canary_values.tsv`(仅在字节变化时) +- Test: `tests/selfhost/test_ccr_v10_format.py` **Interfaces:** - Consumes: Task 4 至 7 的 v10 路径。 - Produces: - - 普通语言 API 不再注册 `cell_store/cell_load/load8/store8/load64/w32/load_str_ptr/store_str_ptr/_dyncpy` 中的机器名。 - - v9 常量、`OFF_ENTRY_HOME`、v9 测试常量全部删除或标为历史 fixture。 - - canary 只保留 v10 值。 + - v9 wire 常量 `589825`、`ESZ_ENTRY_DISK : int = 28`、v9 段 tag/顺序在源码中不再作为有效路径出现;负向 fixture 可保留 `589825`,但必须标注为历史 fixture。 + - `ccr_io.cr` 的 v10 写侧/读侧不再写/读 `home`;`OFF_ENTRY_HOME` 如仍在 corearch 实例侧使用,必须改名为 runtime/instance 字段并注释“不进入 `.ccr`”。 + - `x86_frontend_abi.cr` 不再注册 `w64/r64`(已移除);现有 `cell_*`/`load8`/`store8`/`load64`/`w32`/`load_str_ptr`/`store_str_ptr`/`_dyncpy` 暂作为 `SYSTEM` runtime ABI 保留,因为 stdlib scheduler/arena 仍依赖它们。新增注释说明这是 SYSTEM/raw 边界,不是普通语言 API。 + - canary 只保留 v10 值;ELF canary 保持 `826d74cf...`。 + +**Ruling:** “普通语言 API 彻底删除 fixed-width/raw 内建”需要先实现 `Bytes` 公共层并迁移 stdlib,属于独立任务;本任务不做半删。保留的 raw 内建必须显式标为 `SYSTEM`/runtime ABI,并让使用它们的程序在 header 中标 SYSTEM。 - [ ] **Step 1: 写失败测试** -静态断言 `src/arch/x86_64/x86_frontend_abi.cr` 不出现 fixed-width builtin 名;`src/compiler/ccr_io.cr` 不出现 `589825`、`ESZ_ENTRY_DISK : int = 28`、`home` 写入。 +静态断言: +- `src/compiler/ccr_io.cr` 不出现有效路径的 `589825` 或 `ESZ_ENTRY_DISK : int = 28`;历史负向 fixture 中的 `589825` 豁免。 +- `src/compiler/ccr_io.cr` 的 v10 writer/reader 不写/读 `home`。 +- `src/arch/x86_64/x86_frontend_abi.cr` 不注册 `w64`/`r64`;保留的 raw 内建必须带 `SYSTEM` 注释。 +- `test_ccr_v10_format.py` 继续断言 v10 header/段序/ENT 24B/CASE。 - [ ] **Step 2: 删除并运行** From 057ffba24af5982cade0023f7398fe3e00d35bb6 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 02:20:37 +0800 Subject: [PATCH 55/58] refactor: remove v9 ccr and residual raw frontend surface --- .../specs/2026-09-09-lattice-ir-v7-format.md | 6 + src/arch/x86_64/regalloc.cr | 4 +- src/arch/x86_64/x86_frontend_abi.cr | 8 ++ src/compiler/ccr_io.cr | 25 ++-- src/compiler/dyn_arr.cr | 12 +- src/compiler/globals.cr | 3 +- src/lattice/ent_kernel.cr | 29 +++-- tests/selfhost/test_ccr_types.py | 3 +- tests/selfhost/test_ccr_v10_format.py | 122 ++++++++++++++++++ tests/selfhost/test_ccr_v7.py | 1 + 10 files changed, 179 insertions(+), 34 deletions(-) diff --git a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md index 1ced0ee1..4f25b909 100644 --- a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md +++ b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md @@ -1,5 +1,11 @@ # 格形态 IR 字节格式 +> **历史文档(v7 / v8 / v9)**:本文固定描述 v7→v9 时代的 `.ccr` 字节布局, +> 包括 version 9 的 8 段表、28B ENT 与 `home` 字段。**它不是当前格式**, +> 仅作历史留痕,不随实现更新。当前线格式(v10:9 段、ENT 24B、无 `home`) +> 见 `docs/superpowers/specs/2026-10-23-lattice-ccr-v10-design.md`;v9 文件 +> 自 v10 cutover 起整类拒收,只作为负向 fixture 保留在测试中。 + ## 概述 `.ccr` 是格形态的字节表示。当前版本是 9,使用段表架构。语义定义以 carrier 设计为准,本文件定义字节布局。 diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index 57029d99..15676e76 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -56,8 +56,8 @@ fn try_inject_home_conflict(func_i: int) -> int: if v1 != v2 && entries_coexist(func_i, e1, e2) != 0: meta_remove_var(v1) meta_remove_var(v2) - w32(g_ir_entries, e1 * ESZ_ENTRY + OFF_ENTRY_HOME, 7) - w32(g_ir_entries, e2 * ESZ_ENTRY + OFF_ENTRY_HOME, 7) + w32(g_ir_entries, e1 * ESZ_ENTRY + OFF_ENTRY_RUNTIME_HOME, 7) + w32(g_ir_entries, e2 * ESZ_ENTRY + OFF_ENTRY_RUNTIME_HOME, 7) return 1 e2i = e2i + 1 e1i = e1i + 1 diff --git a/src/arch/x86_64/x86_frontend_abi.cr b/src/arch/x86_64/x86_frontend_abi.cr index 008b1a4f..03f81dcf 100644 --- a/src/arch/x86_64/x86_frontend_abi.cr +++ b/src/arch/x86_64/x86_frontend_abi.cr @@ -1,6 +1,14 @@ // === arch/x86_64/frontend_abi.cr === // Architecture-axis frontend ABI builtins. // +// SYSTEM / RAW RUNTIME ABI BOUNDARY — NOT ORDINARY LANGUAGE API. +// The fixed-width raw memory and string-header view builtins below are part of +// the SYSTEM runtime ABI, not the public language surface. They remain registered +// because the stdlib scheduler/arena and the compiler runtime still use them. +// Programs that call them must be declared SYSTEM at the header level. The +// fixed-width raw aliases w64/r64 are retired; use the cell accessors instead. +// Replacing these with a public `Bytes` layer is a separate, stdlib-wide task. +// // Fixed-width raw memory and string-header view names are x86-64 facts. The // semantic checker does not own them; it consumes the target composition table // through prelude_builtin_*. diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 9670f903..43c35bf0 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -2,7 +2,8 @@ // .ccr binary serialization, the interface between corec and corearch. // // Version 10 only. The loader checks version == CCR_V10_VERSION (0x000A0000) -// and rejects v9 (589825) and every other version; there is no conversion path. +// and rejects the historical v9 wire id and every other version; there is no +// conversion path. v9 bytes survive only as negative fixtures in the test suite. // The format has 9 segments in canonical order: // STR, TYPE, IFACE, SYM, NOD, ENT, REG, EDG, CASE (tags 1..9). // @@ -525,8 +526,6 @@ fn ccr_ent_ls(e: int) -> int: return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_LS) fn ccr_ent_le(e: int) -> int: return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_LE) -fn ccr_ent_home(e: int) -> int: - return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_HOME) fn ccr_ent_flags(e: int) -> int: return buf_read_i32(g_ir_entries, e * ESZ_ENTRY + OFF_ENTRY_FLAGS) @@ -1021,7 +1020,7 @@ fn save_ccr(path: string) -> int: buf_write_u32(buf, pos, ele + 1) pos = pos + 4 // live_end(半开 = 内存闭区间 +1) buf_write_u32(buf, pos, efl) - pos = pos + 4 // flags;home 不属于 v10 + pos = pos + 4 // flags(ENT 记录到此结束;home 不在 v10 盘面) ej = ej + 1 // === REG: regions === @@ -1149,9 +1148,10 @@ fn inject_var_shift() -> int: return 0 // Load: v10 only. Validate the header and canonical segment table, reject -// out-of-range segments, and reject every version other than 0x000A0000 (v9 is -// rejected outright). The table has 9 segments in canonical order. All nine are -// required; ENT may carry an empty record table but its 4B count is present. +// out-of-range segments, and reject every version other than 0x000A0000 (the +// historical v9 wire id is rejected outright). The table has 9 segments in +// canonical order. All nine are required; ENT may carry an empty record table +// but its 4B count is present. // Loader output is semantic objects plus guards. The linear NOD-to-g_ir_instrs // rebuild is outside this file: build_linear_schedule in regalloc.cr is called by // corearch.cr or src/targets/x86_64/linux/elf/main.cr after a successful load. @@ -2028,8 +2028,8 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 // type_id(TYPE row;-1 = 无) fl2 := buf_read_u32(data, pos) pos = pos + 4 // flags(语义特性;非目标事实) - // src2/src3 may not carry v9 target facts. The writer normalizes them; - // the loader rejects old/foreign files instead of silently accepting. + // src2/src3 may not carry legacy v9 target facts. The writer normalizes + // them; the loader rejects old/foreign files instead of silently accepting. if opcode == IR_ALLOC_ARRAY: if s2 != 0 || s3 != 0: return -1 @@ -2073,8 +2073,9 @@ fn load_ccr(data: string, fsize: int) -> int: // === ENT: entries(24B each -> 内存 24B 表)=== // 盘上半开 live_end -> 内存闭区间 −1;version 字段不入内存(组内序可推)。 - // v10 has no home field. The in-memory slot stays -1 until Task 8 retires it; - // the loader never reads that slot from disk. + // v10 has no home field. OFF_ENTRY_RUNTIME_HOME is an instance-side + // allocator slot only: the loader initializes it to -1 and never consumes a + // disk byte for it. if have6 != 0: if !ccr_has_bytes(pos, 4, seg_end6): return -1 @@ -2124,7 +2125,7 @@ fn load_ccr(data: string, fsize: int) -> int: cell_store32(g_ir_entries, mo2 + OFF_ENTRY_DEF, ed) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LS, els) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_LE, ele - 1) - cell_store32(g_ir_entries, mo2 + OFF_ENTRY_HOME, -1) + cell_store32(g_ir_entries, mo2 + OFF_ENTRY_RUNTIME_HOME, -1) cell_store32(g_ir_entries, mo2 + OFF_ENTRY_FLAGS, efl) g_entry_count = eii + 1 eii = eii + 1 diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index d2420f49..b23cb856 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -113,15 +113,19 @@ OFF_DFE_NEXT : int = 16 OFF_DFE_KIND : int = 24 // 0=data (def-use), 1=state (ordering/termination) // Entry record, filled by compute_entries. It splits variables into versions at -// definition points. The in-memory layout is identical to the .ccr on-disk layout: -// 24 bytes, six 4-byte fields, read and written little-endian. -// var_idx u32, def_instr i32, live_start i32, live_end i32, home i32, flags u32. +// definition points. The in-memory record is 24 bytes, six 4-byte fields, read +// and written little-endian. It is NOT the v10 wire record: on disk ENT is also +// 24 bytes but stores `version` in the slot where memory keeps the runtime-only +// allocator home. var_idx u32, def_instr i32, live_start i32, live_end i32, +// runtime_home i32, flags u32. ESZ_ENTRY : int = 24 OFF_ENTRY_VAR : int = 0 // 全局 IR 变量索引 OFF_ENTRY_DEF : int = 4 // 定值指令全局索引(-1 = 无定值) OFF_ENTRY_LS : int = 8 // 版本存在区间起点(全局指令序) OFF_ENTRY_LE : int = 12 // 版本存在区间终点(全局指令序) -OFF_ENTRY_HOME : int = 16 // 分配槽位(-1 = 未分配) +// Runtime/instance-only allocator slot (-1 = unassigned). This is never +// serialized into `.ccr`; v10 ENT has no home field. +OFF_ENTRY_RUNTIME_HOME : int = 16 OFF_ENTRY_FLAGS : int = 20 // 位 0 预留:无配方(条款 4b) // Subgraph entry (48 bytes each) diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 15064adc..95a6aceb 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -347,7 +347,8 @@ g_live_range_cap : int, mut // live_start(8) / live_end(12) i32: version live range as a closed interval in // global instruction order, [def_j, min(def_{j+1}-1, last_ref)]. // Without a defining entry, [first_ref, last_ref]. -// home(16) i32: slot assigned by the allocator, or -1 if unassigned. +// runtime_home(16) i32: allocator/instance slot, or -1 if unassigned. Never +// serialized into `.ccr`; v10 ENT has no home field. // flags(20) u32: 0; bit 0 is reserved. // Each function has one segment, appended in func_i order. Segment bounds are in // g_ir_func_entry_start and g_ir_func_entry_count (8 bytes each). diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index 41efe14e..041bd955 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -212,8 +212,8 @@ fn ent_live_start(e: int) -> int: return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_LS) fn ent_live_end(e: int) -> int: return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_LE) -fn ent_home(e: int) -> int: - return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_HOME) +fn ent_runtime_home(e: int) -> int: + return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_RUNTIME_HOME) fn ent_flags(e: int) -> int: return buf_read_i32(g_ir_entries, ent_off(e) + OFF_ENTRY_FLAGS) @@ -290,7 +290,7 @@ fn compute_entries(func_i: int) -> int: w32(g_ir_entries, eo + OFF_ENTRY_DEF, inst) w32(g_ir_entries, eo + OFF_ENTRY_LS, inst) w32(g_ir_entries, eo + OFF_ENTRY_LE, last_global) - w32(g_ir_entries, eo + OFF_ENTRY_HOME, -1) + w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0) cell_store(prev, lv * 8, g_entry_count) g_entry_count = g_entry_count + 1 @@ -312,7 +312,7 @@ fn compute_entries(func_i: int) -> int: w32(g_ir_entries, eo + OFF_ENTRY_DEF, -1) w32(g_ir_entries, eo + OFF_ENTRY_LS, ist + ff) w32(g_ir_entries, eo + OFF_ENTRY_LE, ist + ll) - w32(g_ir_entries, eo + OFF_ENTRY_HOME, -1) + w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0) g_entry_count = g_entry_count + 1 cnt = cnt + 1 @@ -448,7 +448,7 @@ fn dump_entries_summary(): print("..") print(int_str(ent_live_end(e))) print(" home ") - print(int_str(ent_home(e))) + print(int_str(ent_runtime_home(e))) print(" flags ") println(int_str(ent_flags(e))) ei = ei + 1 @@ -518,14 +518,14 @@ fn coexist_home_conflicts(func_i: int) -> int: if ei >= ec: break e1 := es + ei - h1 := ent_home(e1) + h1 := ent_runtime_home(e1) if h1 >= 0: ej : ., mut = ei + 1 loop: if ej >= ec: break e2 := es + ej - if ent_home(e2) == h1: + if ent_runtime_home(e2) == h1: if entries_coexist(func_i, e1, e2) != 0: conflicts = conflicts + 1 ej = ej + 1 @@ -903,10 +903,11 @@ fn verify_regalloc_consistency(func_i: int) -> int: // Rule 1 has two passes: // 1. Registry pass: entries with kern_loc_of(e) >= 0 grouped by location. - // 2. Home pass: entries with ent_home >= 0 grouped by home value, skipping - // -1. + // 2. Runtime-home pass: entries with ent_runtime_home >= 0 grouped by the + // instance-side slot value, skipping -1. // The registry pass runs first. Entries already registered are excluded from - // the home pass; an entry with both is not produced by the current paths. + // the runtime-home pass; an entry with both is not produced by the current + // paths. reg_cnt : ., mut = 0 home_cnt : ., mut = 0 ei : ., mut = 0 @@ -917,7 +918,7 @@ fn verify_regalloc_consistency(func_i: int) -> int: if ent_live_start(e) >= 0 && ent_live_end(e) >= 0: if kern_loc_of(e) >= 0: reg_cnt = reg_cnt + 1 - else if ent_home(e) >= 0: + else if ent_runtime_home(e) >= 0: home_cnt = home_cnt + 1 ei = ei + 1 rule1_viol : ., mut = 0 @@ -938,8 +939,8 @@ fn verify_regalloc_consistency(func_i: int) -> int: ci = ci + 1 ei = ei + 1 rule1_viol = rl_rule1_sweep(func_i, rec, reg_cnt, 0) - // Home pass. cap_base carries the number already printed so RPT_MAX applies - // across both passes. + // Runtime-home pass. cap_base carries the number already printed so RPT_MAX + // applies across both passes. if home_cnt > 1: rec : string, mut = alloc(home_cnt * 16) ci : ., mut = 0 @@ -949,7 +950,7 @@ fn verify_regalloc_consistency(func_i: int) -> int: break e := es + ei if ent_live_start(e) >= 0 && ent_live_end(e) >= 0: - h := ent_home(e) + h := ent_runtime_home(e) if h >= 0: cell_store(rec, ci * 16, h) cell_store(rec, ci * 16 + 8, e) diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index 1ed33ef2..b5737d55 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -12,7 +12,7 @@ ③ TYPE(2)/IFACE(3) **皆为内容面**(R2 P4 Task 2/3 起——空壳期已退役); ④ loader 负分支(byte mutation,逐个打;全部要求 corearch rc≠0 且**不得 静默当空表**——三态纪律 C.5-3): - · version := 7/8/589825 → 拒(v10 版本闸整类拒收); + · version := 7/8/589825(589825 = 历史 v9.1 负向 fixture)→ 拒(v10 版本闸整类拒收); · **结构性合法的 v7(6 段)文件整体重建** → 拒(D10 的正面证据: 非「字节破烂」被拒,而是合法旧文件被版本闸拒); · seg_cnt := 6(末段 size 扩到 EOF——前闸全过)→ 拒(D11 必备集); @@ -2155,6 +2155,7 @@ def test_p6t3_version_gate_rejects_v8(): corec_ccr(src, ccr_path) arch_elf(ccr_path, out) # v10 正控(非平凡性) data = bytearray(read_ccr(ccr_path)) + # HISTORICAL v9 fixture: 589825 (v9.1) must be rejected by the v10 gate. struct.pack_into(' Date: Sat, 24 Oct 2026 02:32:56 +0800 Subject: [PATCH 56/58] fix: classify raw ABI calls as SYSTEM at graph level --- .../specs/2026-09-09-lattice-ir-v7-format.md | 2 +- src/arch/x86_64/regalloc.cr | 8 ++-- src/compiler/ccr_io.cr | 23 ++++++++- src/compiler/dyn_arr.cr | 14 +++--- src/compiler/ir_gen.cr | 5 +- src/lattice/ent_kernel.cr | 16 ++++--- tests/selfhost/test_ccr_v10_format.py | 47 +++++++++++++++++-- tools/baseline/canary_values.tsv | 22 +++++++-- 8 files changed, 108 insertions(+), 29 deletions(-) diff --git a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md index 4f25b909..87292374 100644 --- a/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md +++ b/docs/superpowers/specs/2026-09-09-lattice-ir-v7-format.md @@ -8,7 +8,7 @@ ## 概述 -`.ccr` 是格形态的字节表示。当前版本是 9,使用段表架构。语义定义以 carrier 设计为准,本文件定义字节布局。 +`.ccr` 是格形态的字节表示。v9 时代的版本号是 9,使用段表架构(历史;当前 v10 见上方指针)。语义定义以 carrier 设计为准,本文件定义字节布局。 文件由头部、段表和段体组成: diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index 15676e76..f5989cf7 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -29,10 +29,10 @@ fn inject_coexist_oob() -> int: // assuming function 0 is the tested main. // // Home-group conflict: pick two coexisting entries for different vars, remove -// both vars from register residency, then set both home slots to 7. The -// verifier reports a home-group violation (rule 1). Vars without a home entry -// have home = -1 and do not participate in the sweep, so there are no side -// effects. +// both vars from register residency, then set both runtime/instance home slots +// to 7. This slot is never serialized into `.ccr`. The verifier reports a +// home-group violation (rule 1). Vars without a home entry have runtime_home = +// -1 and do not participate in the sweep, so there are no side effects. fn try_inject_home_conflict(func_i: int) -> int: es := entry_start(func_i) ec := entry_count(func_i) diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 43c35bf0..3138f648 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -360,6 +360,21 @@ fn ccr_op_is_system(op: int, arena_count: int) -> int: return 1 return 0 +// Raw byte/runtime ABI builtins retained for the stdlib scheduler/arena and +// the compiler runtime. A direct IR_CALL to one of these is a SYSTEM boundary: +// the graph calls are the authority, not the g_ccr_emit_raw_int source bit. +// IR_CALL stores the callee string index in src3; ordinary arguments live in +// the argument chain rooted at s1 and must never be inspected here. +fn ccr_raw_abi_builtin(name: string) -> int: + if name == "cell_store" || name == "cell_load" || name == "load8" || name == "store8" || name == "load64" || name == "w32" || name == "load_str_ptr" || name == "store_str_ptr" || name == "_dyncpy": + return 1 + return 0 + +fn ccr_raw_abi_call(sym: int) -> int: + if sym < 0: + return 0 + return ccr_raw_abi_builtin(istr_get(sym)) + fn ccr_op_is_concurrent(op: int) -> int: if op == IR_SPAWN || op == IR_YIELD || op == IR_AWAIT: return 1 @@ -385,6 +400,8 @@ fn ccr_compute_header_flags() -> int: break if ccr_op_is_system(iri_op(ii), iri_s1(ii)) != 0: hsys = 1 + if iri_op(ii) == IR_CALL && ccr_raw_abi_call(iri_s3(ii)) != 0: + hsys = 1 if ccr_op_is_concurrent(iri_op(ii)) != 0: hconc = 1 ii = ii + 1 @@ -411,6 +428,8 @@ fn ccr_validate_header_flags(rsv: int) -> int: break if ccr_op_is_system(nod_op(ii), nod_s1(ii)) != 0: nsys = 1 + if nod_op(ii) == IR_CALL && ccr_raw_abi_call(nod_s3(ii)) != 0: + nsys = 1 if ccr_op_is_concurrent(nod_op(ii)) != 0: nconc = 1 ii = ii + 1 @@ -2050,8 +2069,8 @@ fn load_ccr(data: string, fsize: int) -> int: // so rejecting here keeps old loaders from silently ignoring meaning. if fl2 > CCR_NOD_FLAG_KNOWN_MASK: return -1 - // 对象缓冲写(语义字段 = 盘字节镜像,w32/w64 低 32/64 位存储,存取 - // 经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) + // 对象缓冲写(语义字段 = 盘字节镜像,由 cell_store/cell_store32 存储, + // 存取经 nod_* 访问器 buf_read_* 符号扩展,数值与原重建段消费完全一致) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_OP, opcode) cell_store32(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_DEST, dest) cell_store(g_v7_nod_sem, ii * ESZ_NOD_SEM + OFF_NS_S1, s1) diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index b23cb856..8f388d54 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -1,7 +1,7 @@ // === dyn_arr.cr === // Compiler table constants + grow functions + string pool. -// Byte helpers (w8/bu8/w32/r32/w64/r64/_dyncpy) live in src/kernel/bytes.cr; -// they are the low-level cell layer, not frontend semantics. +// Byte helpers (w8/bu8/w32/r32/cell_store/cell_load/_dyncpy) live in +// src/kernel/bytes.cr; they are the low-level cell layer, not frontend semantics. // ============================================================ // Element sizes (bytes per element) @@ -114,10 +114,12 @@ OFF_DFE_KIND : int = 24 // 0=data (def-use), 1=state (ordering/termination) // Entry record, filled by compute_entries. It splits variables into versions at // definition points. The in-memory record is 24 bytes, six 4-byte fields, read -// and written little-endian. It is NOT the v10 wire record: on disk ENT is also -// 24 bytes but stores `version` in the slot where memory keeps the runtime-only -// allocator home. var_idx u32, def_instr i32, live_start i32, live_end i32, -// runtime_home i32, flags u32. +// and written little-endian: +// mem 0 var, 4 def, 8 live_start, 12 live_end, 16 runtime_home, 20 flags. +// The v10 wire ENT record is also 24 bytes but has a different slot meaning: +// wire 0 var_id, 4 version, 8 def_nod, 12 live_start, 16 live_end, 20 flags. +// The wire has no home field. In particular, wire offset 16 is live_end while +// memory offset 16 is the runtime-only allocator home. ESZ_ENTRY : int = 24 OFF_ENTRY_VAR : int = 0 // 全局 IR 变量索引 OFF_ENTRY_DEF : int = 4 // 定值指令全局索引(-1 = 无定值) diff --git a/src/compiler/ir_gen.cr b/src/compiler/ir_gen.cr index 152f030a..21246a5f 100644 --- a/src/compiler/ir_gen.cr +++ b/src/compiler/ir_gen.cr @@ -4173,8 +4173,9 @@ fn ccr_set_raw_mode_from_source(): g_ccr_emit_raw_int = 1 if ccr_source_has_marker("import cli") != 0: // The CLI word-table layer intentionally stores raw string pointers via - // w64/r64 and views them back through @str_of. Legacy raw semantics are - // required for that table; user CLI programs are not arithmetic kernels. + // cell_store/cell_load and views them back through @str_of. Legacy raw + // semantics are required for that table; user CLI programs are not + // arithmetic kernels. g_ccr_emit_raw_int = 1 if ccr_source_dir_is_compiler() != 0: g_ccr_emit_raw_int = 1 diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index 041bd955..17e6c926 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -506,9 +506,10 @@ fn coexist_version_conflicts(func_i: int) -> int: ei = ei + 1 return conflicts -// Count conflicts among entries with the same home slot. home = -1 is ignored. -// This is a simple pairwise check; when home assignment is enabled, replace it -// with a per-group sweep instead of reusing this function. +// Count conflicts among entries with the same runtime/instance home slot. +// runtime_home = -1 is ignored. This is a simple pairwise check; when home +// assignment is enabled, replace it with a per-group sweep instead of reusing +// this function. This slot never enters `.ccr`. fn coexist_home_conflicts(func_i: int) -> int: es := entry_start(func_i) ec := entry_count(func_i) @@ -532,8 +533,8 @@ fn coexist_home_conflicts(func_i: int) -> int: ei = ei + 1 return conflicts -// --dump-coexist output: per function, version conflicts (expected 0) and home -// conflicts (expected 0 while home is unassigned). +// --dump-coexist output: per function, version conflicts (expected 0) and +// runtime/instance home conflicts (expected 0 while runtime_home is unassigned). fn dump_coexist_summary(): fi : ., mut = 0 loop: @@ -713,8 +714,9 @@ fn rl_merge_sort(buf: string, n: int): width = width * 2 // Print an opaque location. The kernel does not classify values as registers or -// home slots; that belongs to the instance. Rule 1 has two passes, the registry -// pass and the home-field pass, but they print the same report format. +// runtime/instance home slots; that belongs to the instance. Rule 1 has two +// passes, the registry pass and the runtime-home pass, but they print the same +// report format. fn rl_print_loc(loc: int): print("loc ") print(int_str(loc)) diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 98b88003..32b4dd04 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -853,14 +853,29 @@ def _segment_offset(data, tag): return *p """ +# Direct raw ABI call: the graph must make this SYSTEM even though no extern or +# raw-int build mode is involved. +RAW_CALL_SOURCE = """fn main() -> int: + b := alloc(16) + cell_store(b, 0, 7) + return cell_load(b, 0) +""" + -def test_header_flags_portable_user_program(): +def test_header_flags_minimal_program_is_system(): + """Every compiled program is SYSTEM while raw stdlib/runtime ABI remains. + + The compiler injects runtime functions that call raw builtins (load8/store8/ + cell_load/...), so a minimal user program is not an isolated graph. PORTABLE + cannot be truthfully produced by the current e2e pipeline; the graph-level + classifier (not this test) decides the header. + """ with tempfile.TemporaryDirectory() as td: data = _build_v10(Path(td), PORTABLE_SOURCE, "portable") f = flags(data) assert f & ~0x7 == 0, f"reserved header bits set: {f:#x}" - assert f & 2 == 0, f"portable user program marked SYSTEM: {f:#x}" - assert f in (0, 1), f"unexpected portable flags {f:#x}" + assert f & 2 != 0, f"raw runtime injection did not mark SYSTEM: {f:#x}" + assert f & 1 == 0, f"SYSTEM program also claims PORTABLE: {f:#x}" def test_header_flags_system_artifact(): @@ -871,6 +886,32 @@ def test_header_flags_system_artifact(): assert f & 1 == 0, f"SYSTEM program also claims PORTABLE: {f:#x}" +def test_header_flags_raw_abi_call_is_system(): + """A direct raw ABI call is a SYSTEM boundary at graph level. + + cell_store/cell_load are retained SYSTEM runtime builtins. The header must + say SYSTEM (bit 2), not PORTABLE (bit 1), even without extern/raw-int mode. + """ + with tempfile.TemporaryDirectory() as td: + data = _build_v10(Path(td), RAW_CALL_SOURCE, "rawcall") + f = flags(data) + assert f & 2 != 0, f"raw ABI call missing SYSTEM: {f:#x}" + assert f & 1 == 0, f"raw ABI call also claims PORTABLE: {f:#x}" + + +def test_portable_header_with_raw_abi_call_rejected(): + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_v10(tmp, RAW_CALL_SOURCE, "rawcallportable") + bad = tmp / "rawcallportable.ccr" + # Downgrade the truthful SYSTEM header to PORTABLE. The loader's graph + # classifier must reject it, not trust the header word. + bad.write_bytes(patch_u32(data, 12, 1)) + r = _run([COREARCH, bad, "--elf", "--static", "-o", tmp / "rawcallportable.elf"]) + assert r.returncode != 0, "PORTABLE header with a raw ABI call accepted" + assert "invalid" in (r.stdout + r.stderr) + + def test_portable_system_flags_rejected(): with tempfile.TemporaryDirectory() as td: tmp = Path(td) diff --git a/tools/baseline/canary_values.tsv b/tools/baseline/canary_values.tsv index 8340e14f..cac80733 100644 --- a/tools/baseline/canary_values.tsv +++ b/tools/baseline/canary_values.tsv @@ -177,11 +177,25 @@ # **后来人注意**:**任何新增编译器侧全局**都会移动这张表(登记 ⇒ 启动期 intern ⇒ id 顺移)。 # 这是**固有代价**,不是回归——但按本文件两级纪律,**必须显式声明 + 同批重锁 + 旧值留痕**。 +# ── 【本批重锁 2026-10-24】Task 8 review fix:raw ABI 调用成为图级 SYSTEM 边界 ── +# 四条 `.ccr`:size 全不变,sha 全变;canary ELF 逐字节不变(826d74cf…)。 +# 归因 = `ccr_compute_header_flags` 现在识别 IR_CALL 到保留的 raw ABI 内建 +# (cell_store/cell_load/load8/store8/load64/w32/load_str_ptr/store_str_ptr/ +# _dyncpy)⇒ 注入的 stdlib/runtime 函数使每个程序图都含 raw 调用 ⇒ header +# flags 从 PORTABLE(1) 变为 SYSTEM(2)。逐字节对照旧编译器产物:四条 `.ccr` +# **唯一差异 = 偏移 12 的 header flags 字节 `01 → 02`**,其余字节与尺寸全同; +# ELF canary 不变。D1 5/5 PASS。 +# 旧值留痕(自本批起作废): +# pa_ccr 1115f352656d6e53f388d48088616703e2c9df13c8233b9471bdd047aa16b367 93710 +# pa_static_ccr 5f1b90d4be268c350e0ae34b4d805732573d5a115f53a51df08d0ab4e2192630 93853 +# gt_ccr a1b3d07dc98772351edcdbf5798887e679caa9d452444ef6ad76c86ea2ff05ff 138672 +# gt_static_ccr bd7d2fc42282a3ca3905a567c8c6d577390262ec269002f96da2f59a8a40301c 138815 + canary_elf pa 826d74cf7f099f9758e11a64d443695ff8360327a88e7eb5922ddfd617e0dce5 53430 -pa_ccr pa.ccr 1115f352656d6e53f388d48088616703e2c9df13c8233b9471bdd047aa16b367 93710 -pa_static_ccr pa_st.bin.ccr 5f1b90d4be268c350e0ae34b4d805732573d5a115f53a51df08d0ab4e2192630 93853 -gt_ccr gt.ccr a1b3d07dc98772351edcdbf5798887e679caa9d452444ef6ad76c86ea2ff05ff 138672 -gt_static_ccr gt_st.bin.ccr bd7d2fc42282a3ca3905a567c8c6d577390262ec269002f96da2f59a8a40301c 138815 +pa_ccr pa.ccr f45a52584e647fa093e7b7d785aabfcd2d6a1ba520c2a560eaf0e0901a13cd2f 93710 +pa_static_ccr pa_st.bin.ccr d218b5218a9b05b32b3a9211ec3f6769084dd17b1bdf73012d56788b0631e388 93853 +gt_ccr gt.ccr 6f449f88f25291599e91b956f6f5951882453886747aa7873ecaf7235d765f65 138672 +gt_static_ccr gt_st.bin.ccr 95001eea331f8c3bad0b26ce33116b21a08b0231066267cbe476e51f4cf00288 138815 # ⚠ **本组为最终值(全部编译器源码改动落定后重测)**:重锁分量有两个,**都不是发射面泄漏**—— # ① **内容面**:`fmt.cr` 新增 `bool_str` ⇒ 凡 `import fmt` 的程序多一个函数体(ELF/`.ccr` 同增); # ② **编译器身份位移**(#123 同机制):编译器源码任何改动都会改变**其自身 intern 序** ⇒ 同语义下 From 86b142d4f68d017da5d921bf205ffa29c2df62a4 Mon Sep 17 00:00:00 2001 From: DslsDZC Date: Sat, 24 Oct 2026 03:29:04 +0800 Subject: [PATCH 57/58] fix: harden ccr v10 trust boundaries and existence lattice --- .../plans/2026-10-23-ccr-v10-migration.md | 6 + src/arch/x86_64/corearch.cr | 2 +- src/arch/x86_64/instr.cr | 4 + src/arch/x86_64/regalloc.cr | 48 +- src/ci/run.sh | 2 + src/compiler/ccr_io.cr | 409 +++++++++++++++--- src/compiler/ccr_types.cr | 122 +++++- src/compiler/cir_cache.cr | 4 + src/compiler/dyn_arr.cr | 21 +- src/compiler/globals.cr | 5 + src/lattice/ent_kernel.cr | 60 ++- src/os/linux/linux_frontend_abi.cr | 3 + src/runtime/runtime_frontend_abi.cr | 4 + tests/harness/ci_hook_allowlist.txt | 10 +- tests/selfhost/test_ccr_types.py | 117 ++++- tests/selfhost/test_ccr_v10_format.py | 130 ++++++ tests/selfhost/test_ccr_v7.py | 101 +++-- tests/selfhost/test_ccrb_bundle.py | 2 +- tools/baseline/canary_values.tsv | 21 +- 19 files changed, 931 insertions(+), 140 deletions(-) diff --git a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md index 33c576a6..828d6b10 100644 --- a/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md +++ b/docs/superpowers/plans/2026-10-23-ccr-v10-migration.md @@ -574,9 +574,15 @@ python3 tests/selfhost/test_opt_dex.py python3 tests/selfhost/test_bigint_arith.py python3 tests/selfhost/test_native_memory.py python3 tests/selfhost/test_interp_parity.py +python3 tests/harness/test_ci_hook_coverage.py bash tools/baseline/canary_check.sh ``` +Final-review 修复批补充(2026-10-24):v10 SYM `opt_meta` 恒为 0 计数、 +NOD opcode/operand 域由 loader 拒绝、非类型面 `type_id` 补齐语义域、ENT +消费盘面条目(含全局/参数行)、SYM/REG 跨引用校验,均已由上述 +`test_ccr_v10_format.py` 的负向夹具覆盖。 + 全部通过后,用三阶段自举确认: ```bash diff --git a/src/arch/x86_64/corearch.cr b/src/arch/x86_64/corearch.cr index 8f67f960..c16bd13f 100644 --- a/src/arch/x86_64/corearch.cr +++ b/src/arch/x86_64/corearch.cr @@ -253,7 +253,7 @@ fn dump_alloc_strides(): print(" elem ") print_i(elem) print(" stride ") - print_i(corearch_nod_alloc_stride(dv)) + print_i(corearch_nod_alloc_stride(ni, dv)) println("") ni = ni + 1 diff --git a/src/arch/x86_64/instr.cr b/src/arch/x86_64/instr.cr index 72af4e0b..048ccdf7 100644 --- a/src/arch/x86_64/instr.cr +++ b/src/arch/x86_64/instr.cr @@ -33,6 +33,10 @@ fn get_reg_for_var(var_idx: int) -> int: mk := r32(g_opt_meta, mo) if mk == OPT_KEY_REG_ASSIGN: data_len := r32(g_opt_meta, mo + 4) + // Defensive record bound: in-memory records are OPT_META_STRIDE + // bytes. A corrupt length must not walk past the record. + if data_len > OPT_META_STRIDE - 8: + data_len = OPT_META_STRIDE - 8 di : ., mut = 4 // skip count u32, pairs start at +4 loop: if di >= data_len: diff --git a/src/arch/x86_64/regalloc.cr b/src/arch/x86_64/regalloc.cr index f5989cf7..80566438 100644 --- a/src/arch/x86_64/regalloc.cr +++ b/src/arch/x86_64/regalloc.cr @@ -912,10 +912,17 @@ fn corearch_ptr_pointee(var: int) -> int: // Access width for IR_DEREF/IR_STORE_PTR. Mirrors the front-end derivation // ptr_access_width: pointee value size, falling back to one target slot. fn corearch_nod_access_width(n: int) -> int: - w := mp_size(corearch_ptr_pointee(nod_s1(n))) - if w <= 0: + // Prefer the node's semantic type_id (the pointee domain). The variable row + // remains a fallback for nodes whose domain was unavailable at save time. + tid := nod_type_id(n) + if tid >= 0 && tid < g_type_count: + w := mp_size(tid) + if w > 0: + return w + w2 := mp_size(corearch_ptr_pointee(nod_s1(n))) + if w2 <= 0: return mp_stride(TI_INT) - return w + return w2 // Element stride for IR_ALLOC_ARRAY, derived from the destination variable's // semantic array element type. An uninitialised array declaration @@ -924,13 +931,19 @@ fn corearch_nod_access_width(n: int) -> int: // result arrays) have a scalar/unit row and no element type to read; for those // the explicit single-slot fallback applies. An unreadable type row also falls // back explicitly instead of assuming every element is an int slot. -fn corearch_nod_alloc_stride(dest: int) -> int: +fn corearch_nod_alloc_stride(n: int, dest: int) -> int: + // v10 type_id for ALLOC_ARRAY is the element domain. Use it first; the + // destination variable's array row is the fallback when the domain was not + // derivable at save time. + tid := nod_type_id(n) + if tid >= 0 && tid < g_type_count: + return mp_stride(tid) if dest < 0: return mp_stride(TI_INT) ti := irv_type(dest) if ti < 0 || ti >= g_type_count: return mp_stride(TI_INT) - if get_type_kind(ti) == TYP_ARRAY: + if get_type_kind(ti) == TYP_ARRAY || get_type_kind(ti) == TYP_SLICE: elem := get_type_data(ti) if elem >= 0 && elem < g_type_count: return mp_stride(elem) @@ -950,7 +963,12 @@ fn corearch_alloc_size_of_var(v: int) -> int: if nod_dest(ni) == v: aop := nod_op(ni) if aop == IR_ALLOC_ARRAY: - return mp_extent(nod_s1(ni), TI_INT) + cnt := nod_s1(ni) + if cnt <= 0: + return 0 + // Element stride, not one int slot: bool/char/dex elements and + // natively sized fields make mp_extent(n, TI_INT) wrong. + return cnt * corearch_nod_alloc_stride(ni, v) if aop == IR_ALLOC_STRUCT: nm := nod_s3(ni) si : ., mut = 0 @@ -958,7 +976,21 @@ fn corearch_alloc_size_of_var(v: int) -> int: if si >= g_struct_count: break if si_name(si) == nm: - return mp_extent(si_field_count(si), TI_INT) + fc := si_field_count(si) + total : ., mut = 0 + fj : ., mut = 0 + loop: + if fj >= fc: + break + ft := si_field_type(si, fj) + st := mp_stride(ft) + if st <= 0: + st = mp_stride(TI_INT) + total = total + st + fj = fj + 1 + if total <= 0: + return mp_stride(TI_INT) + return total si = si + 1 return mp_stride(TI_INT) ni = ni + 1 @@ -1011,7 +1043,7 @@ fn build_linear_schedule() -> int: // for pointer stores/loads is derived from the allocation node; a set // flag with no resolvable allocation is a malformed file (fail closed). if op == IR_ALLOC_ARRAY: - iri_set_s2(ni, corearch_nod_alloc_stride(nod_dest(ni))) + iri_set_s2(ni, corearch_nod_alloc_stride(ni, nod_dest(ni))) else if op == IR_DEREF || op == IR_STORE_PTR: iri_set_tk(ni, corearch_nod_access_width(ni)) bsz := corearch_nod_runtime_bound_size(ni) diff --git a/src/ci/run.sh b/src/ci/run.sh index 3f788600..8b6c166e 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -134,6 +134,8 @@ case "$CI_JOB_NAME" in python3 tests/selfhost/test_type_engine.py # 类型项引擎自测通道(selftest-types **417 例**——P5 收官 404 + R2 P6 Task 2 增 11(`ts_t2_neg_run`:¬ 面同类原子身份判据的邻域覆盖——同身份 / μ 字面 / ⊤ₖ 双向 / 跨枚举同名 / 同枚举异变体 / 令牌字面 / AK_NAMED / 空链与链元素域外 / 参数化面 / REF mut 标记面;三例 `t3.*_over_claim` 为**重钉**(0→1,不增减);P0..P6 T2 一路漏挂)+ ccr v10 Task 5 复核 +1(`t4.tk_store_ptr_width8_rejected`:旧宽度码 8 在 IR_STORE_PTR 上仍归辅码面) python3 tests/selfhost/test_iface_ops.py # R2 P2b Task 4/5/6:接口查表接线(算术/逻辑/条件三门 + 索引兜底门 + TY→TI 单表合一;正控/负控/登记面/站点域 76 例 + R2 P6 Task 4a 增 2(方法调用站点 never 透传 + 泛型方法登记钉)= 78 例) python3 tests/selfhost/test_purity.py # 效应/纯度批判据(真纯度 13 例 + 链语义 4 例的自测通道驱动) + python3 tests/selfhost/test_ccr_v10_format.py # v10 wire oracle + header/SYM/NOD negative fixtures(Task 1 + final-review Critical 1/2/6) + python3 tests/selfhost/test_ccr_v10_stream.py # v10 canonical forward-only loader structure + truncation/order negative fixtures python3 tests/selfhost/test_ccr_v7.py # 效应/纯度批 Task 3:.ccr 层三条边集语义断言(取代「与旧版逐字节同」) python3 tests/selfhost/test_ccr_types.py # R2 P4 Task 1/2/3/4 + P5 Task 2:段机制(版本 8 + loader 三闸/必备集 + 旧 v7 拒收)8 例 + TYPE 内容面(行表/项 DAG 序列化 + 确定性装填 + corearch 读回对拍 + loader 负分支)10 例 + IFACE 内容面(五小节/条目扩列 16/形状命名化/签名项化/impl 边与方法表/corearch 读回/跨段引用域/确定性)10 例 + DFNode.TK 项槽(逐节点规则 + dex 语料 + NOD 36B/同序 + 冷/暖快照对称 + dump 零产物影响 + .cir 布局未变)6 例 = 34 例 + P5 T2 单槽化 6 例(复合行走辅码 + 互斥不变量 + F1 hotpatch 无面 + 复合行冷/暖对称 + 复合行 NOD 同序 + 快照盘面派生码保真)= 40 例 + R2 P6 Task 3 项索引 8 例(版本闸 v8 拒收 / NOD 40B 字段序 / 项索引落盘读回对拍(非退化)/ 两通道面一致 / 辅码面与复合行保真 / 项索引越界与不一致 ⇒ 拒绝 / 段界检 / D18 纯度静态)= 49 例(**+1 他批新增,本批机械核对同步**:实测 49/49) python3 tests/selfhost/test_impl.py diff --git a/src/compiler/ccr_io.cr b/src/compiler/ccr_io.cr index 3138f648..5d3c9a58 100644 --- a/src/compiler/ccr_io.cr +++ b/src/compiler/ccr_io.cr @@ -267,7 +267,7 @@ fn ccr_func_root_sg(func_i: int) -> int: si = si + 1 return -1 -// Segment size calculation. Header plus segment table = 16 + 12*8 = 112 bytes. +// Segment size calculation. Header plus segment table = 16 + 12*9 = 124 bytes. // Each segment carries a u32 count. The writer and size calculation must agree; // the final segment end must equal the file size. @@ -320,14 +320,11 @@ fn ccr_sym_seg_size() -> int: sz = sz + 8 + tc * 4 // variant name + tc + types vi = vi + 1 ei = ei + 1 - // opt_meta: opt_count + {key, len, data} + // opt_meta: a single zero count. v10 does not carry register/stack/CSE + // candidates in `.ccr`; they are instance-side optimization data. The 4B + // zero-count placeholder keeps the SYM section boundaries stable for readers + // and size tests, and the loader rejects any nonzero count. sz = sz + 4 - mi : ., mut = 0 - loop: - if mi >= g_opt_meta_count: - break - sz = sz + 8 + cell_load32(g_opt_meta, mi * OPT_META_STRIDE + 4) - mi = mi + 1 return sz fn ccr_nod_seg_size() -> int: @@ -366,8 +363,19 @@ fn ccr_op_is_system(op: int, arena_count: int) -> int: // IR_CALL stores the callee string index in src3; ordinary arguments live in // the argument chain rooted at s1 and must never be inspected here. fn ccr_raw_abi_builtin(name: string) -> int: + // Raw memory/string-header runtime ABI. if name == "cell_store" || name == "cell_load" || name == "load8" || name == "store8" || name == "load64" || name == "w32" || name == "load_str_ptr" || name == "store_str_ptr" || name == "_dyncpy": return 1 + // Runtime/OS execution ABI. These names are registered by the frontend ABI + // axes (runtime_frontend_abi.cr / linux_frontend_abi.cr) and the target + // composition root. A direct IR_CALL is a SYSTEM boundary even when the call + // is written as ordinary source. + if name == "sched_call_0" || name == "sched_call_1" || name == "sched_call_2" || name == "sched_call_3" || name == "sched_call_4": + return 1 + if name == "fiber_switch" || name == "fiber_init" || name == "sched_go" || name == "m_start_workers" || name == "g_set_curg" || name == "g_get_curg": + return 1 + if name == "get_arg" || name == "syscall3" || name == "syscall4": + return 1 return 0 fn ccr_raw_abi_call(sym: int) -> int: @@ -380,6 +388,205 @@ fn ccr_op_is_concurrent(op: int) -> int: return 1 return 0 +// v10 opcode table. The IR opcode values are assigned densely 0..53 in ast.cr; +// anything outside that table is not a v10 operation. Rejecting unknown opcodes +// before emission also prevents emit_instr from silently falling through as a +// zero-byte instruction. +fn ccr_op_is_known(op: int) -> int: + if op >= IR_NOP && op <= IR_STR_OF: + return 1 + return 0 + +// Operand domain helpers. -1 is the reserved "none" id; every other id must be +// inside the domain reconstructed from the earlier segments. +fn ccr_nod_var_ok(v: int) -> int: + if v >= -1 && v < g_ir_var_count: + return 1 + return 0 + +fn ccr_nod_str_ok(v: int) -> int: + if v >= 0 && v < g_str_count: + return 1 + return 0 + +fn ccr_nod_type_ok(v: int) -> int: + if v >= -1 && v < g_type_count: + return 1 + return 0 + +// Argument chain: s1 is the first var of a contiguous run of `count` argument +// vars (ir_gen packs non-contiguous calls). Zero args carries -1. +fn ccr_nod_args_ok(first: int, count: int) -> int: + if count < 0: + return 0 + if count == 0: + if first == -1 || (first >= 0 && first < g_ir_var_count): + return 1 + return 0 + if first < 0 || first + count > g_ir_var_count: + return 0 + return 1 + +fn ccr_label_known(ids: string, n: int, needle: int) -> int: + if needle < 0: + return 0 + i : ., mut = 0 + loop: + if i >= n: + break + if cell_load(ids, i * 8) == needle: + return 1 + i = i + 1 + return 0 + +// Validate NOD operand domains after the whole NOD segment is loaded. The +// loader is the primary rejection point; irv_type/g2_slot guards are a second +// line only. Labels are graph ids, so pass 1 collects the LABEL definitions and +// pass 2 resolves BRANCH/JUMP targets against them. +fn ccr_validate_nod_operands() -> int: + n := g_v7_nod_count + ids := alloc((n + 1) * 8) + ln : ., mut = 0 + i : ., mut = 0 + loop: + if i >= n: + break + if nod_op(i) == IR_LABEL: + if nod_s1(i) < 0: + return -1 + cell_store(ids, ln * 8, nod_s1(i)) + ln = ln + 1 + i = i + 1 + i = 0 + loop: + if i >= n: + break + op := nod_op(i) + d := nod_dest(i) + s1 := nod_s1(i) + s2 := nod_s2(i) + s3 := nod_s3(i) + tid := nod_type_id(i) + if tid < -1 || tid >= g_type_count: + return -1 + if op == IR_NOP || op == IR_APPROX || op == IR_FAST || op == IR_NO_BOUNDS_CHECK || op == IR_UNROLL: + i = i + 1 + continue + if op == IR_CONST: + if ccr_nod_var_ok(d) == 0: + return -1 + if tid == TI_STR && ccr_nod_str_ok(s1) == 0: + return -1 + else if op == IR_CONST_BIG: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s1) == 0: + return -1 + else if op == IR_BINARY: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_UNARY: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_CALL: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s3) == 0 || ccr_nod_args_ok(s1, s2) == 0: + return -1 + else if op == IR_RETURN: + if ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_ALLOC || op == IR_ALLOC_ARRAY: + if ccr_nod_var_ok(d) == 0: + return -1 + else if op == IR_ALLOC_STRUCT: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s3) == 0: + return -1 + else if op == IR_STORE: + if ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_LOAD || op == IR_LOAD_FIELD || op == IR_LOAD_ENUM_TAG: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_STORE_FIELD: + if ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_LOAD_INDEX: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_STORE_INDEX: + if ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_LOAD_INDEX_VAR || op == IR_STORE_INDEX_VAR: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_MAKE_ENUM: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s1) == 0 || s2 < 0: + return -1 + else if op == IR_REF: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_BRANCH: + if ccr_nod_var_ok(s1) == 0: + return -1 + if ccr_label_known(ids, ln, s2) == 0 || ccr_label_known(ids, ln, s3) == 0: + return -1 + else if op == IR_JUMP || op == IR_LABEL: + if ccr_label_known(ids, ln, s1) == 0: + return -1 + else if op == IR_PHI: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_SLICE: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0 || ccr_nod_var_ok(s3) == 0: + return -1 + else if op == IR_DEREF: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_STORE_PTR: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_ADDR_INDEX: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_var_ok(s2) == 0: + return -1 + else if op == IR_SPAWN: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s3) == 0 || ccr_nod_args_ok(s1, s2) == 0: + return -1 + else if op == IR_YIELD: + if ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_AWAIT: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_BOUNDS_CHECK: + if ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_ARENA_NEW: + if ccr_nod_var_ok(d) == 0: + return -1 + else if op == IR_ARENA_RESET: + if ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_INLINE: + if ccr_nod_var_ok(s1) == 0 && ccr_nod_str_ok(s1) == 0: + return -1 + else if op == IR_SECTION: + if ccr_nod_str_ok(s1) == 0: + return -1 + else if op == IR_HOTPATCH_ROUTE || op == IR_CALL_EXTERN: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s1) == 0 || ccr_nod_args_ok(s2, s3) == 0: + return -1 + else if op == IR_FNADDR: + if ccr_nod_var_ok(d) == 0 || ccr_nod_str_ok(s1) == 0: + return -1 + else if op == IR_DYN_TAG || op == IR_DYN_VAL || op == IR_LAZY_THUNK || op == IR_LAZY_FORCE || op == IR_I2F || op == IR_F2I || op == IR_PTR_OF || op == IR_STR_OF: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_DYN_PACK: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0: + return -1 + else if op == IR_DYN_DISPATCH: + if ccr_nod_var_ok(d) == 0 || ccr_nod_var_ok(s1) == 0 || ccr_nod_str_ok(s2) == 0: + return -1 + i = i + 1 + return 0 + // v10 src3 is an id/op/index/symbol for these ops. Every other op must carry 0 // in src3: a nonzero value would be a legacy width/scale/byte-extent target // fact. ALLOC_ARRAY/ADDR_INDEX/DEREF/STORE_PTR are handled explicitly because @@ -928,27 +1135,11 @@ fn save_ccr(path: string) -> int: ei2 = ei2 + 1 // === SYM: opt_meta === - buf_write_u32(buf, pos, g_opt_meta_count) + // v10 never serializes g_opt_meta. The field is a fixed zero count so the + // SYM body remains byte-stable; nonzero counts are rejected on load. Register + // assignments are an instance-side optimization result, not a program fact. + buf_write_u32(buf, pos, 0) pos = pos + 4 - mi : ., mut = 0 - loop: - if mi >= g_opt_meta_count: - break - mo := mi * OPT_META_STRIDE - mk := cell_load32(g_opt_meta, mo) // key (u8 padded to u64) - md_len := cell_load32(g_opt_meta, mo + 4) // data length - buf_write_u32(buf, pos, mk) - pos = pos + 4 - buf_write_u32(buf, pos, md_len) - pos = pos + 4 - di : ., mut = 0 - loop: - if di >= md_len: - break - cell_store_byte(buf, pos, cell_load_byte(g_opt_meta, mo + 8 + di)) - pos = pos + 1 - di = di + 1 - mi = mi + 1 // === NOD: instructions === // 40B each. NOD id is file order and equals global instruction order. Each @@ -1139,6 +1330,12 @@ fn save_ccr(path: string) -> int: buf_write_u32(buf, pos, 0) pos = pos + 4 + // The size calculator and the writer are two independent walks. A mismatch + // means a section changed without updating calc_ccr_size; refuse to write a + // truncated or over-long image. + if pos != tsz: + return -1 + // Use syscall directly (write_file uses str_len which stops at null) fd := syscall3(2, path, 577, 420) // open(O_WRONLY|O_CREAT|O_TRUNC, 0644) if fd < 0: @@ -1217,6 +1414,11 @@ fn load_ccr(data: string, fsize: int) -> int: // must be zero. PORTABLE excludes SYSTEM; CONCURRENT implies SYSTEM. if rsv > CCR_FLAG_KNOWN_MASK: return -1 + // Exactly one of PORTABLE/SYSTEM must be set. A zero header would leave the + // program's system-boundary status undefined, so it is rejected rather than + // documented as an implicit mode. + if (rsv % 4) != CCR_FLAG_PORTABLE && (rsv % 4) != CCR_FLAG_SYSTEM: + return -1 if (rsv % 2 == 1) && ((rsv / CCR_FLAG_SYSTEM) % 2 == 1): return -1 if (rsv % 2 == 1) && ((rsv / CCR_FLAG_CONCURRENT) % 2 == 1): @@ -1359,6 +1561,7 @@ fn load_ccr(data: string, fsize: int) -> int: g_sg_count = 0 g_ir_global_count = 0 g_opt_meta_count = 0 + g_ent_loaded = 0 g_entry_count = 0 g_v7_edge_count = 0 g_v7_nod_count = 0 // NOD 对象缓冲计数(NOD 段载入后置位) @@ -1739,12 +1942,17 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, ESZ_GLOBAL_DISK, seg_end4): return -1 - gname_ni := buf_read_u32(data, pos) + gname_ni := buf_read_i32(data, pos) pos = pos + 4 - gtype := buf_read_u32(data, pos) + gtype := buf_read_i32(data, pos) pos = pos + 4 ginit_val := buf_read_i64(data, pos) pos = pos + 8 + // Cross-segment domains: name -> STR, type -> TYPE (-1 allowed). + if gname_ni < 0 || gname_ni >= g_str_count: + return -1 + if ccr_nod_type_ok(gtype) == 0: + return -1 grow_ir_vars(g_ir_var_count + 1) gvar := g_ir_var_count irv_set_name(gvar, gname_ni) @@ -1776,11 +1984,11 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, ESZ_FUNC_HEAD_DISK, seg_end4): return -1 - fname := buf_read_u32(data, pos) + fname := buf_read_i32(data, pos) pos = pos + 4 fpc := buf_read_u32(data, pos) pos = pos + 4 - fret := buf_read_u32(data, pos) + fret := buf_read_i32(data, pos) pos = pos + 4 froot := buf_read_i32(data, pos) pos = pos + 4 @@ -1788,6 +1996,10 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 fle := buf_read_i32(data, pos) pos = pos + 4 + if fname < 0 || fname >= g_str_count: + return -1 + if ccr_nod_type_ok(fret) == 0: + return -1 if froot < 0: return -1 // 每函数必有根 region(df_begin_func) @@ -1828,10 +2040,14 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, ESZ_VARDECL_DISK, seg_end4): return -1 - dname := buf_read_u32(data, pos) + dname := buf_read_i32(data, pos) pos = pos + 4 - dtype := buf_read_u32(data, pos) + dtype := buf_read_i32(data, pos) pos = pos + 4 + if dname < 0 || dname >= g_str_count: + return -1 + if ccr_nod_type_ok(dtype) == 0: + return -1 grow_ir_vars(g_ir_var_count + 1) drow := g_ir_var_count irv_set_name(drow, dname) @@ -1859,8 +2075,10 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, 4, seg_end4): return -1 - scv := buf_read_u32(data, pos) + scv := buf_read_i32(data, pos) pos = pos + 4 + if scv < 0 || scv >= g_str_count: + return -1 cell_store(g_ir_str_consts, sci * 8, scv) g_ir_str_const_count = sci + 1 sci = sci + 1 @@ -1880,10 +2098,12 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, 8, seg_end4): return -1 - sname_ni := buf_read_u32(data, pos) + sname_ni := buf_read_i32(data, pos) pos = pos + 4 fc := buf_read_u32(data, pos) pos = pos + 4 + if sname_ni < 0 || sname_ni >= g_str_count: + return -1 // 字段数无硬上限(侧表承载),因此旧 MAX_STRUCT_FIELDS // 读回检查与 16 槽清零循环一并删除;段界核算保留(fc 越段,因此拒绝,判定约定)。 if fc > (seg_end4 - pos) / 8: @@ -1904,10 +2124,14 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if fi2 >= fc: break - fn_ni := buf_read_u32(data, pos) + fn_ni := buf_read_i32(data, pos) pos = pos + 4 - ft := buf_read_u32(data, pos) + ft := buf_read_i32(data, pos) pos = pos + 4 + if fn_ni < 0 || fn_ni >= g_str_count: + return -1 + if ccr_nod_type_ok(ft) == 0: + return -1 si_set_field_name(sti, fi2, fn_ni) si_set_field_type(sti, fi2, ft) fi2 = fi2 + 1 @@ -1929,10 +2153,12 @@ fn load_ccr(data: string, fsize: int) -> int: break if !ccr_has_bytes(pos, 8, seg_end4): return -1 - ename_ni := buf_read_u32(data, pos) + ename_ni := buf_read_i32(data, pos) pos = pos + 4 vc := buf_read_u32(data, pos) pos = pos + 4 + if ename_ni < 0 || ename_ni >= g_str_count: + return -1 // 变体/携带值数无硬上限(侧表),因此两处旧检查与 16 槽清零 // 循环删除;段界核算保留(判定约定) if vc > (seg_end4 - pos) / 8: @@ -1952,10 +2178,12 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if vi3 >= vc: break - vni := buf_read_u32(data, pos) + vni := buf_read_i32(data, pos) pos = pos + 4 tc := buf_read_u32(data, pos) pos = pos + 4 + if vni < 0 || vni >= g_str_count: + return -1 if tc > (seg_end4 - pos) / 4: return -1 ei_set_variant_name(ei, vi3, vni) @@ -1964,44 +2192,26 @@ fn load_ccr(data: string, fsize: int) -> int: loop: if tf >= tc: break - tval := buf_read_u32(data, pos) + tval := buf_read_i32(data, pos) pos = pos + 4 + if ccr_nod_type_ok(tval) == 0: + return -1 ei_set_variant_type(ei, vi3, tf, tval) tf = tf + 1 vi3 = vi3 + 1 ei = ei + 1 // === SYM: opt_meta === + // v10 has no optimization metadata channel. Only the zero-count placeholder + // is legal; any nonzero count (or payload) is a target/instance fact smuggled + // into the program carrier and must be rejected. g_opt_meta stays empty and is + // populated in memory only by the instance allocator. if !ccr_has_bytes(pos, 4, seg_end4): return -1 mc := buf_read_u32(data, pos) pos = pos + 4 - mi : ., mut = 0 - loop: - if mi >= mc: - break - if !ccr_has_bytes(pos, 8, seg_end4): - return -1 - mk := buf_read_u32(data, pos) - pos = pos + 4 - md_len := buf_read_u32(data, pos) - pos = pos + 4 - if md_len > OPT_META_STRIDE - 8 || !ccr_has_bytes(pos, md_len, seg_end4): - return -1 - // Allocate and store entry - grow_opt_meta(mi + 1) - mo := mi * OPT_META_STRIDE - cell_store32(g_opt_meta, mo, mk) - cell_store32(g_opt_meta, mo + 4, md_len) - di : ., mut = 0 - loop: - if di >= md_len: - break - cell_store_byte(g_opt_meta, mo + 8 + di, cell_load_byte(data, pos)) - pos = pos + 1 - di = di + 1 - g_opt_meta_count = mi + 1 - mi = mi + 1 + if mc != 0: + return -1 if pos != seg_end4: return -1 @@ -2035,6 +2245,10 @@ fn load_ccr(data: string, fsize: int) -> int: return -1 opcode := buf_read_u32(data, pos) pos = pos + 4 + // v10 opcode table gate. Unknown or out-of-range opcodes are rejected + // before any operand is interpreted; emit_instr has no generic fallthrough. + if ccr_op_is_known(opcode) == 0: + return -1 dest := buf_read_i32(data, pos) pos = pos + 4 s1 := buf_read_i64(data, pos) @@ -2084,6 +2298,11 @@ fn load_ccr(data: string, fsize: int) -> int: g_v7_nod_count = instr_cnt if pos != seg_end5: return -1 + // Operand domains (vars, labels, strings, symbols) are graph facts that the + // NOD segment must prove before any projection runs. This is the primary + // rejection point; emitter-side guards are defensive only. + if ccr_validate_nod_operands() != 0: + return -1 // Header SYSTEM/CONCURRENT bits must agree with the graph. This needs NOD // only, so it runs here (TYPE is already rebuilt; ENT/REG/EDG are not). @@ -2156,8 +2375,28 @@ fn load_ccr(data: string, fsize: int) -> int: // 全属唯一函数,段序与函数序一致)。重建结果与 SYM func 记录 // first_ent/last_ent 逐函数对照(同信息双写,失配 = 格式不一致)。 grow_func_entry_meta(func_cnt) - pf : ., mut = 0 + // Global existence rows form a leading block: one row per global var, in + // namespace order, with the global flag and def_nod = -1. Function blocks + // start after this prefix. pe : ., mut = 0 + gg : ., mut = 0 + loop: + if pe >= g_entry_count: + break + pgv := buf_read_i32(g_ir_entries, pe * ESZ_ENTRY + OFF_ENTRY_VAR) + if pgv >= g_ir_global_count: + break + if pgv != gg: + return -1 + if ccr_ent_flags(pe) != ENTRY_FLAG_GLOBAL: + return -1 + if ccr_ent_def(pe) != -1: + return -1 + gg = gg + 1 + pe = pe + 1 + if gg != g_ir_global_count: + return -1 + pf : ., mut = 0 loop: if pf >= func_cnt: break @@ -2185,6 +2424,9 @@ fn load_ccr(data: string, fsize: int) -> int: pf = pf + 1 if pe != g_entry_count: return -1 + // ENT is now the loaded existence table. compute_live_ranges must not + // rebuild it from the NOD mirror; the file is the authority. + g_ent_loaded = 1 // === REG: region(24B each,spec 3.5 字段序 {kind, parent, enter_nod, // exit_nod, first_ent, last_ent})=== @@ -2221,9 +2463,24 @@ fn load_ccr(data: string, fsize: int) -> int: pos = pos + 4 if rex < ren: return -1 + // Region coordinates are NOD ids; the half-open span must stay inside the + // NOD space. + if ren < 0 || rex > instr_cnt: + return -1 + // Parent is a REG record id, or -1 for a root. Self-parents and cycles + // are rejected in the second pass below. + if rp < -1 || rp >= sg_n: + return -1 + if rp == sg_i: + return -1 // 未闭合 -> ncount 不可派生 if rfe < -1 || rle < -1 || (rfe == -1) != (rle == -1): return -1 + if rfe != -1: + if rfe >= g_entry_count || rle >= g_entry_count: + return -1 + if rle < rfe: + return -1 cell_store(g_sgs, f + OFF_SG_KIND, rk) cell_store(g_sgs, f + OFF_SG_ENTER, ren) cell_store(g_sgs, f + OFF_SG_EXIT, rex) @@ -2241,6 +2498,22 @@ fn load_ccr(data: string, fsize: int) -> int: if rfcnt != func_cnt: return -1 // 根记录少于函数记录(每函数必有根) + // Parent-chain acyclicity. Each chain has at most sg_n distinct records, so a + // walk longer than sg_n proves a cycle. Bounds were checked per record. + cci : ., mut = 0 + loop: + if cci >= sg_n: + break + cp2 : ., mut = cell_load(g_sgs, cci * ESZ_SG + OFF_SG_PARENT) + steps : ., mut = 0 + loop: + if cp2 < 0: + break + steps = steps + 1 + if steps > sg_n: + return -1 + cp2 = cell_load(g_sgs, cp2 * ESZ_SG + OFF_SG_PARENT) + cci = cci + 1 g_sg_count = sg_n // 函数指令边界回填:instr_start = root_region enter, instr_count = exit − enter diff --git a/src/compiler/ccr_types.cr b/src/compiler/ccr_types.cr index 42929059..dfcfa6e4 100644 --- a/src/compiler/ccr_types.cr +++ b/src/compiler/ccr_types.cr @@ -337,6 +337,113 @@ fn ccr_seg_prepare_save() -> int: // and no semantic feature, so it is explicitly classified as featureless. // This is a pure query/derivation: it does not mutate the type tables, the term // table, or g_tk_face_fail. +// Semantic type row for a variable id (-1 when untyped/out of range). +fn ccr_var_type_row(v: int) -> int: + if v < 0 || v >= g_ir_var_count: + return -1 + t := irv_type(v) + if t < 0 || t >= g_type_count: + return -1 + return t + +// Pointee/element row of a pointer/reference/array/slice row. +fn ccr_inner_type_row(t: int) -> int: + if t < 0 || t >= g_type_count: + return -1 + k := get_type_kind(t) + if k == TYP_PTR || k == TYP_REF || k == TYP_ARRAY || k == TYP_SLICE || k == TYP_OPTIONAL: + d := get_type_data(t) + if d >= 0 && d < g_type_count: + return d + return -1 + +// Named/struct row matching a type-name string index. +fn ccr_named_row_of_name(ni: int) -> int: + if ni < 0: + return -1 + i : ., mut = 0 + loop: + if i >= g_type_count: + break + if get_type_kind(i) == TYP_NAMED && get_type_data(i) == ni: + return i + i = i + 1 + return -1 + +// Per-op semantic domain for the v10 NOD type_id. Type-face ops keep the +// existing reversible-row rule; the ops below carry a domain even though their +// legacy type code was a target/aux fact. -1 means the domain is genuinely +// unavailable (fail closed: corearch may then only fall back to the variable +// table, never to a guessed machine width). +fn ccr_nod_type_domain(ii: int, op: int, d: int, s1: int, s2: int, s3: int) -> int: + if op == IR_DEREF: + p := ccr_var_type_row(s1) + if p >= 0: + k := get_type_kind(p) + if k == TYP_PTR || k == TYP_REF: + pin := ccr_inner_type_row(p) + if pin >= 0: + return pin + return ccr_var_type_row(d) + if op == IR_STORE_PTR: + return ccr_var_type_row(s2) + if op == IR_ALLOC_ARRAY: + t := ccr_var_type_row(d) + ein := ccr_inner_type_row(t) + if ein >= 0: + return ein + // Some literal/tuple allocations get their array row after the node. + // Fall back to the first value stored into this base. + j : ., mut = 0 + loop: + if j >= g_ir_instr_count: + break + jop := iri_op(j) + if (jop == IR_STORE_INDEX || jop == IR_STORE_INDEX_VAR) && iri_s1(j) == d: + vt := ccr_var_type_row(iri_s2(j)) + if vt >= 0: + return vt + j = j + 1 + return -1 + if op == IR_ALLOC_STRUCT: + t := ccr_var_type_row(d) + if t >= 0: + k := get_type_kind(t) + if k == TYP_NAMED || k == TYP_GENERIC_APPLY: + return t + return ccr_named_row_of_name(s3) + if op == IR_LOAD: + return ccr_var_type_row(d) + if op == IR_STORE: + return ccr_var_type_row(s2) + if op == IR_ALLOC: + return ccr_var_type_row(d) + if op == IR_ADDR_INDEX: + t := ccr_var_type_row(d) + ain := ccr_inner_type_row(t) + if ain >= 0: + return ain + return t + if op == IR_LOAD_INDEX || op == IR_LOAD_INDEX_VAR: + return ccr_var_type_row(d) + if op == IR_STORE_INDEX: + return ccr_var_type_row(s2) + if op == IR_STORE_INDEX_VAR: + return ccr_var_type_row(d) + if op == IR_LOAD_FIELD: + return ccr_var_type_row(d) + if op == IR_STORE_FIELD: + return ccr_var_type_row(s2) + if op == IR_SLICE: + return ccr_var_type_row(d) + if op == IR_MAKE_ENUM: + return ccr_var_type_row(d) + if op == IR_LOAD_ENUM_TAG: + return ccr_var_type_row(d) + // Listed ops whose domain is genuinely unavailable from the current IR + // state. They remain -1 rather than guessing. + return -1 + fn ccr_nod_type_populate() -> int: n := g_ir_instr_count g_ccr_nod_type = alloc((n + 8) * 4) @@ -353,10 +460,17 @@ fn ccr_nod_type_populate() -> int: fl : ., mut = 0 if sh_tk_face_of_code(op, tk) == 0: tid = tk - else if sh_tk_is_type_face(op) != 0: - // Composite row or out-of-range/untranslatable type code: no - // reversible TYPE row. Task 3 fixes type_id = -1 here. - tid = -1 + else: + // Non-face ops carry a semantic domain when the variable table can + // supply it (DEREF pointee, STORE value, ALLOC element/struct, + // field/index value domains). -1 stays "unavailable". + dt := ccr_nod_type_domain(ii, op, iri_dest(ii), iri_s1(ii), iri_s2(ii), iri_s3(ii)) + if dt >= 0: + tid = dt + if sh_tk_is_type_face(op) != 0: + // Type-face nodes carry no semantic feature bits. A composite row + // stays -1 (not a reversible atom row). + fl = 0 else if op == IR_BOUNDS_CHECK: if tk != 0: fl = fl + CCR_NOD_FLAG_DYNAMIC_LIMIT diff --git a/src/compiler/cir_cache.cr b/src/compiler/cir_cache.cr index 8295a77d..7b71eaf9 100644 --- a/src/compiler/cir_cache.cr +++ b/src/compiler/cir_cache.cr @@ -49,6 +49,10 @@ // semantics. A cache miss is a harmless rebuild. The compiler identity also // invalidates all entries when the running binary changes, so source edits do // not need a manual bump for body changes. +// The v10 `.ccr` wire migration (removing SYM opt_meta and adding semantic +// NOD type ids) does not change this snapshot layout or its derived-code +// contract. The stored records, journal, and trailer are unchanged, so no +// version bump is required for that migration; only `.ccr` bytes changed. // // Magic value: // The magic is a negative 64-bit constant written as signed decimal. The diff --git a/src/compiler/dyn_arr.cr b/src/compiler/dyn_arr.cr index 8f388d54..2844f338 100644 --- a/src/compiler/dyn_arr.cr +++ b/src/compiler/dyn_arr.cr @@ -128,7 +128,17 @@ OFF_ENTRY_LE : int = 12 // 版本存在区间终点(全局指令序) // Runtime/instance-only allocator slot (-1 = unassigned). This is never // serialized into `.ccr`; v10 ENT has no home field. OFF_ENTRY_RUNTIME_HOME : int = 16 -OFF_ENTRY_FLAGS : int = 20 // 位 0 预留:无配方(条款 4b) +OFF_ENTRY_FLAGS : int = 20 // 见 ENTRY_FLAG_*(v10 spec §5) +// v10 ENT flags. Bits 4+ are reserved and rejected by the loader. +// bit 0 no generating method +// bit 1 parameter +// bit 2 global +// bit 3 removal candidate +ENTRY_FLAG_NO_DEF : int = 1 +ENTRY_FLAG_PARAM : int = 2 +ENTRY_FLAG_GLOBAL : int = 4 +ENTRY_FLAG_REMOVABLE : int = 8 +ENTRY_FLAG_KNOWN_MASK : int = 15 // Subgraph entry (48 bytes each) ESZ_SG : int = 48 @@ -593,10 +603,19 @@ fn sym_set_node(n: int, v: int): // IRVar helpers fn irv_name(n: int) -> int: + if n < 0 || n >= g_ir_var_count: + return -1 return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME) fn irv_id(n: int) -> int: + if n < 0 || n >= g_ir_var_count: + return -1 return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_ID) fn irv_type(n: int) -> int: + // Defensive bound: the loader rejects out-of-domain operands, but a few + // diagnostic/projection paths still call this with arbitrary ints. Returning + // -1 keeps them from reading outside the var table. + if n < 0 || n >= g_ir_var_count: + return -1 return cell_load(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_TYPE) fn irv_set_name(n: int, v: int): cell_store(g_ir_vars, n * ESZ_IRVAR + OFF_IRV_NAME, v) diff --git a/src/compiler/globals.cr b/src/compiler/globals.cr index 95a6aceb..a67f9f77 100644 --- a/src/compiler/globals.cr +++ b/src/compiler/globals.cr @@ -357,6 +357,11 @@ g_live_range_cap : int, mut g_ir_entries : string, mut g_entry_count : int, mut g_entry_cap : int, mut +// 1 = the entry table came from the v10 ENT segment. compute_live_ranges keeps +// those entries instead of rebuilding them, so allocation and diagnostics consume +// the loaded existence lattice rather than a recomputed mirror. The corec save +// path leaves this 0 and computes entries from the IR. +g_ent_loaded : int, mut g_ir_func_entry_start : string, mut g_ir_func_entry_count : string, mut g_ir_func_entry_cap : int, mut diff --git a/src/lattice/ent_kernel.cr b/src/lattice/ent_kernel.cr index 17e6c926..eb789d85 100644 --- a/src/lattice/ent_kernel.cr +++ b/src/lattice/ent_kernel.cr @@ -140,12 +140,20 @@ fn compute_live_ranges(): // compute_entries with func_i == 0 resets the table, so this is idempotent. // It runs for --dump-entries and for O2 allocation, independent of the // optimization gate. + // When the v10 loader consumed a valid ENT segment, that table is the + // authority. Rebuilding would replace the file's existence/version facts with + // a mirror and make the loader's ENT validation vacuous, so keep it. + if g_ent_loaded != 0: + return ef : ., mut = 0 loop: if ef >= g_ir_func_count: break compute_entries(ef) ef = ef + 1 + // A program with no functions still has global existence rows. + if g_ir_func_count == 0: + compute_global_entries() // ------------------------------------------------------------------ // Entry versioning: split each variable at its definition points @@ -228,6 +236,28 @@ fn entry_count(func_i: int) -> int: return 0 return cell_load(g_ir_func_entry_count, func_i * 8) +// Global existence rows. Every global var is one entry with def_nod = -1, +// live_start = 0 and live_end = g_ir_instr_count (the whole program). Globals are +// the prefix 0..g_ir_global_count-1 of the var namespace, so the loader can +// partition them as a leading block before the per-function blocks. +fn compute_global_entries(): + if g_ir_instr_count <= 0: + return + gi : ., mut = 0 + loop: + if gi >= g_ir_global_count: + break + grow_entries(g_entry_count + 1) + eo := g_entry_count * ESZ_ENTRY + w32(g_ir_entries, eo + OFF_ENTRY_VAR, gi) + w32(g_ir_entries, eo + OFF_ENTRY_DEF, -1) + w32(g_ir_entries, eo + OFF_ENTRY_LS, 0) + w32(g_ir_entries, eo + OFF_ENTRY_LE, g_ir_instr_count - 1) + w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, ENTRY_FLAG_GLOBAL) + g_entry_count = g_entry_count + 1 + gi = gi + 1 + fn compute_entries(func_i: int) -> int: if func_i == 0: g_entry_count = 0 @@ -235,12 +265,14 @@ fn compute_entries(func_i: int) -> int: // are reused by the new generation, so stale locations must be cleared. // The instance registers only after this generation is built. loc_registry_reset() + compute_global_entries() // The function window uses the same g_ir_func_instr_start/count values as // compute_live_ranges: NOD node coordinates, read through nod_*. ic := cell_load(g_ir_func_instr_count, func_i * 8) ist := cell_load(g_ir_func_instr_start, func_i * 8) vc := cell_load(g_ir_func_var_count, func_i * 8) vs := cell_load(g_ir_func_var_start, func_i * 8) + pc := cell_load(g_ir_func_param_count, func_i * 8) cnt : ., mut = 0 if ic > 0 && vc > 0: // One open-entry slot per function-local variable, initialized to -1. @@ -291,7 +323,10 @@ fn compute_entries(func_i: int) -> int: w32(g_ir_entries, eo + OFF_ENTRY_LS, inst) w32(g_ir_entries, eo + OFF_ENTRY_LE, last_global) w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) - w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0) + efl0 : ., mut = 0 + if lv < pc: + efl0 = ENTRY_FLAG_PARAM + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, efl0) cell_store(prev, lv * 8, g_entry_count) g_entry_count = g_entry_count + 1 cnt = cnt + 1 @@ -305,6 +340,9 @@ fn compute_entries(func_i: int) -> int: gv := vs + lv2 ff := live_first(func_i, gv) ll := live_last(func_i, gv) + efl2 : ., mut = 0 + if lv2 < pc: + efl2 = ENTRY_FLAG_PARAM if ff >= 0 && ll >= 0: grow_entries(g_entry_count + 1) eo := g_entry_count * ESZ_ENTRY @@ -313,7 +351,21 @@ fn compute_entries(func_i: int) -> int: w32(g_ir_entries, eo + OFF_ENTRY_LS, ist + ff) w32(g_ir_entries, eo + OFF_ENTRY_LE, ist + ll) w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) - w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, 0) + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, efl2) + g_entry_count = g_entry_count + 1 + cnt = cnt + 1 + else if lv2 < pc && ic > 0: + // An unreferenced parameter still exists. Spec §5 gives it + // def_nod = -1 and the param flag; the function node span is + // its existence window. + grow_entries(g_entry_count + 1) + eo := g_entry_count * ESZ_ENTRY + w32(g_ir_entries, eo + OFF_ENTRY_VAR, gv) + w32(g_ir_entries, eo + OFF_ENTRY_DEF, -1) + w32(g_ir_entries, eo + OFF_ENTRY_LS, ist) + w32(g_ir_entries, eo + OFF_ENTRY_LE, ist + ic - 1) + w32(g_ir_entries, eo + OFF_ENTRY_RUNTIME_HOME, -1) + w32(g_ir_entries, eo + OFF_ENTRY_FLAGS, efl2) g_entry_count = g_entry_count + 1 cnt = cnt + 1 lv2 = lv2 + 1 @@ -398,6 +450,10 @@ fn ir_op_kind_name(op: int) -> string: // 1-based version within the variable group. kind is the definition opcode, or // "-" when def is -1. fn dump_entries_summary(): + if g_ent_loaded != 0: + println("entries-source: loaded") + else: + println("entries-source: recomputed") fi : ., mut = 0 loop: if fi >= g_ir_func_count: diff --git a/src/os/linux/linux_frontend_abi.cr b/src/os/linux/linux_frontend_abi.cr index 4ad84e60..c2ef9e0a 100644 --- a/src/os/linux/linux_frontend_abi.cr +++ b/src/os/linux/linux_frontend_abi.cr @@ -4,6 +4,9 @@ // Process-entry access and Linux syscall names belong to the OS axis, not to // the semantic checker. The target composition root calls this registration; // prelude_builtin_return_ti delegates syscall3/syscall4 here. +// +// get_arg/syscall3/syscall4 are also SYSTEM boundaries in the graph classifier +// (ccr_io.cr:ccr_raw_abi_builtin). Keep the two lists in sync. fn linux_frontend_builtin_register(): prelude_builtin_add("get_arg", TI_STR) diff --git a/src/runtime/runtime_frontend_abi.cr b/src/runtime/runtime_frontend_abi.cr index 1cbc0b77..a3eba04c 100644 --- a/src/runtime/runtime_frontend_abi.cr +++ b/src/runtime/runtime_frontend_abi.cr @@ -4,6 +4,10 @@ // Allocation and scheduler/fiber entry points are runtime policy, not target // value layout. They stay on the runtime axis and are composed by the target // root; the checker only receives their return types. +// +// The same names are SYSTEM boundaries in the graph classifier +// (ccr_io.cr:ccr_raw_abi_builtin). A direct call must mark the .ccr header +// SYSTEM, so adding a name here requires adding it there too. fn runtime_frontend_builtin_register(): prelude_builtin_add("alloc", TI_STR) diff --git a/tests/harness/ci_hook_allowlist.txt b/tests/harness/ci_hook_allowlist.txt index eccccb74..89f88563 100644 --- a/tests/harness/ci_hook_allowlist.txt +++ b/tests/harness/ci_hook_allowlist.txt @@ -27,8 +27,14 @@ # 本批实测时长后判定可挂)→ `selfhost-tests`(时长 = 本批本机实测,逐条与 run.sh 行注一致) # - 未挂项仍列下方(旧 `test_mw_task1-6` 已随 2-limb 路径删除)。 -# ─── tests/selfhost(11 条;未挂)─── +# ─── tests/selfhost(12 条;未挂)─── tests/selfhost/test_apx_tag.py 未挂(历史缺口):需 corec 端到端;挂点属构建面 ⇒ 与其它编译器型未挂档同批评估 +tests/selfhost/test_ccrb_bundle.py 未挂:需外部 zstd(CI 镜像未保证);`.ccrb` 是分发包装层,核心 v10 raw `.ccr` 闸门已由 test_ccr_v10_format/stream 覆盖。接入条件 = CI 镜像声明 zstd 依赖 +tests/selfhost/test_bigint_arith.py 未挂(历史缺口):BigInt 运行时端到端,需 corec+ELF;构建面成本高,独立批评估 +tests/selfhost/test_bigint_boundaries.py 未挂(历史缺口):BigInt 边界语义,需 corec;与 arith 同族 +tests/selfhost/test_bigint_containers.py 未挂(历史缺口):BigInt 容器载荷,需 corec+ELF +tests/selfhost/test_bigint_interp.py 未挂(历史缺口):BigInt 解释器路径,需 corec +tests/selfhost/test_bigint_literals.py 未挂(历史缺口):BigInt 字面量,需 corec tests/selfhost/test_dex_arith.py 未挂(历史缺口):dex 定点语义;需 corec tests/selfhost/test_dex_type.py 未挂(历史缺口):dex 类型核心;需 corec tests/selfhost/test_directory_build.py 未挂(历史缺口):目录路径工程构建;需 corec+corearch @@ -42,6 +48,8 @@ tests/selfhost/test_native_strings.py 未挂(历史缺口):Native ELF 字 # ─── tests/bootstrap(4 条;未挂)─── tests/bootstrap/test_apx_tag.py 未挂(历史缺口):bootstrap 面 apx 标签(与 selfhost 同名档配对) +tests/bootstrap/test_bigint_frontend.py 未挂(历史缺口):BigInt 前端语义,需 bootstrap 管线 +tests/bootstrap/test_bigint_pipeline.py 未挂(历史缺口):BigInt 管线端到端,需 bootstrap 管线 tests/bootstrap/test_dex_arith.py 未挂(历史缺口):bootstrap dex 精确运算 tests/bootstrap/test_dex_type.py 未挂(历史缺口):bootstrap dex 类型核心 tests/bootstrap/test_modules.py 未挂(历史缺口):bootstrap import alias 回归 diff --git a/tests/selfhost/test_ccr_types.py b/tests/selfhost/test_ccr_types.py index b5737d55..de6fc9e0 100644 --- a/tests/selfhost/test_ccr_types.py +++ b/tests/selfhost/test_ccr_types.py @@ -1739,8 +1739,12 @@ def test_p4t4_ccr_nod_tk_equals_graph_tk(): assert type_id == dr[2], \ f"node {i}: type_id {type_id} != face-0 row {dr[2]}" else: - assert type_id == -1, \ - f"node {i}: non-face node carries type_id {type_id}" + # Non-face ops may now carry a semantic domain (DEREF pointee, + # STORE value, ALLOC element/struct, field/index domains). The + # wire value must still be a TYPE row or -1; it is no longer + # forced to -1 for every non-face node. + assert -1 <= type_id, \ + f"node {i}: non-face node carries invalid type_id {type_id}" finally: _cleanup(src, ccr_path, dot) @@ -2245,15 +2249,20 @@ def test_p6t3_types_match_memory_face(): assert len(recs) == len(rows), f"nodes {len(recs)} != mem rows {len(rows)}" w_mem = sum(1 for r in rows if r[3] >= 0) nhdr, nrows = _nod_type_dump(ccr_path) - assert nhdr['with_type'] == w_mem, \ - f"with_type {nhdr['with_type']} != memory with_term {w_mem}" + # Type-face nodes project the reversible atom row. Non-face ops now carry + # a semantic domain derived from the variable table, so they may be typed + # even when the memory type-term slot is absent. + assert nhdr['with_type'] >= w_mem, \ + f"with_type {nhdr['with_type']} < memory with_term {w_mem}" + face_ops = {1, 2, 6, 4, 10, 49, 50} # CONST/BINARY/ALLOC/CALL/LOAD/I2F/F2I for i, ((op, type_id, flags_), r) in enumerate(zip(recs, rows)): assert op == r[1], f"node {i}: NOD op {op} != memory op {r[1]}" - assert (type_id >= 0) == (r[3] >= 0), \ - f"node {i}: type_id {type_id} vs memory term {r[3]} (face disagreement)" - if type_id >= 0: + if op in face_ops and r[3] >= 0: assert type_id == r[2], \ f"node {i}: type_id {type_id} != derived code {r[2]}" + if type_id >= 0: + assert 0 <= type_id < nhdr['rows'], \ + f"node {i}: type_id {type_id} outside TYPE rows" finally: _cleanup(src, ccr_path, dot) @@ -2287,13 +2296,21 @@ def test_p6t3_aux_face_and_composite_preserved(): comp = [r for r in recs if r[0] == IR_BINARY and r[1] == -1] assert comp, f"{tag}: vacuous (no composite-row node)" for (op, type_id, flags_) in aux: - assert type_id == -1, \ - f"{tag}: aux-face op {op} carries type_id {type_id} (must be -1)" + if op == IR_DEREF or op == IR_STORE_PTR: + # These two now carry a semantic domain (pointee/value) when + # the variable table can supply it; they must never carry a + # target width or byte extent. + assert -1 <= type_id, \ + f"{tag}: pointer op {op} carries invalid type_id {type_id}" + else: + assert type_id == -1, \ + f"{tag}: aux-face op {op} carries type_id {type_id} (must be -1)" if op == IR_BOUNDS_CHECK and flags_ & 1: saw_bound += 1 if op == IR_SPAWN and flags_ & 2: saw_spawn += 1 assert flags_ <= 0x7 + finally: _cleanup(ccr_path) if tag == 'tk': @@ -2442,10 +2459,10 @@ def test_nod_type_id_is_type_row_or_minus_one(): for (n, op, tk, tid, flags) in nrows: assert flags <= 0x7, f"node {n}: unknown flag bits {flags:#x}" exp = expected_nod_type_id(op, tk, rows, terms, rowterms) - assert tid == exp, \ - f"node {n}: type_id {tid} != expected {exp} (op={op} tk={tk})" if exp >= 0: face += 1 + assert tid == exp, \ + f"node {n}: type_id {tid} != expected {exp} (op={op} tk={tk})" if tid > 0: face_gt0 += 1 assert tid == tk, f"node {n}: type_id {tid} != row {tk}" @@ -2453,7 +2470,10 @@ def test_nod_type_id_is_type_row_or_minus_one(): f"node {n}: type_id {tid} outside row domain {len(rows)}" else: nonface += 1 - assert tid == -1, f"node {n}: non-face type_id {tid} != -1" + # Non-face ops use a semantic domain (DEREF pointee, STORE value, + # ALLOC element/struct, field/index domains) when derivable. + assert -1 <= tid < len(rows), \ + f"node {n}: non-face type_id {tid} outside domain" assert face > 0, "vacuous: fixture has no type-face node" assert nonface > 0, "vacuous: fixture has no non-type node" assert face_gt0 > 0, "vacuous: all type ids are row 0 (degenerate mapping)" @@ -2461,6 +2481,68 @@ def test_nod_type_id_is_type_row_or_minus_one(): _cleanup(src, ccr_path) +def test_non_type_face_op_has_semantic_domain(): + """Important 3: non-type-face ops still carry a semantic type_id when the + variable table supplies the domain (DEREF pointee, STORE_PTR value, + ALLOC_ARRAY element, ADDR_INDEX element). The old expectation that every + non-face node is -1 is no longer the v10 behavior.""" + src = tk_fixture('p7dom', COMPOSITE_FIXTURE) + ccr_path = os.path.join(BASE, 'build', 'test_p7t3_domain.ccr') + try: + corec_ccr(src, ccr_path) + recs = ccr_nod_records(read_ccr(ccr_path)) + want = {8: 'ALLOC_ARRAY', 25: 'DEREF', 26: 'STORE_PTR', 31: 'ADDR_INDEX'} + seen = {} + for op, type_id, _flags in recs: + if op in want: + if op not in seen or type_id >= 0: + seen[op] = type_id + typed = [op for op, tid in seen.items() if tid >= 0] + assert typed, f"no derivable non-face domain in fixture: {seen}" + for op, tid in seen.items(): + assert -1 <= tid, f"{want[op]} carries invalid type_id {tid}" + finally: + _cleanup(src, ccr_path) + + +def test_ent_global_and_unused_param_rows(): + """Important 5: globals and unreferenced parameters have existence rows. + + Spec §5: def_nod = -1 for both; flags bit 1 = parameter, bit 2 = global. + The fixture forces both shapes and the file still loads/emits. + """ + src = os.path.join(BASE, 'build', 'test_ent_globals.cr') + ccr_path = os.path.join(BASE, 'build', 'test_ent_globals.ccr') + out = os.path.join(BASE, 'build', 'test_ent_globals.out') + source = ("counter : int, mut = 3\n" + "fn pick(a: int, unused: int) -> int:\n" + " return a\n" + "fn main() -> int:\n" + " return pick(counter, 0)\n") + try: + with open(src, 'w') as fh: + fh.write(source) + corec_ccr(src, ccr_path) + body = CcrFile(read_ccr(ccr_path)).body(ENT_TAG) + (cnt,) = struct.unpack_from(' 0, "no function entry rows dumped (vacuous)" + # ENT also has one leading global row per global var; dump-entries only + # prints function blocks, so the disk count is >= the dumped rows. + assert len(homes) <= cnt, \ + f"{len(homes)} dumped home values > {cnt} ENT records" assert all(int(h) == -1 for h in homes), \ f"v10 loader surfaced a non-(-1) home: {set(homes)}" # 合法 v10 文件端到端仍可载入/发射 @@ -2544,6 +2631,8 @@ def test_v10_ent_24b_without_home(): test_p6t3_nod_count_overflow_rejected, test_p6t3_d18_purity_static, test_nod_type_id_is_type_row_or_minus_one, + test_non_type_face_op_has_semantic_domain, + test_ent_global_and_unused_param_rows, test_v10_ent_24b_without_home] failed = 0 for t in tests: diff --git a/tests/selfhost/test_ccr_v10_format.py b/tests/selfhost/test_ccr_v10_format.py index 32b4dd04..f0f68837 100644 --- a/tests/selfhost/test_ccr_v10_format.py +++ b/tests/selfhost/test_ccr_v10_format.py @@ -958,6 +958,136 @@ def test_old_target_facts_in_src2_src3_rejected(): assert "invalid" in (r.stdout + r.stderr), f"{name}: missing diagnostic" +def _reject_mutated(data, tmp, name): + bad = tmp / f"{name}.ccr" + bad.write_bytes(data) + out = tmp / f"{name}.elf" + r = _run([COREARCH, bad, "--elf", "--static", "-o", out]) + assert r.returncode != 0, f"{name}: mutated file accepted" + assert r.returncode > 0, f"{name}: corearch died by signal ({r.returncode})" + assert "invalid" in (r.stdout + r.stderr), f"{name}: missing loader diagnostic" + assert not out.exists(), f"{name}: emitted an ELF for a rejected file" + + +def test_untrusted_nod_operands_rejected_not_emitted(): + """Critical 2: the loader owns opcode/operand domains. + + Each mutation must produce `load_ccr == -1` (rc 1, no SIGSEGV, no ELF): + a huge DEREF src1, an unknown opcode, a dangling branch/jump label, and a + CONST_BIG string index outside STR. + """ + with tempfile.TemporaryDirectory() as td: + tmp = Path(td) + data = _build_v10(tmp) + off, _rec = _first_node_offset(data, 25) # IR_DEREF + d = bytearray(data) + struct.pack_into(" int: + x := 1 + if x == 1: + return 7 + return 0 +""" + bdata = _build_v10(tmp, branch_src, "branch") + off, _rec = _first_node_offset(bdata, 20) # IR_JUMP + d = bytearray(bdata) + struct.pack_into(" int: + x := 100000000000000000000 + return 0 +""" + cdata = _build_v10(tmp, big_src, "constbig") + off, _rec = _first_node_offset(cdata, 40) + d = bytearray(cdata) + struct.pack_into(" 0: + name_off = sym_off + 4 + else: + # globals count + no globals + func count -> first function header. + name_off = sym_off + 8 + d = bytearray(data) + struct.pack_into(" int: + return sched_call_0() +""" + with tempfile.TemporaryDirectory() as td: + data = _build_v10(Path(td), abi_src, "abicall") + f = flags(data) + assert f & 2 != 0, f"direct runtime ABI call missing SYSTEM: {f:#x}" + assert f & 1 == 0, f"direct runtime ABI call also claims PORTABLE: {f:#x}" + + def test_ent_reserved_flags_and_trailing_rejected(): with tempfile.TemporaryDirectory() as td: tmp = Path(td) diff --git a/tests/selfhost/test_ccr_v7.py b/tests/selfhost/test_ccr_v7.py index 40c3419b..70ec2d1a 100644 --- a/tests/selfhost/test_ccr_v7.py +++ b/tests/selfhost/test_ccr_v7.py @@ -415,12 +415,18 @@ def model_compute_entries(nod, funcs, gcount, regs): defs_out = [] # entries in creation order (def-point order per func) blocks = [] row_no = 0 # global entry counter across funcs (== file order) + # Global existence rows precede function blocks: def=-1, whole-program + # window, global flag (ENTRY_FLAG_GLOBAL = 4). + for gv in range(gcount): + defs_out.append((gv, 1, -1, 0, len(nod) - 1, 4)) + row_no += 1 var_start = gcount for k, f in enumerate(funcs): root = regs[f['root_region']] ist, ex = root[2], root[3] ic = ex - ist vc = f['var_count'] + pc = f['param_count'] vs = var_start var_start += vc ent0 = row_no @@ -460,13 +466,19 @@ def model_compute_entries(nod, funcs, gcount, regs): row = defs_out[pe] defs_out[pe] = (row[0], row[1], row[2], row[3], pend, row[5]) - defs_out.append((dv, 1, inst, inst, last_global, 0)) + pfl = 2 if lv < pc else 0 + defs_out.append((dv, 1, inst, inst, last_global, pfl)) prev[lv] = len(defs_out) - 1 row_no += 1 for lv2 in range(vc): + pfl2 = 2 if lv2 < pc else 0 if prev[lv2] < 0 and first[lv2] >= 0 and last[lv2] >= 0: defs_out.append((vs + lv2, 1, -1, ist + first[lv2], - ist + last[lv2], 0)) + ist + last[lv2], pfl2)) + row_no += 1 + elif prev[lv2] < 0 and lv2 < pc and ic > 0: + # Unreferenced parameter: def=-1, function span. + defs_out.append((vs + lv2, 1, -1, ist, ist + ic - 1, pfl2)) row_no += 1 blocks.append((ent0, row_no - ent0)) # version = 同 var 文件序组内 1-based 序数 @@ -557,34 +569,40 @@ def test_v7_ent_hand_expected_small(): var_of[(0, di)] = vs0 + di for di in range(funcs[1]['var_count']): var_of[(1, di)] = vs1 + di - # 手算表只覆盖文件前两函数块(identity 2 + main 10 = 文件行 0..11—— - # 函数块按函数序拼接,其余函数块在其后,本测试只断言前缀 12 行) - assert len(ents) >= len(exp), \ - f"entries {len(ents)} < hand-expected {len(exp)}" + # 全局存在行在前(每个全局一条 def=-1/flag=global),函数块随后。 + base = len(sym['globals']) + assert len(ents) >= base + len(exp), \ + f"entries {len(ents)} < globals {base} + hand-expected {len(exp)}" + for gi in range(base): + ev, ever, edef, els, ele, efl = ents[gi] + assert (ev, edef, efl) == (gi, -1, 4), \ + f"global row {gi}: {(ev, edef, efl)}" + assert els == 0 and ele > 0 for k, (fidx, erow) in enumerate(zip([0] * 2 + [1] * 10, exp)): - evar, ever, edef, els, ele, efl = ents[k] + evar, ever, edef, els, ele, efl = ents[base + k] assert evar == var_of[(fidx, erow[0])], \ f"row {k}: var {evar} != decl-row {erow[0]} var " \ f"{var_of[(fidx, erow[0])]}" assert (ever, edef, els, ele) == erow[1:], \ f"row {k} {evar}: got ({ever},{edef},{els},{ele}) " \ f"expected {erow[1:]}" - assert efl == 0, f"row {k}: flags {efl} != 0" - # SYM func 记录:块界回填 = 文件行 0..1 / 2..11;param_ents 实回填 - # (identity 参数 n 的 def=-1 条目 = 文件行 1) + want_fl = 2 if erow[0] < funcs[fidx]['param_count'] else 0 + assert efl == want_fl, f"row {k}: flags {efl} != {want_fl}" + # SYM func 记录:块界回填 = 全局前缀后的 0..1 / 2..11(全局行偏移); + # param_ents 实回填(identity 参数 n 的 def=-1 条目 = 前缀 + 1) f0, f1 = funcs[0], funcs[1] - assert (f0['first_ent'], f0['last_ent']) == (0, 1), \ + assert (f0['first_ent'], f0['last_ent']) == (base, base + 1), \ f"identity range {f0['first_ent']}..{f0['last_ent']}" - assert f0['param_ents'] == [1], \ - f"identity param_ents {f0['param_ents']} != [1]" - assert (f1['first_ent'], f1['last_ent']) == (2, 11), \ + assert f0['param_ents'] == [base + 1], \ + f"identity param_ents {f0['param_ents']} != [{base + 1}]" + assert (f1['first_ent'], f1['last_ent']) == (base + 2, base + 11), \ f"main range {f1['first_ent']}..{f1['last_ent']}" assert f1['param_count'] == 0 # REG 根行(SG_FUNC)first/last 与 SYM func 记录双写一致 regs = v7.reg() roots = [i for i, r in enumerate(regs) if r[0] == 0] - assert regs[roots[0]][4] == 0 and regs[roots[0]][5] == 1 - assert regs[roots[1]][4] == 2 and regs[roots[1]][5] == 11 + assert regs[roots[0]][4] == base and regs[roots[0]][5] == base + 1 + assert regs[roots[1]][4] == base + 2 and regs[roots[1]][5] == base + 11 finally: try: os.unlink(ccr_path) @@ -693,20 +711,22 @@ def test_v7_ent_hand_expected_pure_add_block(): assert strs[funcs[0]['name']] == 'pure_add' ents = v7.ent() vs0 = len(sym['globals']) + base = vs0 # one leading global existence row per global exp = [ (vs0 + 2, 1, 0, 0, 4), (vs0 + 3, 1, 1, 1, 3), (vs0 + 0, 1, -1, 1, 2), (vs0 + 1, 1, -1, 1, 2), ] got = [(ev, ever, edef, els, ele) for (ev, ever, edef, els, ele, - efl) in ents[:4]] + efl) in ents[base:base + 4]] assert got == exp, f"pure_add block:\n got {got}\n expected {exp}" - for (ev, ever, edef, els, ele, efl) in ents[:4]: - assert efl == 0 + for (ev, ever, edef, els, ele, efl) in ents[base:base + 4]: + want_fl = 2 if (ev - vs0) < funcs[0]['param_count'] else 0 + assert efl == want_fl, f"pure_add row flags {efl} != {want_fl}" f0 = funcs[0] - assert (f0['first_ent'], f0['last_ent']) == (0, 3), \ - f"pure_add range ({f0['first_ent']},{f0['last_ent']}) != (0,3)" - assert f0['param_ents'] == [2, 3], \ - f"pure_add param_ents {f0['param_ents']} != [2,3]" + assert (f0['first_ent'], f0['last_ent']) == (base, base + 3), \ + f"pure_add range ({f0['first_ent']},{f0['last_ent']}) != ({base},{base+3})" + assert f0['param_ents'] == [base + 2, base + 3], \ + f"pure_add param_ents {f0['param_ents']} != {[base+2, base+3]}" # main 内多定值 var 版本切分 spot-check(手算): # v(decl9): ALLOC@20 + STORE@22 + STORE_INDEX_VAR@24 读(carve-out) # 值读@26/@34 → 版本1 (20,22) 版本2 (22,35) @@ -913,7 +933,7 @@ def test_v7_layout_and_walk(): # 引用 + _arena 定值),本程序条目数 > 0;全行字段自洽 assert len(e) > 0, f"ENT empty — Task 2: corec should write entries: {e}" for (ev, evr, ed, els, ele, efl) in e: - assert evr >= 1 and 0 <= ev and efl == 0 + assert evr >= 1 and 0 <= ev and efl in (0, 2, 4) assert els < ele and (ed < 0 or ed == els) edg = v7.edg() # full EDG walk + forward/kind invariant checks # EDG 段必落且至少一条边(每函数 arena_new→arena_reset def-use 存在) @@ -1225,7 +1245,7 @@ def test_v7_roundtrip_elf(): # Task 2: build 链中间产物携带实条目(corearch 已 load 校验 + 发射) assert len(ents) > 0, "built .ccr should carry real ENT records" for (ev, evr, ed, els, ele, efl) in ents: - assert evr >= 1 and efl == 0 + assert evr >= 1 and efl in (0, 2, 4) assert els < ele and (ed < 0 or ed == els) assert sum(len(v) for v in v7.edg().values()) >= 2 # direct corearch invocation on the same .ccr @@ -1274,9 +1294,12 @@ def test_v7_ent_real_optmeta_absent(): assert sym['opt_count'] == 0, f"opt_meta not empty: {sym['opt_count']}" funcs = sym['funcs'] assert len(funcs) >= 2 - # Python 侧块切分(loader 块对照同语义):块序 = 函数序,块 = 连续段 - vs = len(sym['globals']) - pos = 0 + # Python 侧块切分(loader 块对照同语义):全局前缀行在前,块序 = 函数序 + base = len(sym['globals']) + vs = base + pos = base + assert all(ents[g][2] == -1 and ents[g][5] == 4 for g in range(base)), \ + "global ENT prefix malformed" for f in funcs: win = range(vs, vs + f['var_count']) start = pos @@ -1302,6 +1325,8 @@ def test_v7_ent_real_optmeta_absent(): f"func param_ents[{p}] {f['param_ents'][p]} != {pid}" vs += f['var_count'] assert pos == len(ents), "block partition did not cover all entries" + assert all(ents[g][0] == g and ents[g][2] == -1 and ents[g][5] == 4 + for g in range(base)), "global prefix rows malformed" # REG 根行 first/last 与 SYM func 记录双写一致(实值) regs = v7.reg() roots = [(i, r) for i, r in enumerate(regs) if r[0] == 0] @@ -1438,9 +1463,10 @@ def test_v7_sym_func_shapes(): assert [strs[d['name']] for d in pdecls] == ['a', 'b'] assert [d['type'] for d in pdecls] == [0, 0], "int param type != TI_INT" # 条目范围/param_ents 实回填——Python 侧块切分自洽(loader 块对照同 - # 语义):add = 文件首块(func0 起点 0,含 a/b 的 def=-1 条目) - vs = len(sym['globals']) - pos = 0 + # 语义):全局存在行是前导块,add = 其后的第一个函数块。 + base = len(sym['globals']) + vs = base + pos = base block_of = [] for f in funcs: win = range(vs, vs + f['var_count']) @@ -1450,6 +1476,8 @@ def test_v7_sym_func_shapes(): block_of.append((start, pos - start)) vs += f['var_count'] assert pos == len(ents), "block partition did not cover all entries" + assert all(ents[g][0] == g and ents[g][2] == -1 and ents[g][5] == 4 + for g in range(base)), "global prefix rows malformed" for k, f in enumerate(funcs): st, pcnt = block_of[k] fe = st if pcnt else -1 @@ -1598,12 +1626,13 @@ def test_v7_loader_rejects_ent_var_foreign_block(): sym = v7.sym_parse() funcs = sym['funcs'] ents = v7.ent() - assert funcs[0]['first_ent'] == 0, \ - f"precondition: func0 first entry at 0, got {funcs[0]['first_ent']}" + f0e = funcs[0]['first_ent'] + assert f0e >= len(sym['globals']), \ + f"precondition: func0 first entry after global prefix, got {f0e}" vs1 = len(sym['globals']) + funcs[0]['var_count'] # func1 窗口起点 - assert ents[0][0] < vs1, \ - f"precondition: entry0 var {ents[0][0]} already outside func0 window" - _mutate_ent_field(data, 0, 0, vs1, ' Date: Sat, 24 Oct 2026 03:37:57 +0800 Subject: [PATCH 58/58] test: give ccr v10 oracle fixtures a valid header flag --- tests/selfhost/ccr_v10_model.py | 35 ++++++++++++++++++++++++--- tests/selfhost/test_ccr_v10_format.py | 4 ++- tests/selfhost/test_ccr_v10_stream.py | 29 +++++++++++++++++++--- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/tests/selfhost/ccr_v10_model.py b/tests/selfhost/ccr_v10_model.py index 53bd8ab4..c630f06c 100644 --- a/tests/selfhost/ccr_v10_model.py +++ b/tests/selfhost/ccr_v10_model.py @@ -46,6 +46,14 @@ } FLAGS_KNOWN_MASK = 0x7 # PORTABLE | SYSTEM | CONCURRENT +FLAG_PORTABLE = 0x1 +FLAG_SYSTEM = 0x2 +FLAG_CONCURRENT = 0x4 +# Synthetic wire fixtures are program-shaped but contain no SYSTEM boundary. +# The real loader requires exactly one of PORTABLE/SYSTEM; use PORTABLE as the +# default so oracle images satisfy the same header gate. Callers rebuilding a +# real .ccr must pass that file's flags explicitly (see test_ccr_v10_stream). +DEFAULT_FLAGS = FLAG_PORTABLE class CcrV10Error(ValueError): @@ -155,15 +163,24 @@ def segment(data, tag): raise CcrV10Error(f"segment tag not present: {tag}") -def encode(segments): +def encode(segments, flags=DEFAULT_FLAGS): """Serialize a complete tag->body mapping into a v10 `.ccr` image. All nine tags must be present; bodies may be empty. Missing, unknown, or non-bytes entries raise `CcrV10Error`. Output is canonical and deterministic - regardless of input dict order. + regardless of input dict order. `flags` must satisfy the loader's header + rule: exactly one of PORTABLE/SYSTEM, CONCURRENT only together with SYSTEM. """ if not isinstance(segments, dict): raise CcrV10Error(f"segments must be a dict, got {type(segments).__name__}") + if flags & ~FLAGS_KNOWN_MASK: + raise CcrV10Error(f"reserved header flag bits set: 0x{flags:08X}") + if (flags & (FLAG_PORTABLE | FLAG_SYSTEM)) not in (FLAG_PORTABLE, FLAG_SYSTEM): + raise CcrV10Error( + f"header flags must set exactly one of PORTABLE/SYSTEM: 0x{flags:08X}" + ) + if (flags & FLAG_CONCURRENT) and not (flags & FLAG_SYSTEM): + raise CcrV10Error("CONCURRENT requires SYSTEM") unknown = [tag for tag in segments if tag not in SEG_TAGS] if unknown: @@ -182,7 +199,7 @@ def encode(segments): ) bodies.append(bytes(body)) - header = struct.pack(" cut + 40 bodies[TAG_OF["NOD"]] = bodies[TAG_OF["NOD"]][:-cut] - return encode(bodies) + return encode(bodies, flags=flags(data)) def _permuted_offset_image(data): @@ -183,7 +206,7 @@ def _permuted_offset_image(data): placed.append(bodies[tag]) offset += len(bodies[tag]) - header = struct.pack("