diff --git a/runtimec/CMakeLists.txt b/runtimec/CMakeLists.txt new file mode 100644 index 00000000..4502532c --- /dev/null +++ b/runtimec/CMakeLists.txt @@ -0,0 +1,177 @@ +# 组件:runtimec 顶层构建 +# 作用:C11-only 构建(不链接 libstdc++);按 contract、host、binrwir、 +# vmkv、binrwir_compiler、vmbin、engine、interface 生成独立 target, +# 并从 interface/exports.txt +# 生成三平台导出脚本(README §9、§10)。当前产物名刻意带 _scaffold,禁止用于 +# benchmark/tutorial/发布。 +# 联系:组件依赖只沿本文件声明的单向图连接;JIT 是 BinRWIR Compiler +# 内部实现,只有 engine route 汇合 compiler 与 vmbin。 +cmake_minimum_required(VERSION 3.20) + +project(kvlang_runtimec_scaffold VERSION 0.1.0 LANGUAGES C) + +include(CTest) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) + +option(RUNTIMEC_ENABLE_JIT "Enable BinRWIR JIT compilation capability" ON) + +set(RUNTIMEC_INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include") +get_filename_component(KVLANG_INCLUDE_DIRECTORY + "${CMAKE_CURRENT_SOURCE_DIR}/../include" ABSOLUTE) +if(NOT EXISTS "${KVLANG_INCLUDE_DIRECTORY}/kvspace/xvalue_head.h") + message(FATAL_ERROR + "runtimec requires kvlang include/kvspace/xvalue_head.h") +endif() +set(RUNTIMEC_GENERATED_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated") +file(MAKE_DIRECTORY "${RUNTIMEC_GENERATED_DIRECTORY}") + +set(RUNTIMEC_EXPORT_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/interface/exports.txt") +file(STRINGS "${RUNTIMEC_EXPORT_MANIFEST}" RUNTIMEC_PUBLIC_SYMBOLS) +set(RUNTIMEC_ELF_EXPORTS "{\n global:\n") +set(RUNTIMEC_MACHO_EXPORTS "") +set(RUNTIMEC_WINDOWS_EXPORTS "LIBRARY kvlang_runtimec_scaffold\nEXPORTS\n") +foreach(public_symbol IN LISTS RUNTIMEC_PUBLIC_SYMBOLS) + string(APPEND RUNTIMEC_ELF_EXPORTS " ${public_symbol};\n") + string(APPEND RUNTIMEC_MACHO_EXPORTS "_${public_symbol}\n") + string(APPEND RUNTIMEC_WINDOWS_EXPORTS " ${public_symbol}\n") +endforeach() +string(APPEND RUNTIMEC_ELF_EXPORTS " local: *;\n};\n") +file(WRITE "${RUNTIMEC_GENERATED_DIRECTORY}/exports.map" "${RUNTIMEC_ELF_EXPORTS}") +file(WRITE "${RUNTIMEC_GENERATED_DIRECTORY}/exports.macos" "${RUNTIMEC_MACHO_EXPORTS}") +file(WRITE "${RUNTIMEC_GENERATED_DIRECTORY}/exports.def" "${RUNTIMEC_WINDOWS_EXPORTS}") + +function(runtimec_configure_target target_name) + target_compile_features(${target_name} PRIVATE c_std_11) + target_include_directories(${target_name} + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}" + "${RUNTIMEC_INCLUDE_DIRECTORY}" + "${KVLANG_INCLUDE_DIRECTORY}") + set_target_properties(${target_name} PROPERTIES + C_VISIBILITY_PRESET hidden + POSITION_INDEPENDENT_CODE ON) + + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options(${target_name} PRIVATE + -Wall -Wextra -Wpedantic -Werror + -Wstrict-prototypes -Wmissing-prototypes) + elseif(MSVC) + target_compile_options(${target_name} PRIVATE /W4) + endif() +endfunction() + +# 每个组件必须拥有真实源文件。缺失组件说明目录或构建关系错误,配置阶段 +# 立即失败;禁止用空翻译单元把缺失能力伪装成成功构建。 +function(runtimec_add_component target_name) + set(component_sources) + foreach(component_path IN LISTS ARGN) + file(GLOB_RECURSE path_sources CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/${component_path}/*.c") + list(APPEND component_sources ${path_sources}) + endforeach() + + if(NOT component_sources) + message(FATAL_ERROR + "runtimec component ${target_name} has no source files") + endif() + + add_library(${target_name} STATIC ${component_sources}) + runtimec_configure_target(${target_name}) +endfunction() + +runtimec_add_component(runtimec_contract contract) +runtimec_add_component(runtimec_host host) +runtimec_add_component(runtimec_binrwir binrwir) +runtimec_add_component(runtimec_vmkv vmkv) +runtimec_add_component(runtimec_binrwir_compiler binrwir_compiler) +runtimec_add_component(runtimec_vmbin vmbin) +runtimec_add_component(runtimec_engine engine) + +target_include_directories(runtimec_contract + PUBLIC + "$" + "$") +target_link_libraries(runtimec_host + PRIVATE runtimec_contract) +target_link_libraries(runtimec_binrwir + PRIVATE runtimec_contract) +target_link_libraries(runtimec_vmkv + PRIVATE runtimec_contract runtimec_binrwir) +target_link_libraries(runtimec_binrwir_compiler + PRIVATE runtimec_contract runtimec_binrwir) +target_link_libraries(runtimec_vmbin + PRIVATE runtimec_contract runtimec_binrwir) +target_link_libraries(runtimec_engine + PRIVATE + runtimec_contract + runtimec_host + runtimec_binrwir + runtimec_vmkv + runtimec_binrwir_compiler + runtimec_vmbin) +target_compile_definitions(runtimec_binrwir_compiler + PRIVATE RUNTIMEC_ENABLE_JIT=$) +target_compile_definitions(runtimec_engine + PRIVATE RUNTIMEC_ENABLE_JIT=$) + +# 编译公共二进制接口实现一次,在静态接口组件和 scaffold 动态库间共享。 +add_library(runtimec_interface_objects OBJECT interface/public_interface.c) +runtimec_configure_target(runtimec_interface_objects) +target_compile_definitions(runtimec_interface_objects + PRIVATE KVLANG_RUNTIMEC_BUILDING_LIBRARY=1) + +add_library(runtimec_interface STATIC + $) +runtimec_configure_target(runtimec_interface) +target_link_libraries(runtimec_interface + PUBLIC runtimec_contract + PRIVATE runtimec_engine) + +add_library(kvlang_runtimec_scaffold SHARED + $) +target_include_directories(kvlang_runtimec_scaffold + PUBLIC "$") +target_link_libraries(kvlang_runtimec_scaffold + PRIVATE runtimec_interface runtimec_engine) +set_target_properties(kvlang_runtimec_scaffold PROPERTIES + C_VISIBILITY_PRESET hidden + OUTPUT_NAME kvlang_runtimec_scaffold + POSITION_INDEPENDENT_CODE ON) + +if(APPLE) + target_link_options(kvlang_runtimec_scaffold PRIVATE + "LINKER:-exported_symbols_list,${RUNTIMEC_GENERATED_DIRECTORY}/exports.macos") + set_property(TARGET kvlang_runtimec_scaffold APPEND PROPERTY + LINK_DEPENDS "${RUNTIMEC_GENERATED_DIRECTORY}/exports.macos") +elseif(WIN32) + target_sources(kvlang_runtimec_scaffold PRIVATE + "${RUNTIMEC_GENERATED_DIRECTORY}/exports.def") +elseif(UNIX) + target_link_options(kvlang_runtimec_scaffold PRIVATE + "LINKER:--version-script=${RUNTIMEC_GENERATED_DIRECTORY}/exports.map") + set_property(TARGET kvlang_runtimec_scaffold APPEND PROPERTY + LINK_DEPENDS "${RUNTIMEC_GENERATED_DIRECTORY}/exports.map") +endif() + +add_library(kvlang::runtimec_contract ALIAS runtimec_contract) +add_library(kvlang::runtimec_host ALIAS runtimec_host) +add_library(kvlang::runtimec_binrwir ALIAS runtimec_binrwir) +add_library(kvlang::runtimec_vmkv ALIAS runtimec_vmkv) +add_library(kvlang::runtimec_binrwir_compiler ALIAS runtimec_binrwir_compiler) +add_library(kvlang::runtimec_vmbin ALIAS runtimec_vmbin) +add_library(kvlang::runtimec_engine ALIAS runtimec_engine) +add_library(kvlang::runtimec_interface ALIAS runtimec_interface) +add_library(kvlang::runtimec_scaffold ALIAS kvlang_runtimec_scaffold) + +if(BUILD_TESTING) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt") + message(FATAL_ERROR "BUILD_TESTING requires runtimec/tests/CMakeLists.txt") + endif() + add_subdirectory(tests) +endif() diff --git a/runtimec/README.md b/runtimec/README.md new file mode 100644 index 00000000..2924c4bd --- /dev/null +++ b/runtimec/README.md @@ -0,0 +1,417 @@ +# runtimec 架构与实现总纲 + +> 当前状态:目录、数据结构、函数边界和失败封闭骨架已经建立;语言执行语义尚未实现。 +> `scaffold` 产物名必须保留到真实语义通过验收,防止骨架被误当成可发布实现。 + +`runtimec/` 是 kvlang 的全新 C11 运行时底座。它只允许一条从语言规范到执行的主干: +kvspace 中的明文 RWIR 先被解析并解引用为进程内 canonical BinRWIR,再由顶层 BinRWIR +Compiler 完成执行计划生成和执行目标判定,最后由 route 调用唯一已确定的执行入口。 + +未实现的能力必须返回 `RUNTIMEC_STATUS_NOT_IMPLEMENTED`,非法输入必须返回 +`RUNTIMEC_STATUS_INVALID`。空值、零、`false` 或默认结构不能伪装成功。 + +## 1. 绝对原则 + +1. **只有一条正常方向。** 正常完成只返回 `RUNTIMEC_STATUS_SUCCESS`;其它状态必须 + 准确说明暂停、等待、未支持、未实现、非法输入、内存不足或错误。 +2. **语义先于实现。** `myrwircaps` 与 RWIR specification 定义 runtimec 能处理的 + RWIR;五语言矩阵先裁决行为,代码只实现已裁决语义。 +3. **等价生成。** vmkv 只验证 RWIR,并等价生成 canonical BinRWIR;它不优化、不选择 + 执行器、不生成机器码。 +4. **编译期一次决定。** `binrwir_compiler/` 生成 execution plan,并在同一次编译中把 + compilation 的执行目标确定为 JIT code 或 VMBIN plan。 +5. **执行期不改决定。** route 只读取 compilation 中已经确定的目标,并且只调用一个 + 执行入口;执行期间不得重新判定、改选或隐式降级。 +6. **只有一个策略汇合点。** `engine/route.c` 是唯一同时看见 vmkv、BinRWIR Compiler、 + 内部 JIT executor 和 vmbin 的文件,也是唯一执行调度与提交入口。 +7. **错误立即停止。** 不猜测输入、不截断、不静默修复、不把错误转换成另一条路径。 +8. **不保留旧入口。** 公共层只有 `include/kvlang/runtimec.h` 和 + `interface/public_interface.c` 定义的结构化接口。 +9. **Less code, more useful capability.** 不建立第三执行器,不为错误分层增加修补代码。 +10. **V.body 只有一个权威驻留位置。** 语言可见且可恢复的 value body 始终位于 kvspace; + XValue 固定由 8 字节 head 与紧随其后的 `V.body` 组成,因此 + `argaddr = xvalue_address + 8`。BinRWIR 与 vmbin 只持有最终 `argaddr`,不复制、不缓存 + XValue 或 body,也不建立 register file。 +11. **地址派生只有一个所有者。** host 对静态 key 只返回非零、生命周期有效的 64 位 + XValue 基址;只有 vmkv 可以验证固定 8 字节 head ABI、检查加法溢出并执行 `+8`。 + host、BinRWIR、Compiler、vmbin 与 JIT 都不得选择或重算 body 偏移。 +12. **地址错误失败封闭。** 零 XValue 基址、`xvalue_address + 8` 溢出、非 8 字节 head ABI、 + 动态 key 或无法证明生命周期都不得产生 BinRWIR。map、struct 等动态拼接 key 在 + key/xvalue 生命周期 ABI 建立前明确返回 `RUNTIMEC_STATUS_UNSUPPORTED`。 +13. **每条指令完成都是强制边界。** 每个 logical instruction completion 都必须成为 + recovery/observable boundary,不能由配置、优化、融合或执行目标关闭。 +14. **完成提交,未完成丢弃。** JIT 只能把完整完成指令的 dirty state 提交到原 + `argaddr`;当前未完成指令的 speculative/partial register 与 spill state 在 guard + failure、error、recovery 或 resume 时必须丢弃,绝不写回。 +15. **可观测性与恢复不可交易。** 保留逐指令 `pc` 与 `status` 的可观察、可暂停、可恢复 + 语义;不迁移 `mem_frame` / `segment_may_stop` 这类不可观察纯内存帧,调用边界优化 + 不得以牺牲任意指令可恢复为代价。 +16. **派生状态必须可丢弃可重建。** Program、execution plan、机器码、JIT register/spill、 + profile 与 repository 条目都是从 kvspace 明文 RWIR 与当前 `V.body` 派生的进程内状态。 + 删除全部派生缓存后,必须能从明文重建并继续执行;它们不是第二份持久事实。 + +## 2. 唯一主干 + +```text +myrwircaps + plaintext RWIR in kvspace + -> vmkv validates RWIR and requests each static key + -> host dereferences each static key to a uint64 XValue base address + -> vmkv validates the fixed 8-byte XValue head and derives argaddr = xvalue_address + 8 + -> vmkv emits addressed canonical BinRWIR + -> binrwir_compiler builds execution plan and attempts internal JIT compilation + -> compile-time target decision: JIT code or VMBIN plan + -> compilation repository caches the complete compilation + -> engine route reads the decided target before execution + -> binrwir_jit_execute or vmbin_execute + -> route commits complete effects and checkpoint +``` + +主干顺序不能重排: + +- `specification/` 保存行为决策、RWIR 操作事实及 myrwircap 到 canonical BinRWIR 的映射。 +- `vmkv/` 从 kvspace 读取含 operation name、参数类型和参数名的明文 RWIR,通过 host 将 + 每个静态参数 key 解引用为 XValue 基址,并作为唯一组件校验固定 8 字节 head、检查 `+8` + 溢出和生成最终 `argaddr`;只有全部地址合格时才生成 canonical BinRWIR Program。 +- `binrwir/` 拥有中立的进程内 Program、`opid` 与 `argidx + uint64 argaddr` 参数记录、 + signature、execution frame、execution exit 与 execution plan 合同;它不属于 vmbin。 +- `binrwir_compiler/` 是 canonical BinRWIR 的唯一编译组件。它派生 execution plan, + 在内部 `jit/` 尝试生成机器码,并形成不可再改选的 compilation。 +- `engine/compilation_repository` 缓存完整 compilation,使 canonical Program、execution + plan 和可选机器码在同一租约内有效。 +- `engine/route` 在执行前读取 `binrwir_execution_target_t`。目标为 JIT 时只调用 + `binrwir_jit_execute`;目标为 VMBIN 时只调用 `vmbin_execute`。 +- `binrwir_jit_execute` 只执行 compilation 已持有的机器码;`vmbin_execute` 按 + `argaddr` 直接读写 kvspace body。两者都不能编译或改变目标。 + +## 3. 目录主干 + +```text +runtimec/ +├── include/kvlang/runtimec.h # 唯一公共 C 头 +├── interface/ # 公共请求、结果与精确导出名单 +├── contract/ # 跨组件状态、错误、所有权与主机服务合同 +├── host/ # kvspace 与外部能力适配 +├── specification/ # 行为、RWIR 与 canonical 映射事实源 +├── vmkv/ # 明文 RWIR 验证、参数解引用与 BinRWIR 生成 +├── binrwir/ # 进程内 opid + argidx + argaddr 合同 +│ ├── instruction.* +│ ├── argument.* +│ ├── signature.* +│ ├── program.* +│ ├── execution_frame.* +│ ├── execution_exit.h +│ └── execution_plan.* +├── binrwir_compiler/ # BinRWIR 编译、优化与目标判定唯一所有者 +│ ├── compiler.* # compilation、内部 JIT 请求与最终目标 +│ ├── optimization.* # 不缓存 body 的 execution plan 优化 +│ └── jit/ # BinRWIR Compiler 的机器码内部实现 +│ ├── compiler.* # 唯一 Program + plan → code 入口 +│ ├── verifier.* # 编译期 eligibility +│ ├── lowering_plan.* # execution plan 的目标绑定 +│ ├── lower.* # lowering plan → backend request +│ ├── backend.h # 目标架构与机器码生成合同 +│ ├── backend/ # x86_64 与 AArch64 并列实现 +│ ├── code_memory.* # 可写到可执行的内存发布 +│ ├── code.* # 已发布机器码句柄 +│ ├── execution_interface.* # 机器码调用合同 +│ └── executor.* # 已生成机器码的确定执行入口 +├── vmbin/ # 完整 BinRWIR 基线执行器 +├── engine/ # owner graph、唯一 compilation cache、唯一 route +│ ├── session.* # runtimec owner graph 根 +│ ├── compilation_repository.* # 完整 compilation 的唯一缓存 +│ └── route.* # 执行前单选目标与提交 +├── documentation/architecture/ # 架构说明与 Mermaid 源 +├── tools/architecture/ # 名称、依赖、矩阵与导出闸门 +└── tests/ # 公共合同与失败封闭测试 +``` + +组件没有源文件时,构建配置必须立即失败。禁止生成空翻译单元让缺失组件看似成功。 + +## 4. Specification 与 canonical 映射 + +RWIR 是 kvspace 中可恢复的明文存储格式。每条调用保留 operation name;每个参数保留 +参数类型和参数名,并以 Read 或 Write 方向参与调用。`myrwircaps` 表示本 runtime 能就地 +兑现的 RWIR capability 集合。runtimec 不使用数组 +下标、文件顺序或声明顺序作为操作身份。每个 myrwircap 必须在 +`specification/rwir_catalog.def` 中拥有一条且只有一条映射记录,该记录必须显式给出: + +- 稳定 RWIR operation identifier; +- myrwircap 的规范名称; +- 唯一 canonical BinRWIR operation identifier; +- Read 参数与 Write 参数结构; +- 每个参数在 canonical signature 中的 `argidx`; +- 五语言行为决策引用。 + +稳定 identifier 是规范数据,不是容器位置。重新排序 manifest、插入新操作或改变生成器 +都不能改变既有 identifier;已删除 identifier 不得复用。以下情况都是编译错误: + +- myrwircap 没有 canonical 映射; +- 同一个 myrwircap 出现多条映射; +- 两条记录复用同一个稳定 identifier; +- 名称、identifier、参数方向或行为决策无法得到唯一结果。 + +vmkv 必须逐条执行这个映射并保持语义等价:operation name 映射为 `opid`,参数类型与 +参数名经 signature 验证后映射为 `argidx`;host 将参数 key 解引用为 64 位 XValue 基址, +vmkv 验证 XValue head ABI 固定为 8 字节并以溢出检查计算 +`uint64 argaddr = xvalue_address + 8`。它不能用近似操作、默认操作、未知操作或数组位置补洞。 +host 不得返回自行选择偏移后的 body 地址。完整规则见 +`specification/myrwircap-binrwir-mapping.md`。 + +## 5. BinRWIR 中立合同 + +BinRWIR 属于顶层 `binrwir/`,不属于 vmkv、vmbin 或 JIT 内部实现。它是进程内二进制 +派生格式,不是持久化格式,也不是 RWIR 的另一份明文表示: + +- vmkv 通过 builder 构造 canonical Program; +- Compiler 只读 canonical Program 并派生 execution plan; +- vmbin 与 `binrwir_compiler/jit/` 消费相同的中立合同; +- Program finalize 后不可变,但任何地址生命周期失效都会使整个 Program 失效; +- execution plan、机器码、profile 与缓存都是可删除并重建的派生状态。 + +热 instruction 只保存 `opid`;每个参数只保存: + +```text +opid + argidx + uint64 argaddr + argidx + uint64 argaddr + ... +``` + +`argidx` 在由 `opid` 选中的 canonical signature 中确定参数顺序、类型和 Read/Write +权限。key 解引用结果是 64 位 XValue 基址,不是任意 body 地址;XValue head ABI 固定为 +8 字节,vmkv 唯一执行 `argaddr = xvalue_address + 8`,所以 `argaddr` 仍直接指向 kvspace +`V.body`。operation name、参数类型、参数名、key 文本、XValue 和 body 均不复制进热 +instruction。 + +公共二进制布局固定为:4 字节 instruction header 仅含 `opid`;每个参数记录在偏移 0 +保存 32 位 `argidx`,偏移 4 是必须清零的 32 位地址对齐填充,偏移 8 保存 64 位 +`argaddr`。对齐填充没有语义。可变参数的 Read/Write span、source mapping 与存储 framing +只存在于 Program 的私有实现和非二进制只读 view 中,不得重新进入 instruction 字段。 + +BinRWIR 保留 RWIR 的不固定参数数量语义;确定的一次调用必须编码全部参数对,不能截断 +参数或拆分一个原子 RWIR 操作。两个不同 `argidx` 可以持有相同 `argaddr`,这就是已声明 +Read/Write alias 在二进制形式中的表示。 + +BinRWIR 保存的是 vmkv 派生后的最终 `argaddr`,不是 XValue 基址、XValue 或 `V.body`; +vmbin 也只消费该最终地址。vthread 的 `pc`、`status` 以及 `/lib` 下函数源等 key 生命周期 +长且 XValue 基址稳定,是当前允许的优化范围。 +map、struct 等依赖运行时数据动态拼接的 key,其 body 可能尚未分配或地址会变化;在 +key/xvalue 生命周期 ABI 能提供 pin、generation 与失效合同前,vmkv 必须返回 +`RUNTIMEC_STATUS_UNSUPPORTED`,不得写零地址、延迟间接查找或建立 fallback Program。 + +## 6. BinRWIR Compiler + +`binrwir_compiler/` 负责 canonical BinRWIR 之后、执行之前的全部派生工作: + +1. 验证 canonical Program 与 specification identity。 +2. 生成 execution plan,包括紧凑指令调度、`opid` 特化、静态 `argaddr` 使用、在保持 + 每条逻辑指令写回边界前提下的融合,以及控制流派生信息。 +3. 在启用 JIT 时,由内部 `jit/` verifier、lowering 与 backend 尝试生成机器码。 +4. 把 Program、execution plan、已决定目标与可选机器码封装为 compilation。 + +内部 JIT 编译判定只有以下三种有意义的方向;顶层 compilation 对象只在前两种成功 +方向产生,因此不存在可观察的“正在编译”或“错误 compilation”对象: + +| Compiler 判定 | 调用状态 | compilation 内容 | 后续方向 | +|---|---|---|---| +| `COMPILED` | `RUNTIMEC_STATUS_SUCCESS` | JIT 目标、Program、plan、机器码 | route 只调用 `binrwir_jit_execute` | +| `NOT_COMPILABLE` | `RUNTIMEC_STATUS_SUCCESS` | VMBIN 目标、Program、plan、无机器码 | route 只调用 `vmbin_execute` | +| `ERROR` | 明确错误状态 | 不产生可执行 compilation | 立即停止 | + +`NOT_COMPILABLE` 是成功完成的可编译性判定,不是执行错误。未知目标架构、明确不支持的 +操作或无法满足机器码执行合同可以得到该判定。非法 BinRWIR、映射不完整、内存不足与 +内部错误必须进入 `ERROR` 并立即返回,不能生成 VMBIN compilation。 + +通用 BinRWIR execution plan 不得包含任何 value body 副本或通用寄存器分配。寄存器分配 +只允许存在于内部 JIT lowering 结果中。 + +## 7. Repository 与 route + +repository 的缓存单位是 compilation,不是裸 Program、裸 execution plan 或裸机器码。 +缓存键至少绑定 authoritative snapshot、RWIR manifest version、canonical encoding +revision、BinRWIR binary interface generation 与 compiler revision。租约保证 execution +期间 compilation 的全部对象同时有效。 +因为 BinRWIR 含进程内 `argaddr`,缓存身份还必须受完整 `snapshot.source_revision` 约束;地址 +失效后不能继续租用旧 compilation,只能从 kvspace 中的 RWIR 与当前 body 地址重新生成。 +内部 JIT 不建立第二套 code cache,也不存在绕过 BinRWIR Compiler 的独立编译入口。 + +route 的“选择”只表示读取已经编译好的 `execution_target` 并调用对应入口。route 不再次 +询问 JIT 是否可用,不重新验证 eligibility,也不在一个入口返回后调用另一个入口。执行 +错误、机器码错误和内部错误都沿原路径报告并停止。 + +每条 logical instruction completion 都是不可关闭的 recovery/observable boundary。vmbin +在该边界完成前,所有 Write 结果必须已经位于对应 `argaddr` 指向的 kvspace body。JIT +只能在当前逻辑指令内部把 body 临时提升到机器寄存器或 spill;指令完整完成后,才允许 +执行 `COMMIT_COMPLETED_STATE`,把该指令的全部 dirty state materialize 到原 `argaddr`。 +只有全部 body 写回成功,route 才能推进 logical program counter 与 checkpoint。 + +guard failure、error、recovery 或 resume 落在当前指令尚未完成时,JIT 必须执行 +`DISCARD_INCOMPLETE_STATE`:丢弃该指令全部 speculative/partial register 与 spill state, +materialization entry 必须为空,不得向任何 `argaddr` 写回部分结果。logical program +counter 与 checkpoint 保持最后一个已提交完成边界;恢复只从 kvspace 重建机器状态。 + +## 8. Read/Write 与别名 + +RWIR 和 BinRWIR 只有 Read 参数与 Write 参数。原地算法通过两个不同 `argidx` 保存同一个 +`argaddr` 表达显式别名,不引入第三种参数方向: + +```text +Read argidx ──> uint64 argaddr ──┐ + ├──> one kvspace V.body +Write argidx ──> uint64 argaddr ──┘ +``` + +- Read `argidx` 只能读,Write `argidx` 只能写;权限由 `(opid, argidx)` 的 signature 决定。 +- 两个 `argidx` 始终不同,即使 `argaddr` 完全相同。 +- canonical signature 是 alias policy 的唯一拥有者,地址相等只证明实际发生 alias。 +- 写入后,后续通过 Read 别名看到新值,并遵循程序顺序。 +- 未声明允许的别名立即返回 `RUNTIMEC_STATUS_INVALID`。 +- vmbin 不得用预读或 body cache 让后续 Read 看见旧值;JIT 必须维护 alias dependency。 + 当前指令内的 aliased Read 观察 speculative Write,只有指令完整完成后才能整体写回; + 未完成状态必须整体丢弃。 + +## 9. 组件边界 + +### vmkv + +vmkv 只做明文 RWIR source 读取、结构验证、`opid`/`argidx` 映射、通过 host 将参数 key +解引用为 XValue 基址、静态地址资格验证,以及固定 8 字节 head 的唯一 `+8` 派生和 +canonical BinRWIR 生成。host 不能决定 body 偏移。类型固化、融合、执行计划、机器码、 +缓存与执行目标都不属于 vmkv。 + +### vmbin + +vmbin 是 address-driven memory executor。它从每个 `argaddr` 指向的 kvspace body 读取, +并在逻辑 instruction 完成前把 Write 结果写入对应 body。它不拥有 xvalue/value cache、 +software register file 或跨逻辑指令的 body 临时副本。generic、typed、fused +与 continuous handler 只能是 `vmbin_execute` 背后的内部 dispatch 策略;融合不得消除 +kvspace 写回,也不得跳过任何 logical instruction completion recovery/observable boundary。 + +### binrwir_compiler/jit + +JIT 是 BinRWIR Compiler 的内部机器码实现,不是 VM,也不是并列顶层组件。它是唯一允许 +把 `V.body` 临时提升到机器寄存器、进行 register allocation 和使用 spill 的路径。每条 +logical instruction completion 都是强制 safepoint:完整完成时执行 +`COMMIT_COMPLETED_STATE`;guard failure、error、recovery 或 resume 遇到未完成指令时执行 +`DISCARD_INCOMPLETE_STATE`。提交只能包含 completed dirty state;丢弃不能包含任何 +materialization entry。logical program counter/checkpoint 只在完整提交成功后推进。 +可编译性必须在 compilation 建立期间确定;`binrwir_jit_execute` 不接收 RWIR、不解析 key、 +不依赖 vmbin,也不拥有执行期目标选择能力。 + +## 10. 状态与错误 + +`runtimec_status_t` 的唯一成功值是 `RUNTIMEC_STATUS_SUCCESS`: + +- `YIELD`、`WAIT`:执行尚未正常完成; +- `UNSUPPORTED`:当前合同明确不接受该能力; +- `NOT_IMPLEMENTED`:骨架尚无该能力; +- `INVALID`:输入违反合同; +- `OUT_OF_MEMORY`、`ERROR`:明确失败。 + +`COMPILED` 与 `NOT_COMPILABLE` 是内部 JIT 编译判定,不是替代 `runtimec_status_t` 的 +第二套调用状态。`ERROR` 只通过明确错误状态返回,并且不产生 compilation。所有输出 +参数必须先进入明确无效状态,再开始可能失败的工作。 + +当前动态拼接 key 属于 `UNSUPPORTED`:vmkv 不得为 map/struct 参数产生零 `argaddr`, +BinRWIR Compiler 也不得把它改写为运行时 key lookup 或借 JIT 绕过限制。零 XValue 基址、 +`+8` 溢出、非 8 字节 XValue head ABI 或无法证明地址生命周期同样必须在 Program 产生前 +失败封闭。 + +## 11. 五语言决策矩阵 + +`specification/five-language-behavior-matrix.md` 是语言行为决策的唯一事实源。 + +- C、Python、Rust、Go、V8/TypeScript 一致:kvlang 必须遵从。 +- 五家分裂:记录选择阵营、原因、边界 fixture 和非法输入结果。 +- 每个 specification manifest 条目必须引用已有 `behavior-decision`。 +- 没有矩阵记录,不能添加 myrwircap 映射、canonical operation 或执行语义。 + +## 12. 依赖方向 + +允许方向: + +```text +interface -> public header + contract + engine +engine/route -> host + vmkv + binrwir_compiler + binrwir + binrwir_compiler/jit executor + vmbin +engine/compilation_repository -> binrwir_compiler + contract +binrwir_compiler -> contract + binrwir + internal jit +vmkv -> contract + host address-resolution contract + binrwir build contracts + specification generated facts +vmbin -> contract + binrwir execution contracts +binrwir_compiler/jit -> contract + binrwir execution contracts +host -> contract + external kvspace interface +``` + +禁止方向: + +```text +binrwir -> vmkv/vmbin/binrwir_compiler/engine +vmkv -> binrwir_compiler/vmbin +vmbin -> vmkv/binrwir_compiler +binrwir_compiler/jit -> vmkv/vmbin/concrete host +repository -> vmkv/vmbin/binrwir_compiler/jit +engine files other than route -> vmkv/vmbin/binrwir_compiler/jit +public header -> internal runtimec header +``` + +## 13. 公共接口、名称与验收 + +公共层只安装 `include/kvlang/runtimec.h`,只导出 `interface/exports.txt` 中的符号。 +公共头不暴露 RWIR、BinRWIR、vmkv、BinRWIR Compiler、vmbin、JIT 或 kvspace 内部结构。 + +除 kvlang、kvspace、runtimec、RWIR、BinRWIR、vmkv、vmbin、JIT、vthread、langtype +及 C 标准类型外,自造名称使用完整单词,不保留旧名称别名或转发头。 + +最小 fresh 验收: + +```bash +cmake -S runtimec -B /tmp/kvlang-runtimec-build \ + -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON +cmake --build /tmp/kvlang-runtimec-build --parallel +ctest --test-dir /tmp/kvlang-runtimec-build --output-on-failure +``` + +语义实现后按 shared memory → benchmark quick → benchmark full → tutorial 的顺序验证。 +当前 scaffold 没有执行语义,不运行 benchmark 或 tutorial。 + +## 14. 后续实现合同 + +实现前按此顺序读取: + +1. `README.md` +2. `documentation/architecture/runtimec-architecture.md` +3. `specification/README.md` +4. `specification/myrwircap-binrwir-mapping.md` +5. `specification/five-language-behavior-matrix.md` +6. 目标组件头文件 + +实现按 #622 的依赖顺序在 runtimec 结构内落地,禁止跨阶段抢跑: + +- 阶段 1:先填 `vmbin/` 与 `engine/` 的逐指令 `pc`/`status` 执行引擎,以纯解释档对齐 + c2 执行引擎为第一个硬指标;在引擎达标前不动 JIT。 +- 阶段 2:填 `vmkv/` 与 `binrwir/` 的明文艺文验证、单一 manifest 生成、typed 特化与 + 等价 canonical BinRWIR 生成。 +- 阶段 3:填 `binrwir_compiler/`(含内部 JIT):M1 手写发射器 → JIT2 → 生成器标量矩阵 → + x86_64;AArch64 后端同阶段一等实现,不得顺延为空桩。 +- 阶段 4:runtimec 成为默认 runtime,冻结并删除 `runtimec2/`,旧 `runtime/` 不再作为 + 独立实现继续演进。 + +每个阶段都必须先补齐对应 specification 决策与映射,再实现函数。遇到不清楚的输入、 +映射或状态必须失败,不能添加旧名字别名、隐藏执行路径或空组件兜底。 + +## 15. 与 #622、#499、#541 的对齐 + +runtimec 是 #622“runtime 回归 C”的新落点:它取代旧 `runtime/` 与 `runtimec2/` 两支 +实现,成为唯一 C11 运行时主干;两个旧目录只读冻结,不出现在 runtimec 构建、测试或 +文档的主干中。 + +- 语义裁决先落 spec:`#622 4.1` 六项与 `#622 4.3` 两项已写入 + `specification/five-language-behavior-matrix.md`;没有矩阵决策,不得实现对应语义。 +- 单一事实源:`specification/rwir_catalog.def` 是 myrwircap → canonical BinRWIR 的唯一 + manifest;签名与执行家族从同一输入生成,CI 必须断言不存在可与它漂移的第二份表。 +- 检查点粒度:保留逐指令 `pc` + `status`,不迁移 `mem_frame`(`#622 4.4`)。 +- BIN 栈可重建:删除全部派生缓存后,从 kvspace 明文 RWIR 与当前 `V.body` 重建,并从 + 最后已提交完成边界继续执行(#499)。 +- AArch64 一等后端:`binrwir_compiler/jit/backend/aarch64.c` 与 x86_64 是 peer,必须实现 + 同一 backend 合同,不得长期保留空实现(#541)。 +- JIT 发射器先定形态:先手写 M1 发射器并用固定 opcode 表量出代码量与可读性,再决定 + JIT2 组织;标量矩阵由生成器产生,禁止手写第二份表。 diff --git a/runtimec/binrwir/argument.c b/runtimec/binrwir/argument.c new file mode 100644 index 00000000..3d097de1 --- /dev/null +++ b/runtimec/binrwir/argument.c @@ -0,0 +1,35 @@ +/* + * 组件:binrwir/argument 的实现 + * 作用:地址化参数的构造与校验入口;真实地址解析尚未实现。 + * 约束:所有输出先进入零地址无效状态,动态 key 不得伪装为已解析。 + */ + +#include "binrwir/argument.h" + +#include +#include + +runtimec_status_t binrwir_argument_create( + uint32_t argument_index, uint64_t body_address, + binrwir_argument_t *output_argument) { + if (output_argument != NULL) { + memset(output_argument, 0, sizeof(*output_argument)); + } + if (output_argument == NULL || + argument_index == BINRWIR_ARGUMENT_INDEX_NONE || + body_address == BINRWIR_BODY_ADDRESS_NONE) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_argument_validate( + const binrwir_argument_t *argument) { + if (argument == NULL || + argument->argument_index == BINRWIR_ARGUMENT_INDEX_NONE || + argument->body_address == BINRWIR_BODY_ADDRESS_NONE || + argument->reserved_32_bits != 0U) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir/argument.h b/runtimec/binrwir/argument.h new file mode 100644 index 00000000..e4f241c5 --- /dev/null +++ b/runtimec/binrwir/argument.h @@ -0,0 +1,47 @@ +#ifndef BINRWIR_ARGUMENT_H +#define BINRWIR_ARGUMENT_H + +/* + * 组件:binrwir/argument + * 作用:记录 RWIR 参数序号及其解引用后的 KVspace V.body 内存地址。 + * 联系:instruction 按读取区和写入区引用 Program 的 argument 池。 + * 约束:二进制语义字段严格按 argidx、argaddr 排列。中间 32 位只是必须为零的 + * 64 位地址对齐填充,不是第三个语义字段;argument 不拥有或缓存 V.body。 + */ + +#include +#include + +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_BODY_ADDRESS_NONE UINT64_C(0) +#define BINRWIR_ARGUMENT_INDEX_NONE UINT32_MAX + +typedef struct binrwir_argument { + uint32_t argument_index; + uint32_t reserved_32_bits; + uint64_t body_address; +} binrwir_argument_t; + +runtimec_status_t binrwir_argument_create( + uint32_t argument_index, uint64_t body_address, + binrwir_argument_t *output_argument); +runtimec_status_t binrwir_argument_validate( + const binrwir_argument_t *argument); + +_Static_assert(sizeof(binrwir_argument_t) == 16U, + "binrwir arguments must remain 16 bytes"); +_Static_assert(offsetof(binrwir_argument_t, argument_index) == 0U, + "binrwir argidx must begin each argument"); +_Static_assert(offsetof(binrwir_argument_t, body_address) == 8U, + "binrwir argaddr must follow canonical alignment padding"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/execution_exit.h b/runtimec/binrwir/execution_exit.h new file mode 100644 index 00000000..df870e66 --- /dev/null +++ b/runtimec/binrwir/execution_exit.h @@ -0,0 +1,54 @@ +#ifndef BINRWIR_EXECUTION_EXIT_H +#define BINRWIR_EXECUTION_EXIT_H + +/* + * 组件:binrwir/execution_exit + * 作用:表达 vmbin 或 BinRWIR JIT 离开执行窗口的唯一结构化结果。 + * 联系:engine route 消费退出并决定调用、交接、恢复或报错。 + * 约束:只携带控制信息。语言可见值必须已写回 V.body;不得携带值、机器 + * 地址、重试或反优化请求,所选执行目标失败即失败。 + */ + +#include + +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_EXECUTION_PROGRAM_COUNTER_NONE UINT32_MAX + +typedef enum binrwir_execution_exit_kind { + BINRWIR_EXECUTION_EXIT_NONE = 0, + BINRWIR_EXECUTION_EXIT_DONE, + BINRWIR_EXECUTION_EXIT_CALL, + BINRWIR_EXECUTION_EXIT_BUILTIN, + BINRWIR_EXECUTION_EXIT_HANDOFF, + BINRWIR_EXECUTION_EXIT_YIELD, + BINRWIR_EXECUTION_EXIT_ERROR +} binrwir_execution_exit_kind_t; + +typedef enum binrwir_execution_exit_flags { + BINRWIR_EXECUTION_EXIT_FLAG_NONE = 0, + BINRWIR_EXECUTION_EXIT_FLAG_COMPLETED_STATE_COMMITTED = 1U << 0 +} binrwir_execution_exit_flags_t; + +typedef struct binrwir_execution_exit { + uint16_t kind; /* binrwir_execution_exit_kind_t */ + uint16_t flags; /* binrwir_execution_exit_flags_t */ + runtimec_status_t status; /* 组件状态,与结构化退出原因正交 */ + uint32_t logical_program_counter; + uint32_t resume_logical_program_counter; + uint32_t detail_code; /* 结构化细节码(拒绝原因/错误分类) */ + uint32_t target_identifier; +} binrwir_execution_exit_t; + +_Static_assert(sizeof(binrwir_execution_exit_t) == 24U, + "binrwir structured exits must contain only control state"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/execution_frame.c b/runtimec/binrwir/execution_frame.c new file mode 100644 index 00000000..e8a4d8ac --- /dev/null +++ b/runtimec/binrwir/execution_frame.c @@ -0,0 +1,45 @@ +/* + * 组件:binrwir/execution_frame 的实现 + * 作用:安全初始化和重置只保存控制状态的 Frame。 + * 约束:Frame 不拥有 Program,也不保存 V.body 地址或内容。 + */ + +#include "binrwir/execution_frame.h" + +#include + +runtimec_status_t binrwir_execution_frame_initialize( + binrwir_execution_frame_t *frame, const binrwir_program_t *program, + binrwir_execution_frame_t *parent, + uint64_t snapshot_generation, uint64_t program_generation, + uint32_t logical_program_counter) { + if (frame == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(frame, 0, sizeof(*frame)); + if (program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + + frame->program = program; + frame->parent = parent; + frame->snapshot_generation = snapshot_generation; + frame->program_generation = program_generation; + frame->logical_program_counter = logical_program_counter; + frame->flags = BINRWIR_EXECUTION_FRAME_FLAG_INITIALIZED; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t binrwir_execution_frame_reset( + binrwir_execution_frame_t *frame) { + if (frame == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(frame, 0, sizeof(*frame)); + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t binrwir_execution_frame_destroy( + binrwir_execution_frame_t *frame) { + return binrwir_execution_frame_reset(frame); +} diff --git a/runtimec/binrwir/execution_frame.h b/runtimec/binrwir/execution_frame.h new file mode 100644 index 00000000..06eb230d --- /dev/null +++ b/runtimec/binrwir/execution_frame.h @@ -0,0 +1,54 @@ +#ifndef BINRWIR_EXECUTION_FRAME_H +#define BINRWIR_EXECUTION_FRAME_H + +/* + * 组件:binrwir/execution_frame + * 作用:保存 BinRWIR 执行所需的控制状态。 + * 联系:engine route 创建 Frame;vmbin 与 BinRWIR JIT 消费。 + * 约束:Frame 不保存参数绑定或 V.body 内容。语言可见值的唯一权威副本 + * 位于 kvspace;每条源指令边界必须可观察。 + */ + +#include + +#include "contract/status.h" +#include "binrwir/program.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_execution_frame_flags { + BINRWIR_EXECUTION_FRAME_FLAG_NONE = 0, + BINRWIR_EXECUTION_FRAME_FLAG_INITIALIZED = 1U << 0, + BINRWIR_EXECUTION_FRAME_FLAG_OBSERVABLE = 1U << 1, + BINRWIR_EXECUTION_FRAME_FLAG_RESUMED = 1U << 2 +} binrwir_execution_frame_flags_t; + +typedef struct binrwir_execution_frame { + const binrwir_program_t *program; + struct binrwir_execution_frame *parent; + uint64_t snapshot_generation; + uint64_t program_generation; + uint32_t logical_program_counter; + uint32_t flags; +} binrwir_execution_frame_t; + +#define BINRWIR_EXECUTION_FRAME_ALIGNMENT \ + ((uint32_t)_Alignof(binrwir_execution_frame_t)) + +runtimec_status_t binrwir_execution_frame_initialize( + binrwir_execution_frame_t *frame, const binrwir_program_t *program, + binrwir_execution_frame_t *parent, + uint64_t snapshot_generation, uint64_t program_generation, + uint32_t logical_program_counter); +runtimec_status_t binrwir_execution_frame_reset( + binrwir_execution_frame_t *frame); +runtimec_status_t binrwir_execution_frame_destroy( + binrwir_execution_frame_t *frame); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/execution_plan.c b/runtimec/binrwir/execution_plan.c new file mode 100644 index 00000000..0d65165b --- /dev/null +++ b/runtimec/binrwir/execution_plan.c @@ -0,0 +1,39 @@ +/* execution plan 生命周期与查询的失败封闭骨架。 */ + +#include "binrwir/execution_plan.h" +#include "binrwir/execution_plan_internal.h" + +#include + +runtimec_status_t binrwir_execution_plan_validate( + const binrwir_execution_plan_t *plan) { + if (plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_execution_plan_get_view( + const binrwir_execution_plan_t *plan, + binrwir_execution_plan_view_t *output_view) { + if (output_view != NULL) { + memset(output_view, 0, sizeof(*output_view)); + output_view->identity.snapshot_generation = UINT64_MAX; + output_view->identity.program_generation = UINT64_MAX; + output_view->identity.plan_generation = UINT64_MAX; + output_view->identity.binrwir_binary_interface_generation = UINT32_MAX; + output_view->identity.compiler_revision = UINT32_MAX; + } + if (plan == NULL || output_view == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_execution_plan_destroy( + binrwir_execution_plan_t *plan) { + if (plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir/execution_plan.h b/runtimec/binrwir/execution_plan.h new file mode 100644 index 00000000..db7d9e2c --- /dev/null +++ b/runtimec/binrwir/execution_plan.h @@ -0,0 +1,115 @@ +#ifndef BINRWIR_EXECUTION_PLAN_H +#define BINRWIR_EXECUTION_PLAN_H + +/* + * execution plan 是 canonical BinRWIR Program 的只读派生物。它可以随时 + * 删除并从同一 Program 重建;它不是 RWIR 或 BinRWIR 的语义事实源。 + * execution plan 只承载类型特化、dispatch 融合、控制流和 source logical + * program counter 映射。Program 已经是地址化二进制,不得在 plan 中重复 + * 编码 instruction、解析或缓存地址,也不得建立任何中间值存储模型。 + * BinRWIR Compiler 在编译阶段确定执行目标;vmbin 与 BinRWIR JIT 只执行 + * 已选择的目标,不拒绝计划、不重新选路。 + */ + +#include +#include + +#include "contract/status.h" +#include "binrwir/program.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_EXECUTION_PLAN_IDENTIFIER_NONE UINT32_MAX + +typedef enum binrwir_execution_plan_flags { + BINRWIR_EXECUTION_PLAN_FLAG_NONE = 0, + BINRWIR_EXECUTION_PLAN_FLAG_HAS_TYPE_SPECIALIZATION = 1U << 0, + BINRWIR_EXECUTION_PLAN_FLAG_HAS_DISPATCH_FUSION = 1U << 1, + BINRWIR_EXECUTION_PLAN_FLAG_HAS_CONTROL_FLOW = 1U << 2, + BINRWIR_EXECUTION_PLAN_FLAG_HAS_SOURCE_MAPPING = 1U << 3 +} binrwir_execution_plan_flags_t; + +typedef struct binrwir_execution_plan_identity { + uint64_t snapshot_generation; + uint64_t program_generation; + uint64_t plan_generation; + uint32_t binrwir_binary_interface_generation; + uint32_t compiler_revision; +} binrwir_execution_plan_identity_t; + +typedef struct binrwir_type_specialization { + uint32_t instruction_identifier; + uint32_t type_identifier; + uint32_t guard_identifier; + uint32_t specialized_operation_identifier; + uint16_t flags; +} binrwir_type_specialization_t; + +/* + * dispatch fusion 只减少解释器分发。它必须保持每条逻辑 instruction 的 + * V.body 写回和 source mapping,且不得跨越 recovery boundary。 + */ +typedef struct binrwir_dispatch_fusion { + uint32_t first_instruction_identifier; + uint32_t instruction_count; + uint32_t fused_dispatch_identifier; + uint32_t first_source_mapping; + uint32_t source_mapping_count; +} binrwir_dispatch_fusion_t; + +typedef struct binrwir_control_flow_block { + uint32_t first_instruction_identifier; + uint32_t instruction_count; + uint32_t successor_offset; + uint16_t successor_count; + uint16_t flags; +} binrwir_control_flow_block_t; + +typedef struct binrwir_source_program_counter_mapping { + uint32_t execution_dispatch_identifier; + uint32_t source_logical_program_counter; + uint32_t first_instruction_identifier; + uint32_t instruction_count; +} binrwir_source_program_counter_mapping_t; + +typedef struct binrwir_execution_plan binrwir_execution_plan_t; + +typedef struct binrwir_execution_plan_view { + binrwir_execution_plan_identity_t identity; + const binrwir_program_t *program; + const binrwir_type_specialization_t *type_specializations; + size_t type_specialization_count; + const binrwir_dispatch_fusion_t *dispatch_fusions; + size_t dispatch_fusion_count; + const binrwir_control_flow_block_t *control_flow_blocks; + size_t control_flow_block_count; + const uint32_t *control_flow_successors; + size_t control_flow_successor_count; + const binrwir_source_program_counter_mapping_t *source_mappings; + size_t source_mapping_count; + uint32_t flags; + uint32_t reserved_32_bits; +} binrwir_execution_plan_view_t; + +_Static_assert(sizeof(binrwir_execution_plan_identity_t) == 32U, + "binrwir execution plan identity must remain 32 bytes"); +_Static_assert( + sizeof(((binrwir_type_specialization_t *)0) + ->specialized_operation_identifier) == sizeof(uint32_t), + "specialized operation identifiers must remain 32 bits"); + +runtimec_status_t binrwir_execution_plan_validate( + const binrwir_execution_plan_t *plan); +runtimec_status_t binrwir_execution_plan_get_view( + const binrwir_execution_plan_t *plan, + binrwir_execution_plan_view_t *output_view); +runtimec_status_t binrwir_execution_plan_destroy( + binrwir_execution_plan_t *plan); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/execution_plan_internal.h b/runtimec/binrwir/execution_plan_internal.h new file mode 100644 index 00000000..04779093 --- /dev/null +++ b/runtimec/binrwir/execution_plan_internal.h @@ -0,0 +1,22 @@ +#ifndef BINRWIR_EXECUTION_PLAN_INTERNAL_H +#define BINRWIR_EXECUTION_PLAN_INTERNAL_H + +/* + * 中立 execution plan 的共享私有布局。binrwir_compiler/optimization 负责构造, + * binrwir/execution_plan 只负责验证、查询与销毁。允许的四类派生数组放入 + * 一个 owned_storage 块。Program 自身的 instruction/argument 二进制和其中 + * 的 V.body 地址不复制到 plan,也不在这里建立缓存或寄存器状态。 + */ + +#include "binrwir/execution_plan.h" +#include "contract/allocator.h" + +struct binrwir_execution_plan { + runtimec_allocator_t allocator; + binrwir_execution_plan_view_t view; + void *owned_storage; + size_t owned_storage_size; + size_t owned_storage_alignment; +}; + +#endif diff --git a/runtimec/binrwir/instruction.c b/runtimec/binrwir/instruction.c new file mode 100644 index 00000000..6692b076 --- /dev/null +++ b/runtimec/binrwir/instruction.c @@ -0,0 +1,70 @@ +/* + * 组件:binrwir/instruction 的实现 + * 作用:地址化指令构建请求校验与只读视图生成;当前明确返回未实现。 + */ + +#include "binrwir/instruction.h" + +#include + +static int binrwir_argument_span_is_invalid( + const binrwir_argument_span_t *arguments) { + size_t argument_ordinal; + + for (argument_ordinal = 0U; argument_ordinal < arguments->count; + ++argument_ordinal) { + if (arguments->data[argument_ordinal].argument_index == + BINRWIR_ARGUMENT_INDEX_NONE || + arguments->data[argument_ordinal].body_address == + BINRWIR_BODY_ADDRESS_NONE || + arguments->data[argument_ordinal].reserved_32_bits != 0U) { + return 1; + } + } + return 0; +} + +runtimec_status_t binrwir_instruction_validate_build_request( + const binrwir_instruction_build_request_t *request) { + if (request == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + if (request->operation_identifier == BINRWIR_OPERATION_IDENTIFIER_NONE || + request->read_arguments.count > UINT32_MAX || + request->write_arguments.count > UINT32_MAX || + request->read_arguments.count > + UINT32_MAX - request->write_arguments.count || + (request->read_arguments.count != 0U && + request->read_arguments.data == NULL) || + (request->write_arguments.count != 0U && + request->write_arguments.data == NULL) || + binrwir_argument_span_is_invalid(&request->read_arguments) || + binrwir_argument_span_is_invalid(&request->write_arguments)) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_instruction_get_view( + const binrwir_instruction_t *instruction, + uint32_t source_logical_program_counter, + binrwir_argument_span_t read_arguments, + binrwir_argument_span_t write_arguments, + binrwir_instruction_view_t *output_view) { + if (output_view != NULL) { + memset(output_view, 0, sizeof(*output_view)); + } + if (instruction == NULL || output_view == NULL || + instruction->operation_identifier == BINRWIR_OPERATION_IDENTIFIER_NONE || + read_arguments.count > UINT32_MAX || + write_arguments.count > UINT32_MAX || + read_arguments.count > UINT32_MAX - write_arguments.count || + (read_arguments.count != 0U && read_arguments.data == NULL) || + (write_arguments.count != 0U && write_arguments.data == NULL) || + binrwir_argument_span_is_invalid(&read_arguments) || + binrwir_argument_span_is_invalid(&write_arguments)) { + return RUNTIMEC_STATUS_INVALID; + } + (void)source_logical_program_counter; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir/instruction.h b/runtimec/binrwir/instruction.h new file mode 100644 index 00000000..72aba924 --- /dev/null +++ b/runtimec/binrwir/instruction.h @@ -0,0 +1,65 @@ +#ifndef BINRWIR_INSTRUCTION_H +#define BINRWIR_INSTRUCTION_H + +/* + * 组件:binrwir/instruction + * 作用:定义只含 opid 的 BinRWIR 二进制指令头,以及非二进制只读视图。 + * 联系:Program 私有存储负责 framing;vmbin 与 BinRWIR JIT 通过 view 消费。 + * 约束:二进制指令头不得携带 offset、count、key、类型、名称或值。 + */ + +#include +#include + +#include "binrwir/argument.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_OPERATION_IDENTIFIER_NONE UINT32_MAX + +typedef struct binrwir_instruction { + uint32_t operation_identifier; +} binrwir_instruction_t; + +typedef struct binrwir_argument_span { + const binrwir_argument_t *data; + size_t count; +} binrwir_argument_span_t; + +typedef struct binrwir_instruction_build_request { + uint32_t source_logical_program_counter; + uint32_t operation_identifier; + binrwir_argument_span_t read_arguments; + binrwir_argument_span_t write_arguments; +} binrwir_instruction_build_request_t; + +typedef struct binrwir_instruction_view { + const binrwir_instruction_t *instruction; + uint32_t source_logical_program_counter; + uint32_t reserved_32_bits; + binrwir_argument_span_t read_arguments; + binrwir_argument_span_t write_arguments; +} binrwir_instruction_view_t; + +runtimec_status_t binrwir_instruction_validate_build_request( + const binrwir_instruction_build_request_t *request); +runtimec_status_t binrwir_instruction_get_view( + const binrwir_instruction_t *instruction, + uint32_t source_logical_program_counter, + binrwir_argument_span_t read_arguments, + binrwir_argument_span_t write_arguments, + binrwir_instruction_view_t *output_view); + +_Static_assert(sizeof(binrwir_instruction_t) == 4U, + "binrwir instruction heads must contain only opid"); +_Static_assert(offsetof(binrwir_instruction_t, operation_identifier) == 0U, + "binrwir opid must begin each instruction"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/program.c b/runtimec/binrwir/program.c new file mode 100644 index 00000000..3dfde187 --- /dev/null +++ b/runtimec/binrwir/program.c @@ -0,0 +1,149 @@ +/* + * 组件:binrwir/program 的实现 + * 作用:地址化 Program 查询和构造入口;当前均明确返回未实现。 + * 约束:每个输出在失败前进入无效状态。 + */ + +#include "binrwir/program.h" + +#include + +static void binrwir_program_identity_invalidate( + binrwir_program_identity_t *identity) { + if (identity != NULL) { + memset(identity, 0, sizeof(*identity)); + identity->schema_version = UINT32_MAX; + identity->rwir_manifest_version = UINT32_MAX; + identity->binrwir_binary_interface_generation = UINT32_MAX; + identity->canonical_encoding_revision = UINT32_MAX; + } +} + +runtimec_status_t binrwir_program_get_identity( + const binrwir_program_t *program, + binrwir_program_identity_t *output_identity) { + binrwir_program_identity_invalidate(output_identity); + if (program == NULL || output_identity == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_validate(const binrwir_program_t *program) { + if (program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_get_view( + const binrwir_program_t *program, binrwir_program_view_t *output_view) { + if (output_view != NULL) { + memset(output_view, 0, sizeof(*output_view)); + binrwir_program_identity_invalidate(&output_view->identity); + } + if (program == NULL || output_view == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_get_instruction( + const binrwir_program_t *program, uint32_t instruction_identifier, + binrwir_instruction_view_t *output_instruction) { + if (output_instruction != NULL) { + memset(output_instruction, 0, sizeof(*output_instruction)); + } + if (program == NULL || instruction_identifier == BINRWIR_IDENTIFIER_NONE || + output_instruction == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_destroy(binrwir_program_t *program) { + if (program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_create( + const runtimec_allocator_t *allocator, + const binrwir_program_identity_t *identity, + binrwir_program_builder_t **output_builder) { + if (output_builder != NULL) { + *output_builder = NULL; + } + if (allocator == NULL || identity == NULL || output_builder == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_reset(binrwir_program_builder_t *builder) { + if (builder == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_destroy( + binrwir_program_builder_t *builder) { + if (builder == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_add_parameter( + binrwir_program_builder_t *builder, binrwir_parameter_direction_t direction, + uint32_t argument_index, uint32_t *output_parameter_index) { + if (output_parameter_index != NULL) { + *output_parameter_index = BINRWIR_ARGUMENT_INDEX_NONE; + } + if (builder == NULL || direction == BINRWIR_PARAMETER_DIRECTION_INVALID || + argument_index == BINRWIR_ARGUMENT_INDEX_NONE || + output_parameter_index == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_add_alias_rule( + binrwir_program_builder_t *builder, const binrwir_alias_rule_t *rule, + uint32_t *output_alias_rule_identifier) { + if (output_alias_rule_identifier != NULL) { + *output_alias_rule_identifier = BINRWIR_IDENTIFIER_NONE; + } + if (builder == NULL || rule == NULL || + output_alias_rule_identifier == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_add_instruction( + binrwir_program_builder_t *builder, + const binrwir_instruction_build_request_t *request, + uint32_t *output_instruction_identifier) { + if (output_instruction_identifier != NULL) { + *output_instruction_identifier = BINRWIR_IDENTIFIER_NONE; + } + if (builder == NULL || request == NULL || + output_instruction_identifier == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_program_builder_finalize( + binrwir_program_builder_t *builder, binrwir_program_t **output_program) { + if (output_program != NULL) { + *output_program = NULL; + } + if (builder == NULL || output_program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir/program.h b/runtimec/binrwir/program.h new file mode 100644 index 00000000..94202760 --- /dev/null +++ b/runtimec/binrwir/program.h @@ -0,0 +1,83 @@ +#ifndef BINRWIR_PROGRAM_H +#define BINRWIR_PROGRAM_H + +/* + * 组件:binrwir/program + * 作用:定义与 RWIR 语义等价的 canonical、不可变、地址化 BinRWIR Program。 + * 联系:vmkv 解析 RWIR 并构造 Program;BinRWIR Compiler 与 vmbin 只读消费。 + * 约束:公开二进制载荷只有 opid、argidx、argaddr。可变参数分区、source mapping + * 与存储 framing 是 Program 私有元数据,不进入 instruction,也不缓存 V.body。 + */ + +#include +#include + +#include "binrwir/argument.h" +#include "binrwir/instruction.h" +#include "binrwir/signature.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_IDENTIFIER_NONE UINT32_MAX + +typedef struct binrwir_program_identity { + uint64_t snapshot_generation; + uint64_t program_generation; + uint32_t schema_version; + uint32_t rwir_manifest_version; + uint32_t binrwir_binary_interface_generation; + uint32_t canonical_encoding_revision; +} binrwir_program_identity_t; + +typedef struct binrwir_program binrwir_program_t; +typedef struct binrwir_program_builder binrwir_program_builder_t; + +typedef struct binrwir_program_view { + binrwir_program_identity_t identity; + binrwir_signature_view_t signature; + /* 逐条内容只能经 get_instruction 取得,私有 framing 不对外暴露。 */ + size_t instruction_count; +} binrwir_program_view_t; + +runtimec_status_t binrwir_program_get_identity( + const binrwir_program_t *program, binrwir_program_identity_t *output_identity); +runtimec_status_t binrwir_program_validate(const binrwir_program_t *program); +runtimec_status_t binrwir_program_get_view(const binrwir_program_t *program, + binrwir_program_view_t *output_view); +runtimec_status_t binrwir_program_get_instruction( + const binrwir_program_t *program, uint32_t instruction_identifier, + binrwir_instruction_view_t *output_instruction); +runtimec_status_t binrwir_program_destroy(binrwir_program_t *program); + +runtimec_status_t binrwir_program_builder_create( + const runtimec_allocator_t *allocator, + const binrwir_program_identity_t *identity, + binrwir_program_builder_t **output_builder); +runtimec_status_t binrwir_program_builder_reset(binrwir_program_builder_t *builder); +runtimec_status_t binrwir_program_builder_destroy( + binrwir_program_builder_t *builder); +runtimec_status_t binrwir_program_builder_add_parameter( + binrwir_program_builder_t *builder, binrwir_parameter_direction_t direction, + uint32_t argument_index, uint32_t *output_parameter_index); +runtimec_status_t binrwir_program_builder_add_alias_rule( + binrwir_program_builder_t *builder, const binrwir_alias_rule_t *rule, + uint32_t *output_alias_rule_identifier); +runtimec_status_t binrwir_program_builder_add_instruction( + binrwir_program_builder_t *builder, + const binrwir_instruction_build_request_t *request, + uint32_t *output_instruction_identifier); +runtimec_status_t binrwir_program_builder_finalize( + binrwir_program_builder_t *builder, binrwir_program_t **output_program); + +_Static_assert(sizeof(binrwir_program_identity_t) == 32U, + "binrwir program identity must remain 32 bytes"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir/signature.c b/runtimec/binrwir/signature.c new file mode 100644 index 00000000..030848fc --- /dev/null +++ b/runtimec/binrwir/signature.c @@ -0,0 +1,40 @@ +/* + * 组件:binrwir/signature 的实现 + * 作用:签名校验与别名策略查询;当前明确返回未实现。 + * 约束:默认输出仅是无效前置状态,不表示已经证明不重叠。 + */ + +#include "binrwir/signature.h" + +runtimec_status_t +binrwir_signature_validate(const binrwir_signature_view_t *signature) { + if (signature == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + if (signature->read_parameter_count > UINT32_MAX || + signature->write_parameter_count > UINT32_MAX || + signature->alias_rule_count > UINT32_MAX || + (signature->read_parameter_count != 0U && + signature->read_parameter_argument_indices == NULL) || + (signature->write_parameter_count != 0U && + signature->write_parameter_argument_indices == NULL) || + (signature->alias_rule_count != 0U && + signature->alias_rules == NULL)) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_signature_get_alias_policy( + const binrwir_signature_view_t *signature, uint32_t read_parameter_index, + uint32_t write_parameter_index, binrwir_alias_policy_t *output_policy) { + if (output_policy != NULL) { + *output_policy = BINRWIR_ALIAS_POLICY_INVALID; + } + if (signature == NULL || output_policy == NULL || + read_parameter_index >= signature->read_parameter_count || + write_parameter_index >= signature->write_parameter_count) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir/signature.h b/runtimec/binrwir/signature.h new file mode 100644 index 00000000..d34a39ee --- /dev/null +++ b/runtimec/binrwir/signature.h @@ -0,0 +1,68 @@ +#ifndef BINRWIR_SIGNATURE_H +#define BINRWIR_SIGNATURE_H + +/* + * 组件:binrwir/signature + * 作用:定义读参数、写参数和跨方向显式别名规则。 + * 联系:vmkv 生成签名;BinRWIR Compiler 按同一签名校验参数别名关系。 + * 约束:签名是别名策略的唯一所有者;未知关系不得猜测为不重叠。 + */ + +#include +#include + +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_parameter_direction { + BINRWIR_PARAMETER_DIRECTION_INVALID = 0, + BINRWIR_PARAMETER_DIRECTION_READ = 1, + BINRWIR_PARAMETER_DIRECTION_WRITE = 2 +} binrwir_parameter_direction_t; + +typedef enum binrwir_alias_policy { + BINRWIR_ALIAS_POLICY_INVALID = 0, + BINRWIR_ALIAS_NO_ALIAS = 1, + BINRWIR_ALIAS_MAY_ALIAS = 2, + BINRWIR_ALIAS_MUST_ALIAS = 3 +} binrwir_alias_policy_t; + +typedef enum binrwir_alias_rule_flags { + BINRWIR_ALIAS_RULE_FLAG_NONE = 0, + BINRWIR_ALIAS_RULE_FLAG_PROJECTION = 1U << 0 +} binrwir_alias_rule_flags_t; + +typedef struct binrwir_alias_rule { + uint32_t read_parameter_index; + uint32_t write_parameter_index; + uint8_t policy; + uint8_t flags; + uint16_t reserved_16_bits; +} binrwir_alias_rule_t; + +typedef struct binrwir_signature_view { + const uint32_t *read_parameter_argument_indices; + size_t read_parameter_count; + const uint32_t *write_parameter_argument_indices; + size_t write_parameter_count; + const binrwir_alias_rule_t *alias_rules; + size_t alias_rule_count; +} binrwir_signature_view_t; + +runtimec_status_t +binrwir_signature_validate(const binrwir_signature_view_t *signature); +runtimec_status_t binrwir_signature_get_alias_policy( + const binrwir_signature_view_t *signature, uint32_t read_parameter_index, + uint32_t write_parameter_index, binrwir_alias_policy_t *output_policy); + +_Static_assert(sizeof(binrwir_alias_rule_t) == 12U, + "binrwir alias rules must remain 12 bytes"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/compiler.c b/runtimec/binrwir_compiler/compiler.c new file mode 100644 index 00000000..a5d86a3e --- /dev/null +++ b/runtimec/binrwir_compiler/compiler.c @@ -0,0 +1,113 @@ +/* + * BinRWIR Compiler 当前只提供已地址化 Program 的失败封闭桩。 + * 输出先进入无效状态;合法但尚未实现的请求返回 NOT_IMPLEMENTED。 + */ + +#include "binrwir_compiler/compiler.h" + +static void binrwir_compilation_reset_view( + binrwir_compilation_view_t *view) { + if (view != NULL) { + *view = (binrwir_compilation_view_t){ + .execution_target = BINRWIR_EXECUTION_TARGET_INVALID, + .canonical_program = NULL, + .execution_plan = NULL, + .binrwir_jit_code = NULL, + }; + } +} + +static bool binrwir_compiler_jit_configuration_is_valid( + const binrwir_compiler_configuration_t *configuration) { + if (configuration->enable_jit) { + return configuration->jit_machine_code_capacity > 0U && + configuration->jit_target.architecture != + BINRWIR_JIT_ARCHITECTURE_NONE && + runtimec_string_view_is_valid(configuration->jit_target.triple); + } + return configuration->jit_machine_code_capacity == 0U && + configuration->jit_target.architecture == + BINRWIR_JIT_ARCHITECTURE_NONE && + configuration->jit_target.triple.data == NULL && + configuration->jit_target.triple.size == 0U && + configuration->jit_target.feature_mask == 0U; +} + +runtimec_status_t binrwir_compiler_create( + const runtimec_allocator_t *allocator, + const binrwir_compiler_configuration_t *configuration, + binrwir_compiler_t **output_compiler, + runtimec_error_t *error) { + if (output_compiler != NULL) { + *output_compiler = NULL; + } + if (!runtimec_allocator_is_valid(allocator) || configuration == NULL || + output_compiler == NULL || + configuration->structure_size != sizeof(*configuration) || + configuration->compiler_revision == 0U || + !binrwir_compiler_jit_configuration_is_valid(configuration)) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_compiler_destroy( + binrwir_compiler_t *compiler, + runtimec_error_t *error) { + if (compiler == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_compiler_get_revision( + const binrwir_compiler_t *compiler, + uint32_t *output_compiler_revision) { + if (output_compiler_revision != NULL) { + *output_compiler_revision = UINT32_MAX; + } + if (compiler == NULL || output_compiler_revision == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_compiler_compile( + binrwir_compiler_t *compiler, + const binrwir_program_t *canonical_program, + binrwir_compilation_t **output_compilation, + runtimec_error_t *error) { + if (output_compilation != NULL) { + *output_compilation = NULL; + } + if (compiler == NULL || canonical_program == NULL || + output_compilation == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_compilation_get_view( + const binrwir_compilation_t *compilation, + binrwir_compilation_view_t *output_view, + runtimec_error_t *error) { + binrwir_compilation_reset_view(output_view); + if (compilation == NULL || output_view == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_compilation_destroy( + binrwir_compilation_t *compilation, + runtimec_error_t *error) { + if (compilation == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/compiler.h b/runtimec/binrwir_compiler/compiler.h new file mode 100644 index 00000000..4a1aa068 --- /dev/null +++ b/runtimec/binrwir_compiler/compiler.h @@ -0,0 +1,108 @@ +#ifndef RUNTIMEC_BINRWIR_COMPILER_COMPILER_H +#define RUNTIMEC_BINRWIR_COMPILER_COMPILER_H + +/* + * 组件:BinRWIR Compiler + * 作用:从 VMKV 已经地址化的 canonical BinRWIR Program 派生 execution plan, + * 并在启用时于同一编译阶段尝试生成 JIT 机器码。输出是统一的 opaque + * compilation。 + * 联系:唯一内容输入是带非零 argaddr 的 binrwir_program_t;编译阶段一次性 + * 选择 VMBIN 或 JIT。 + * 约束:不读取 RWIR 或 kvspace,不解析 key,不缓存 V.body,不执行 Program; + * 通用 plan 不重复编码 instruction、地址或寄存器状态。 + * JIT 不可编译只形成 VMBIN plan;真实编译错误不得转换成 VMBIN plan。 + * 执行阶段不得再次探测 JIT,也不得在 JIT 与 VMBIN 之间回退。 + */ + +#include +#include +#include + +#include "binrwir/execution_plan.h" +#include "binrwir/program.h" +#include "binrwir_compiler/jit/backend.h" +#include "binrwir_compiler/jit/code.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_compiler_t binrwir_compiler_t; +typedef struct binrwir_compilation_t binrwir_compilation_t; + +typedef enum binrwir_execution_target_t { + BINRWIR_EXECUTION_TARGET_INVALID = 0, + BINRWIR_EXECUTION_TARGET_VMBIN = 1, + BINRWIR_EXECUTION_TARGET_JIT = 2 +} binrwir_execution_target_t; + +/* + * BinRWIR Compiler 根据这些值在内部创建并独占 JIT compiler,调用者不得 + * 构造或注入 JIT compiler 对象。enable_jit 为 true 时 capacity 必须非零, + * target architecture 必须明确;为 false 时所有 JIT 字段必须保持零值。 + * create 必须复制 jit_target.triple 字节。configuration 只在调用期间有效。 + */ +typedef struct binrwir_compiler_configuration_t { + size_t structure_size; + uint32_t compiler_revision; + bool enable_jit; + size_t jit_machine_code_capacity; + binrwir_jit_target_t jit_target; +} binrwir_compiler_configuration_t; + +/* + * 本视图借用 compilation 持有的对象。compilation 只表示已经成功完成的编译。 + * JIT 目标必须携带非空 binrwir_jit_code;VMBIN 目标必须保持该字段为空。 + * VMBIN compilation 不携带 body cache;它只借用 Program 中已解析的 argaddr。 + * JIT 不可编译产生 VMBIN compilation;编译错误不产生 compilation。 + */ +typedef struct binrwir_compilation_view_t { + binrwir_execution_target_t execution_target; + const binrwir_program_t *canonical_program; + const binrwir_execution_plan_t *execution_plan; + const binrwir_jit_code_t *binrwir_jit_code; +} binrwir_compilation_view_t; + +runtimec_status_t binrwir_compiler_create( + const runtimec_allocator_t *allocator, + const binrwir_compiler_configuration_t *configuration, + binrwir_compiler_t **output_compiler, + runtimec_error_t *error); + +runtimec_status_t binrwir_compiler_destroy( + binrwir_compiler_t *compiler, + runtimec_error_t *error); + +runtimec_status_t binrwir_compiler_get_revision( + const binrwir_compiler_t *compiler, + uint32_t *output_compiler_revision); + +/* + * 唯一内容输入是 canonical_program。其全部 argument 必须已由 VMKV 解析为 + * 非零 V.body 地址。成功产生的 compilation 持有该 Program 的不可变副本、 + * execution plan 与可选机器码,但不拥有地址指向的 body。地址只在生成它的 + * snapshot/source revision 有效期间可用;该 revision 失效时必须使整份 + * compilation 失效。调用者仍拥有输入 Program。 + */ +runtimec_status_t binrwir_compiler_compile( + binrwir_compiler_t *compiler, + const binrwir_program_t *canonical_program, + binrwir_compilation_t **output_compilation, + runtimec_error_t *error); + +runtimec_status_t binrwir_compilation_get_view( + const binrwir_compilation_t *compilation, + binrwir_compilation_view_t *output_view, + runtimec_error_t *error); + +runtimec_status_t binrwir_compilation_destroy( + binrwir_compilation_t *compilation, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/backend.h b/runtimec/binrwir_compiler/jit/backend.h new file mode 100644 index 00000000..d638c359 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/backend.h @@ -0,0 +1,75 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_BACKEND_H +#define RUNTIMEC_BINRWIR_JIT_BACKEND_H + +/* + * 组件:binrwir_compiler/jit/backend + * 作用:单一 backend 契约:目标架构与 feature 判定及机器码生成。x86_64 与 + * AArch64 是并列 peer,谁也不是另一个的默认模板。 + * backend 只拥有指令编码、调用约定实现、既定 register allocation/spill、 + * completed-state commit、incomplete-state discard、常量池、branch fixup、 + * 可执行页切换与 instruction cache + * 同步;不拥有 Program 语义、eligibility 或 cache identity。 + * 联系:binrwir_compiler/jit/compiler 选择 backend;emit 请求来自 lower;输出由 + * code_memory 发布。backend 禁止读 RWIR、kvspace 或 route policy。 + * 约束:guard failure/error 路径不得写回 speculative/partial state。未知 + * processor 或缺 feature 必须返回 UNSUPPORTED,不得默认 x86_64。 + */ + +#include +#include +#include +#include "contract/status.h" +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_jit_architecture_t { + BINRWIR_JIT_ARCHITECTURE_NONE = 0, + BINRWIR_JIT_ARCHITECTURE_X86_64 = 1, + BINRWIR_JIT_ARCHITECTURE_AARCH64 = 2 +} binrwir_jit_architecture_t; +typedef struct binrwir_jit_target_t { + binrwir_jit_architecture_t architecture; + runtimec_string_view_t triple; + uint64_t feature_mask; +} binrwir_jit_target_t; + +struct binrwir_jit_lowered_program; +struct binrwir_jit_register_allocation; +struct binrwir_jit_safepoint_plan; + +typedef struct binrwir_jit_emit_request_t { + const struct binrwir_jit_lowered_program *lowered_program; + const struct binrwir_jit_register_allocation *register_allocation; + const struct binrwir_jit_safepoint_plan *safepoint_plan; + runtimec_bytes_buffer_t writable_code; +} binrwir_jit_emit_request_t; + +typedef struct binrwir_jit_emit_result_t { + size_t machine_code_size; + size_t entry_byte_offset; +} binrwir_jit_emit_result_t; +typedef struct binrwir_jit_backend_t { + binrwir_jit_architecture_t architecture; + runtimec_string_view_t name; + bool (*supports)(const binrwir_jit_target_t *target); + runtimec_status_t (*emit)( + const binrwir_jit_emit_request_t *request, + binrwir_jit_emit_result_t *output_result, + runtimec_error_t *error); +} binrwir_jit_backend_t; +runtimec_status_t binrwir_jit_backend_for_target( + const binrwir_jit_target_t *target, + const binrwir_jit_backend_t **output_backend); +runtimec_status_t binrwir_jit_x86_64_backend( + const binrwir_jit_backend_t **output_backend); +runtimec_status_t binrwir_jit_aarch64_backend( + const binrwir_jit_backend_t **output_backend); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/backend/aarch64.c b/runtimec/binrwir_compiler/jit/backend/aarch64.c new file mode 100644 index 00000000..498a79a4 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/backend/aarch64.c @@ -0,0 +1,20 @@ +/* + * 组件:binrwir_compiler/jit/backend/aarch64 + * 作用:AArch64 backend 入口;当前显式返回 UNSUPPORTED。它不是 x86 完成后的 + * 占位:必须真实实现同一 register allocation、completed-state commit 与 + * incomplete-state discard contract,否则不可编译。 + * 联系:与 x86_64 peer;只消费 lower/emit request。 + */ + +#include "binrwir_compiler/jit/backend.h" + +#include + +runtimec_status_t binrwir_jit_aarch64_backend( + const binrwir_jit_backend_t **output_backend) { + if (output_backend == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + *output_backend = NULL; + return RUNTIMEC_STATUS_UNSUPPORTED; +} diff --git a/runtimec/binrwir_compiler/jit/backend/backend.c b/runtimec/binrwir_compiler/jit/backend/backend.c new file mode 100644 index 00000000..25611a4f --- /dev/null +++ b/runtimec/binrwir_compiler/jit/backend/backend.c @@ -0,0 +1,22 @@ +/* + * 组件:binrwir_compiler/jit/backend/backend + * 作用:按目标选择 backend;当前显式返回 UNSUPPORTED。未知 processor + * 不得默认 x86_64。 + * 联系:binrwir_compiler/jit/compiler 调用;x86_64/aarch64 是并列 peer。 + */ + +#include "binrwir_compiler/jit/backend.h" + +#include + +runtimec_status_t binrwir_jit_backend_for_target( + const binrwir_jit_target_t *target, + const binrwir_jit_backend_t **output_backend) { + if (output_backend != NULL) { + *output_backend = NULL; + } + if (target == NULL || output_backend == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_UNSUPPORTED; +} diff --git a/runtimec/binrwir_compiler/jit/backend/x86_64.c b/runtimec/binrwir_compiler/jit/backend/x86_64.c new file mode 100644 index 00000000..828fa162 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/backend/x86_64.c @@ -0,0 +1,23 @@ +/* + * 组件:binrwir_compiler/jit/backend/x86_64 + * 作用:x86_64 backend 入口;当前显式返回 UNSUPPORTED。它与 AArch64 + * 是 peer 关系,只拥有 + * 目标机制(编码、调用约定、执行既定 register allocation、spill、 + * completed-state commit、incomplete-state discard、常量池、branch fixup、 + * 代码页切换与 instruction cache 同步),不得自行发明额外缓存位置, + * 也不得在 guard/error 路径写回 partial state。 + * 联系:binrwir_compiler/jit/compiler 通过 backend contract 选择;不得读 RWIR/kvspace。 + */ + +#include "binrwir_compiler/jit/backend.h" + +#include + +runtimec_status_t binrwir_jit_x86_64_backend( + const binrwir_jit_backend_t **output_backend) { + if (output_backend == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + *output_backend = NULL; + return RUNTIMEC_STATUS_UNSUPPORTED; +} diff --git a/runtimec/binrwir_compiler/jit/code.c b/runtimec/binrwir_compiler/jit/code.c new file mode 100644 index 00000000..dcb8e5a3 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/code.c @@ -0,0 +1,46 @@ +/* binrwir_compiler/jit/code 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/code.h" + +runtimec_status_t binrwir_jit_code_create( + binrwir_jit_code_memory_t *memory, + size_t machine_code_size, + size_t entry_byte_offset, + binrwir_jit_code_t **output_code, + runtimec_error_t *error) { + (void)error; + if (output_code != NULL) { + *output_code = NULL; + } + if (memory == NULL || machine_code_size == 0U || + entry_byte_offset >= machine_code_size || output_code == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_retain(binrwir_jit_code_t *code) { + if (code == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_release(binrwir_jit_code_t *code) { + if (code == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_get_machine_code_function( + const binrwir_jit_code_t *code, + binrwir_jit_machine_code_function_t *output_function) { + if (output_function != NULL) { + *output_function = NULL; + } + if (code == NULL || output_function == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/code.h b/runtimec/binrwir_compiler/jit/code.h new file mode 100644 index 00000000..9f73b9a3 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/code.h @@ -0,0 +1,43 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_CODE_H +#define RUNTIMEC_BINRWIR_JIT_CODE_H + +/* + * 组件:binrwir_compiler/jit/code + * 作用:已发布机器码的引用计数句柄:retain/release 保证执行期间 code + * 不被回收;entry 返回调用入口。 + * 联系:binrwir_jit 编译阶段产生;BinRWIR compilation 持有 code,compilation + * repository 的 lease 保证执行期间有效;最后一个引用释放后回收 + * 只读可执行页。 + * 约束:句柄不暴露机器地址给持久层;事务边界由 route 保证。 + */ + +#include + +#include "contract/snapshot.h" +#include "binrwir_compiler/jit/backend.h" +#include "binrwir_compiler/jit/code_memory.h" +#include "binrwir_compiler/jit/execution_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_jit_code binrwir_jit_code_t; +/* 成功时 code 接管已发布 memory;失败时 memory 仍归调用者。 */ +runtimec_status_t binrwir_jit_code_create( + binrwir_jit_code_memory_t *memory, + size_t machine_code_size, + size_t entry_byte_offset, + binrwir_jit_code_t **output_code, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_code_retain(binrwir_jit_code_t *code); +runtimec_status_t binrwir_jit_code_release(binrwir_jit_code_t *code); +runtimec_status_t binrwir_jit_code_get_machine_code_function( + const binrwir_jit_code_t *code, + binrwir_jit_machine_code_function_t *output_function); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/code_memory.c b/runtimec/binrwir_compiler/jit/code_memory.c new file mode 100644 index 00000000..98e42710 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/code_memory.c @@ -0,0 +1,49 @@ +/* binrwir_compiler/jit/code_memory 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/code_memory.h" + +runtimec_status_t binrwir_jit_code_memory_create( + size_t capacity, + binrwir_jit_code_memory_t **output_memory, + runtimec_error_t *error) { + (void)error; + if (output_memory != NULL) { + *output_memory = NULL; + } + if (capacity == 0U || output_memory == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_memory_writable_view( + binrwir_jit_code_memory_t *memory, + runtimec_bytes_buffer_t *output_buffer, + runtimec_error_t *error) { + (void)error; + if (output_buffer != NULL) { + runtimec_bytes_buffer_reset(output_buffer); + } + if (memory == NULL || output_buffer == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_memory_publish( + binrwir_jit_code_memory_t *memory, + size_t code_size, + runtimec_error_t *error) { + (void)error; + if (memory == NULL || code_size == 0U) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_code_memory_destroy(binrwir_jit_code_memory_t *memory) { + if (memory == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/code_memory.h b/runtimec/binrwir_compiler/jit/code_memory.h new file mode 100644 index 00000000..8b7dd62a --- /dev/null +++ b/runtimec/binrwir_compiler/jit/code_memory.h @@ -0,0 +1,42 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_CODE_MEMORY_H +#define RUNTIMEC_BINRWIR_JIT_CODE_MEMORY_H + +/* + * 组件:binrwir_compiler/jit/code_memory + * 作用:可执行页的生命周期:分配可写页 -> emit/relocate -> 切换可执行 + * 并取消可写 -> 平台 instruction cache 同步 -> 由 code lease 管理回收。 + * 内存不可同时可写与可执行。 + * 联系:backend 写入 writable_view;binrwir_jit compiler 调 publish;完成的 + * BinRWIR compilation 管理机器码生命周期。 + * 约束:发布失败或 entitlement 不足时返回错误,绝不执行未发布内存。 + */ + +#include + +#include "contract/status.h" +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_jit_code_memory binrwir_jit_code_memory_t; +runtimec_status_t binrwir_jit_code_memory_create( + size_t capacity, + binrwir_jit_code_memory_t **output_memory, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_code_memory_writable_view( + binrwir_jit_code_memory_t *memory, + runtimec_bytes_buffer_t *output_buffer, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_code_memory_publish( + binrwir_jit_code_memory_t *memory, + size_t code_size, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_code_memory_destroy(binrwir_jit_code_memory_t *memory); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/compiler.c b/runtimec/binrwir_compiler/jit/compiler.c new file mode 100644 index 00000000..985c1c7f --- /dev/null +++ b/runtimec/binrwir_compiler/jit/compiler.c @@ -0,0 +1,61 @@ +/* binrwir_compiler/jit/compiler 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/compiler.h" + +static void binrwir_jit_compilation_result_reset( + binrwir_jit_compilation_result_t *result, + binrwir_jit_compilation_outcome_t outcome, + binrwir_jit_refusal_reason_t refusal_reason) { + if (result != NULL) { + *result = (binrwir_jit_compilation_result_t){0}; + result->outcome = outcome; + result->refusal.reason = refusal_reason; + } +} + +runtimec_status_t binrwir_jit_compiler_create( + runtimec_allocator_t allocator, + const binrwir_jit_compiler_configuration_t *configuration, + binrwir_jit_compiler_t **output_compiler, + runtimec_error_t *error) { + (void)error; + if (output_compiler != NULL) { + *output_compiler = NULL; + } + if (!runtimec_allocator_is_valid(&allocator) || configuration == NULL || + configuration->structure_size != sizeof(*configuration) || + configuration->machine_code_capacity == 0U || + output_compiler == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_compiler_destroy(binrwir_jit_compiler_t *compiler) { + if (compiler == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_compile( + binrwir_jit_compiler_t *compiler, + const binrwir_program_t *program, + const binrwir_execution_plan_t *execution_plan, + binrwir_jit_compilation_result_t *output_result, + runtimec_error_t *error) { + (void)error; + binrwir_jit_compilation_result_reset( + output_result, + BINRWIR_JIT_COMPILATION_OUTCOME_INVALID, + BINRWIR_JIT_REFUSAL_INVALID_PROGRAM); + if (compiler == NULL || program == NULL || execution_plan == NULL || + output_result == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + binrwir_jit_compilation_result_reset( + output_result, + BINRWIR_JIT_COMPILATION_OUTCOME_ERROR, + BINRWIR_JIT_REFUSAL_NONE); + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/compiler.h b/runtimec/binrwir_compiler/jit/compiler.h new file mode 100644 index 00000000..8e14856d --- /dev/null +++ b/runtimec/binrwir_compiler/jit/compiler.h @@ -0,0 +1,69 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_COMPILER_H +#define RUNTIMEC_BINRWIR_JIT_COMPILER_H + +/* + * 组件:binrwir_compiler/jit/compiler + * 作用:binrwir_jit 机器码编译入口:消费 canonical Program 与顶层 BinRWIR + * Compiler 已生成的 execution plan,完成验证、lowering 与 backend 生成。 + * 联系:顶层 BinRWIR Compiler 在编译阶段调用;本组件先验证 canonical + * BinRWIR 与 execution plan,再生成机器码并返回编译判定。 + * 约束:compile 不读 RWIR/kvspace;只有 COMPILED 携带 code。内部阶段的 + * UNSUPPORTED 转成 SUCCESS + NOT_COMPILABLE;非法 BinRWIR、内存不足 + * 和内部错误返回原错误状态 + ERROR,不能建立可执行 code。 + */ + +#include +#include + +#include "contract/allocator.h" +#include "contract/status.h" +#include "binrwir/execution_plan.h" +#include "binrwir/program.h" +#include "binrwir_compiler/jit/backend.h" +#include "binrwir_compiler/jit/code.h" +#include "binrwir_compiler/jit/verifier.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_jit_compiler_t binrwir_jit_compiler_t; +typedef enum binrwir_jit_compilation_outcome_t { + BINRWIR_JIT_COMPILATION_OUTCOME_INVALID = 0, + BINRWIR_JIT_COMPILATION_OUTCOME_COMPILED = 1, + BINRWIR_JIT_COMPILATION_OUTCOME_NOT_COMPILABLE = 2, + BINRWIR_JIT_COMPILATION_OUTCOME_ERROR = 3 +} binrwir_jit_compilation_outcome_t; +typedef struct binrwir_jit_compilation_result_t { + binrwir_jit_compilation_outcome_t outcome; + /* 仅 COMPILED 可非 NULL;其它 outcome 必须保持 NULL。 */ + binrwir_jit_code_t *code; + binrwir_jit_refusal_t refusal; +} binrwir_jit_compilation_result_t; +typedef struct binrwir_jit_compiler_configuration_t { + uint32_t structure_size; + size_t machine_code_capacity; + uint64_t generation; + binrwir_jit_target_t target; +} binrwir_jit_compiler_configuration_t; + +/* create 必须复制 target.triple 字节;configuration 只在调用期间有效。 */ + +runtimec_status_t binrwir_jit_compiler_create( + runtimec_allocator_t allocator, + const binrwir_jit_compiler_configuration_t *configuration, + binrwir_jit_compiler_t **output_compiler, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_compiler_destroy(binrwir_jit_compiler_t *compiler); +runtimec_status_t binrwir_jit_compile( + binrwir_jit_compiler_t *compiler, + const binrwir_program_t *program, + const binrwir_execution_plan_t *execution_plan, + binrwir_jit_compilation_result_t *output_result, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/execution_interface.c b/runtimec/binrwir_compiler/jit/execution_interface.c new file mode 100644 index 00000000..4f396a5f --- /dev/null +++ b/runtimec/binrwir_compiler/jit/execution_interface.c @@ -0,0 +1,14 @@ +/* binrwir_compiler/jit/execution_interface 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/execution_interface.h" + +runtimec_status_t binrwir_jit_execution_interface( + binrwir_jit_execution_interface_t *output_interface) { + if (output_interface != NULL) { + *output_interface = (binrwir_jit_execution_interface_t){0}; + } + if (output_interface == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/execution_interface.h b/runtimec/binrwir_compiler/jit/execution_interface.h new file mode 100644 index 00000000..b03848f5 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/execution_interface.h @@ -0,0 +1,48 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_EXECUTION_INTERFACE_H +#define RUNTIMEC_BINRWIR_JIT_EXECUTION_INTERFACE_H + +/* + * 组件:binrwir_compiler/jit/execution_interface + * 作用:机器码入口与执行 binary interface 的最小描述:V.body 地址、参数序号、 + * Frame 对齐与 safepoint 合同版本。骨架期不冻结最终寄存器约定、 + * JIT frame/stack 布局或稳定机器 binary interface。 + * 联系:JIT compiler 与 backend 共用;BinRWIR argument 与 execution frame + * 合同是唯一布局来源,机器码生成器不得手写第二份 offset。 + * 约束:版本变化必须进入 compilation repository key;本文件不定义语言语义。 + */ + +#include + +#include "contract/status.h" +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BINRWIR_JIT_EXECUTION_BINARY_INTERFACE_MAJOR_VERSION 0u +#define BINRWIR_JIT_EXECUTION_BINARY_INTERFACE_MINOR_VERSION 3u + +typedef runtimec_status_t (*binrwir_jit_machine_code_function_t)( + void *execution_state, + binrwir_execution_frame_t *frame, + binrwir_execution_exit_t *exit); + +typedef struct binrwir_jit_execution_interface_t { + uint16_t binary_interface_major_version; + uint16_t binary_interface_minor_version; + uint16_t value_body_address_size_bytes; + uint16_t argument_index_size_bytes; + uint32_t frame_alignment_bytes; + uint32_t safepoint_contract_revision; +} binrwir_jit_execution_interface_t; + +runtimec_status_t binrwir_jit_execution_interface( + binrwir_jit_execution_interface_t *output_interface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/executor.c b/runtimec/binrwir_compiler/jit/executor.c new file mode 100644 index 00000000..aabb311e --- /dev/null +++ b/runtimec/binrwir_compiler/jit/executor.c @@ -0,0 +1,21 @@ +/* BinRWIR JIT 确定执行入口的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/executor.h" + +runtimec_status_t binrwir_jit_execute( + const binrwir_jit_code_t *code, + binrwir_execution_frame_t *frame, + binrwir_execution_exit_t *output_exit, + runtimec_error_t *error) { + (void)error; + if (output_exit != NULL) { + *output_exit = (binrwir_execution_exit_t){0}; + output_exit->kind = BINRWIR_EXECUTION_EXIT_ERROR; + output_exit->status = RUNTIMEC_STATUS_INVALID; + } + if (code == NULL || frame == NULL || output_exit == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + output_exit->status = RUNTIMEC_STATUS_NOT_IMPLEMENTED; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/executor.h b/runtimec/binrwir_compiler/jit/executor.h new file mode 100644 index 00000000..4b47c15a --- /dev/null +++ b/runtimec/binrwir_compiler/jit/executor.h @@ -0,0 +1,37 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_EXECUTOR_H +#define RUNTIMEC_BINRWIR_JIT_EXECUTOR_H + +/* + * 组件:binrwir_compiler/jit/executor + * 作用:执行 BinRWIR Compiler 已选定并生成的机器码。可编译性只在 compiler、 + * verifier 与 backend 阶段决定;执行入口没有拒绝、重编译或替代路径。 + * 联系:engine/route 只在 BinRWIR compilation 的不可变执行目标为 JIT 时调用; + * 输出为 BinRWIR execution exit,交给 route 提交。 + * 约束:JIT executor 不读 RWIR、不依赖 vmbin。机器码只可按 BinRWIR argaddr + * 读取或提交 KVspace V.body。每条完整 logical instruction 的 dirty state + * 必须先 commit,之后才可推进 logical program counter/checkpoint;当前 + * 指令的 speculative/partial state 在 guard failure/error 时必须 discard, + * 不得写回。recovery/resume 丢弃全部机器状态。非法机器码句柄、commit + * 失败或内部错误必须直接返回错误,不得改走 VMBIN。 + */ + +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" +#include "binrwir_compiler/jit/code.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +runtimec_status_t binrwir_jit_execute( + const binrwir_jit_code_t *code, + binrwir_execution_frame_t *frame, + binrwir_execution_exit_t *output_exit, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/lower.c b/runtimec/binrwir_compiler/jit/lower.c new file mode 100644 index 00000000..b5f3a77e --- /dev/null +++ b/runtimec/binrwir_compiler/jit/lower.c @@ -0,0 +1,27 @@ +/* binrwir_compiler/jit/lower 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/lower.h" + +runtimec_status_t binrwir_jit_lower( + const binrwir_jit_lowering_plan_t *plan, + const runtimec_allocator_t *allocator, + binrwir_jit_lowered_program_t **output_program, + runtimec_error_t *error) { + (void)error; + if (output_program != NULL) { + *output_program = NULL; + } + if (plan == NULL || !runtimec_allocator_is_valid(allocator) || + output_program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_lowered_program_destroy( + binrwir_jit_lowered_program_t *program) { + if (program == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/lower.h b/runtimec/binrwir_compiler/jit/lower.h new file mode 100644 index 00000000..5e2832bf --- /dev/null +++ b/runtimec/binrwir_compiler/jit/lower.h @@ -0,0 +1,37 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_LOWER_H +#define RUNTIMEC_BINRWIR_JIT_LOWER_H + +/* + * 组件:binrwir_compiler/jit/lower + * 作用:把目标相关 lowering plan 转成机器码生成请求,保留每个 register/spill + * 到 V.body 的来源映射、逐 logical instruction commit safepoint,以及 + * guard/error discard 路径,不重新解释 RWIR 语义。 + * 联系:plan 来自 binrwir_compiler/jit/lowering_plan;输出交给 backend 的 emit;lower 不接触 + * kvspace/host。 + * 约束:不得删除 instruction completion boundary,不得将 speculative/partial + * state 放入 commit 表,也不得产生无法恢复来源的临时值;具体 micro + * operation 编码当前未冻结。 + */ + +#include "contract/allocator.h" +#include "contract/status.h" +#include "binrwir_compiler/jit/lowering_plan.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_jit_lowered_program binrwir_jit_lowered_program_t; +runtimec_status_t binrwir_jit_lower( + const binrwir_jit_lowering_plan_t *plan, + const runtimec_allocator_t *allocator, + binrwir_jit_lowered_program_t **output_program, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_lowered_program_destroy( + binrwir_jit_lowered_program_t *program); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/lowering_plan.c b/runtimec/binrwir_compiler/jit/lowering_plan.c new file mode 100644 index 00000000..0d50decd --- /dev/null +++ b/runtimec/binrwir_compiler/jit/lowering_plan.c @@ -0,0 +1,63 @@ +/* binrwir_compiler/jit/lowering_plan 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/lowering_plan.h" + +runtimec_status_t binrwir_jit_lowering_plan_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_execution_plan_t *execution_plan, + const binrwir_jit_target_t *target, + const binrwir_jit_lowering_plan_options_t *options, + const runtimec_allocator_t *allocator, + binrwir_jit_lowering_plan_t **output_plan, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error) { + (void)error; + if (output_plan != NULL) { + *output_plan = NULL; + } + if (refusal != NULL) { + *refusal = (binrwir_jit_refusal_t){ + .reason = BINRWIR_JIT_REFUSAL_INVALID_PROGRAM, + }; + } + if (verified_program == NULL || verified_program->program == NULL || + execution_plan == NULL || target == NULL || options == NULL || + options->structure_size != sizeof(*options) || + !runtimec_allocator_is_valid(allocator) || output_plan == NULL || + refusal == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + refusal->reason = BINRWIR_JIT_REFUSAL_NONE; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_lowering_plan_destroy(binrwir_jit_lowering_plan_t *plan) { + if (plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_lowering_plan_get_register_allocation( + const binrwir_jit_lowering_plan_t *plan, + const binrwir_jit_register_allocation_t **output_allocation) { + if (output_allocation != NULL) { + *output_allocation = NULL; + } + if (plan == NULL || output_allocation == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_lowering_plan_get_safepoint_plan( + const binrwir_jit_lowering_plan_t *plan, + const binrwir_jit_safepoint_plan_t **output_safepoint_plan) { + if (output_safepoint_plan != NULL) { + *output_safepoint_plan = NULL; + } + if (plan == NULL || output_safepoint_plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/lowering_plan.h b/runtimec/binrwir_compiler/jit/lowering_plan.h new file mode 100644 index 00000000..97c2f953 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/lowering_plan.h @@ -0,0 +1,62 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_LOWERING_PLAN_H +#define RUNTIMEC_BINRWIR_JIT_LOWERING_PLAN_H + +/* + * 组件:binrwir_compiler/jit/lowering_plan + * 作用:把已验证 Program 与顶层 Compiler 的 execution plan 绑定为目标相关 + * lowering 输入,包括调用约定、JIT 专属 register allocation、spill、 + * safepoint materialization 和 source logical program counter 映射。 + * 联系:binrwir_jit compiler 构建,lower 消费;backend 只消费 emit request。 + * 约束:每条 logical instruction completion 必须生成不可关闭的 commit + * safepoint;guard failure/error 必须生成 discard 路径。不得重新优化 + * BinRWIR、改变 execution target 或建立执行期替代路径; + * 无法精确表达 execution plan 时必须在机器码生成前返回 + * RUNTIMEC_STATUS_UNSUPPORTED 并填写 refusal。顶层 binrwir_jit_compile 只把 + * UNSUPPORTED 映射为成功的 NOT_COMPILABLE,其它错误原样停止。 + */ + +#include +#include + +#include "binrwir/execution_plan.h" +#include "contract/allocator.h" +#include "contract/status.h" +#include "binrwir_compiler/jit/backend.h" +#include "binrwir_compiler/jit/register_allocation.h" +#include "binrwir_compiler/jit/safepoint.h" +#include "binrwir_compiler/jit/verifier.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct binrwir_jit_lowering_plan binrwir_jit_lowering_plan_t; + +typedef struct binrwir_jit_lowering_plan_options_t { + size_t structure_size; + uint32_t code_generation_revision; +} binrwir_jit_lowering_plan_options_t; + +runtimec_status_t binrwir_jit_lowering_plan_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_execution_plan_t *execution_plan, + const binrwir_jit_target_t *target, + const binrwir_jit_lowering_plan_options_t *options, + const runtimec_allocator_t *allocator, + binrwir_jit_lowering_plan_t **output_plan, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error); + +runtimec_status_t binrwir_jit_lowering_plan_destroy(binrwir_jit_lowering_plan_t *plan); +runtimec_status_t binrwir_jit_lowering_plan_get_register_allocation( + const binrwir_jit_lowering_plan_t *plan, + const binrwir_jit_register_allocation_t **output_allocation); +runtimec_status_t binrwir_jit_lowering_plan_get_safepoint_plan( + const binrwir_jit_lowering_plan_t *plan, + const binrwir_jit_safepoint_plan_t **output_safepoint_plan); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/register_allocation.c b/runtimec/binrwir_compiler/jit/register_allocation.c new file mode 100644 index 00000000..182f2ea1 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/register_allocation.c @@ -0,0 +1,50 @@ +/* JIT 专属寄存器分配的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/register_allocation.h" + +runtimec_status_t binrwir_jit_register_allocation_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_execution_plan_t *execution_plan, + const binrwir_jit_target_t *target, + const runtimec_allocator_t *allocator, + binrwir_jit_register_allocation_t **output_allocation, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error) { + (void)error; + if (output_allocation != NULL) { + *output_allocation = NULL; + } + if (refusal != NULL) { + *refusal = (binrwir_jit_refusal_t){ + .reason = BINRWIR_JIT_REFUSAL_INVALID_PROGRAM, + }; + } + if (verified_program == NULL || verified_program->program == NULL || + execution_plan == NULL || target == NULL || + !runtimec_allocator_is_valid(allocator) || output_allocation == NULL || + refusal == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + refusal->reason = BINRWIR_JIT_REFUSAL_NONE; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_register_allocation_get_view( + const binrwir_jit_register_allocation_t *allocation, + binrwir_jit_register_allocation_view_t *output_view) { + if (output_view != NULL) { + *output_view = (binrwir_jit_register_allocation_view_t){0}; + } + if (allocation == NULL || output_view == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_register_allocation_destroy( + binrwir_jit_register_allocation_t *allocation) { + if (allocation == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/register_allocation.h b/runtimec/binrwir_compiler/jit/register_allocation.h new file mode 100644 index 00000000..198b5fc1 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/register_allocation.h @@ -0,0 +1,105 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_REGISTER_ALLOCATION_H +#define RUNTIMEC_BINRWIR_JIT_REGISTER_ALLOCATION_H + +/* + * 组件:binrwir_compiler/jit/register_allocation + * 作用:JIT 独占的物理寄存器与 spill 分配合同。每个临时位置必须精确映射回 + * 一个非零 KVspace V.body 地址及其 RWIR 参数序号。 + * 联系:lowering_plan 创建分配;safepoint 依据分配生成写回表;backend 只能 + * 实现既定位置,不得建立无来源的值缓存。 + * 约束:BinRWIR 与 VMBIN 不得消费本合同。寄存器和 spill 都只是 V.body 的 + * 临时派生状态;只有完整完成 logical instruction 的 dirty state 可进入 + * commit 表。当前指令的 speculative/partial state 在 guard failure/error + * 时必须丢弃。恢复时全部丢弃。写别名命中读缓存时必须更新或失效, + * 禁止保留不可见的旧值。 + */ + +#include +#include + +#include "binrwir/execution_plan.h" +#include "binrwir_compiler/jit/backend.h" +#include "binrwir_compiler/jit/verifier.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_jit_location_kind { + BINRWIR_JIT_LOCATION_NONE = 0, + BINRWIR_JIT_LOCATION_PHYSICAL_REGISTER = 1, + BINRWIR_JIT_LOCATION_SPILL = 2 +} binrwir_jit_location_kind_t; + +typedef enum binrwir_jit_location_flags { + BINRWIR_JIT_LOCATION_FLAG_NONE = 0, + BINRWIR_JIT_LOCATION_FLAG_READ = 1U << 0, + BINRWIR_JIT_LOCATION_FLAG_WRITE = 1U << 1, + BINRWIR_JIT_LOCATION_FLAG_DIRTY = 1U << 2 +} binrwir_jit_location_flags_t; + +typedef struct binrwir_jit_body_origin { + uint64_t body_address; + uint32_t argument_index; + uint32_t reserved_32_bits; +} binrwir_jit_body_origin_t; + +typedef struct binrwir_jit_location_mapping { + binrwir_jit_body_origin_t body_origin; + uint32_t location_identifier; + uint16_t location_kind; + uint16_t flags; +} binrwir_jit_location_mapping_t; + +typedef enum binrwir_jit_write_alias_action { + BINRWIR_JIT_WRITE_ALIAS_ACTION_INVALID = 0, + BINRWIR_JIT_WRITE_ALIAS_ACTION_UPDATE_READ_LOCATION = 1, + BINRWIR_JIT_WRITE_ALIAS_ACTION_INVALIDATE_READ_LOCATION = 2 +} binrwir_jit_write_alias_action_t; + +typedef struct binrwir_jit_write_alias_mapping { + uint64_t body_address; + uint32_t read_argument_index; + uint32_t write_argument_index; + uint32_t read_location_identifier; + uint32_t write_location_identifier; + uint16_t action; + uint16_t reserved_16_bits; +} binrwir_jit_write_alias_mapping_t; + +typedef struct binrwir_jit_register_allocation + binrwir_jit_register_allocation_t; + +typedef struct binrwir_jit_register_allocation_view { + const binrwir_jit_location_mapping_t *location_mappings; + size_t location_mapping_count; + const binrwir_jit_write_alias_mapping_t *write_alias_mappings; + size_t write_alias_mapping_count; +} binrwir_jit_register_allocation_view_t; + +runtimec_status_t binrwir_jit_register_allocation_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_execution_plan_t *execution_plan, + const binrwir_jit_target_t *target, + const runtimec_allocator_t *allocator, + binrwir_jit_register_allocation_t **output_allocation, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_register_allocation_get_view( + const binrwir_jit_register_allocation_t *allocation, + binrwir_jit_register_allocation_view_t *output_view); +runtimec_status_t binrwir_jit_register_allocation_destroy( + binrwir_jit_register_allocation_t *allocation); + +_Static_assert(sizeof(binrwir_jit_body_origin_t) == 16U, + "JIT body origins must remain 16 bytes"); +_Static_assert(sizeof(binrwir_jit_location_mapping_t) == 24U, + "JIT location mappings must remain 24 bytes"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/safepoint.c b/runtimec/binrwir_compiler/jit/safepoint.c new file mode 100644 index 00000000..61d73cb3 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/safepoint.c @@ -0,0 +1,100 @@ +/* JIT safepoint materialization 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/safepoint.h" + +runtimec_status_t binrwir_jit_safepoint_plan_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_jit_register_allocation_t *register_allocation, + const runtimec_allocator_t *allocator, + binrwir_jit_safepoint_plan_t **output_plan, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error) { + (void)error; + if (output_plan != NULL) { + *output_plan = NULL; + } + if (refusal != NULL) { + *refusal = (binrwir_jit_refusal_t){ + .reason = BINRWIR_JIT_REFUSAL_INVALID_PROGRAM, + }; + } + if (verified_program == NULL || verified_program->program == NULL || + register_allocation == NULL || + !runtimec_allocator_is_valid(allocator) || output_plan == NULL || + refusal == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + refusal->reason = BINRWIR_JIT_REFUSAL_NONE; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_safepoint_plan_get_view( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_plan_view_t *output_view) { + if (output_view != NULL) { + *output_view = (binrwir_jit_safepoint_plan_view_t){0}; + } + if (plan == NULL || output_view == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +static int binrwir_jit_safepoint_is_commit_boundary( + binrwir_jit_safepoint_boundary_t boundary) { + return boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_PAUSE || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_HANDOFF || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_CALL || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_EXTERNAL_OBSERVATION; +} + +static int binrwir_jit_safepoint_is_discard_boundary( + binrwir_jit_safepoint_boundary_t boundary) { + return boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_RECOVERY || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_RESUME || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_GUARD_FAILURE || + boundary == BINRWIR_JIT_SAFEPOINT_BOUNDARY_ERROR; +} + +runtimec_status_t binrwir_jit_safepoint_commit_completed_state( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_boundary_t boundary, + uint32_t completed_logical_program_counter, + uint32_t next_logical_program_counter, + void *execution_state, + binrwir_execution_frame_t *frame, + runtimec_error_t *error) { + (void)next_logical_program_counter; + (void)error; + if (plan == NULL || !binrwir_jit_safepoint_is_commit_boundary(boundary) || + execution_state == NULL || frame == NULL || + frame->logical_program_counter != completed_logical_program_counter) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_safepoint_discard_incomplete_state( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_boundary_t boundary, + uint32_t current_logical_program_counter, + void *execution_state, + binrwir_execution_frame_t *frame, + runtimec_error_t *error) { + (void)error; + if (plan == NULL || !binrwir_jit_safepoint_is_discard_boundary(boundary) || + execution_state == NULL || frame == NULL || + frame->logical_program_counter != current_logical_program_counter) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t binrwir_jit_safepoint_plan_destroy( + binrwir_jit_safepoint_plan_t *plan) { + if (plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/safepoint.h b/runtimec/binrwir_compiler/jit/safepoint.h new file mode 100644 index 00000000..301401f8 --- /dev/null +++ b/runtimec/binrwir_compiler/jit/safepoint.h @@ -0,0 +1,117 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_SAFEPOINT_H +#define RUNTIMEC_BINRWIR_JIT_SAFEPOINT_H + +/* + * 组件:binrwir_compiler/jit/safepoint + * 作用:定义 JIT 已完成状态提交与未完成机器状态丢弃的恢复/可观察边界。 + * 联系:lowering_plan 以 register_allocation 的精确来源映射建立 safepoint; + * backend 发出写回序列;executor 只允许在写回完整成功后推进 Frame 的 + * logical program counter。 + * 约束:每条 logical instruction completion 都是强制边界。只有完整完成指令 + * 产生的 dirty state 可以提交到原 body_address;当前未完成指令的 + * speculative/partial register 或 spill 在 guard failure/error 时必须丢弃, + * 禁止 materialize。提交任一项失败时 logical program counter 与 checkpoint + * 均不得推进。recovery/resume 必须丢弃全部机器状态,只从 KVspace V.body + * 重建;执行错误不得转交 VMBIN 重试。 + */ + +#include +#include + +#include "binrwir/execution_frame.h" +#include "binrwir_compiler/jit/register_allocation.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_jit_safepoint_boundary { + BINRWIR_JIT_SAFEPOINT_BOUNDARY_INVALID = 0, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION = 1, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_PAUSE = 2, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_HANDOFF = 3, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_CALL = 4, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_EXTERNAL_OBSERVATION = 5, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_RECOVERY = 6, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_RESUME = 7, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_GUARD_FAILURE = 8, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_ERROR = 9 +} binrwir_jit_safepoint_boundary_t; + +typedef enum binrwir_jit_safepoint_action { + BINRWIR_JIT_SAFEPOINT_ACTION_INVALID = 0, + BINRWIR_JIT_SAFEPOINT_ACTION_COMMIT_COMPLETED_STATE = 1, + BINRWIR_JIT_SAFEPOINT_ACTION_DISCARD_INCOMPLETE_STATE = 2 +} binrwir_jit_safepoint_action_t; + +typedef enum binrwir_jit_materialization_flags { + BINRWIR_JIT_MATERIALIZATION_FLAG_NONE = 0, + BINRWIR_JIT_MATERIALIZATION_FLAG_COMPLETED_DIRTY = 1U << 0 +} binrwir_jit_materialization_flags_t; + +typedef struct binrwir_jit_materialization_entry { + binrwir_jit_body_origin_t body_origin; + uint32_t location_identifier; + uint16_t location_kind; + uint16_t flags; +} binrwir_jit_materialization_entry_t; + +typedef struct binrwir_jit_safepoint_descriptor { + uint32_t logical_program_counter; + /* discard action 的 entry count 必须为零,禁止写回未完成状态。 */ + uint32_t materialization_entry_offset; + uint32_t materialization_entry_count; + uint16_t boundary; + uint16_t action; +} binrwir_jit_safepoint_descriptor_t; + +typedef struct binrwir_jit_safepoint_plan binrwir_jit_safepoint_plan_t; + +typedef struct binrwir_jit_safepoint_plan_view { + const binrwir_jit_safepoint_descriptor_t *safepoints; + size_t safepoint_count; + /* 这里只允许完整完成 logical instruction 的 dirty state。 */ + const binrwir_jit_materialization_entry_t *materialization_entries; + size_t materialization_entry_count; +} binrwir_jit_safepoint_plan_view_t; + +runtimec_status_t binrwir_jit_safepoint_plan_create( + const binrwir_jit_verified_program_t *verified_program, + const binrwir_jit_register_allocation_t *register_allocation, + const runtimec_allocator_t *allocator, + binrwir_jit_safepoint_plan_t **output_plan, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_safepoint_plan_get_view( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_plan_view_t *output_view); +runtimec_status_t binrwir_jit_safepoint_commit_completed_state( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_boundary_t boundary, + uint32_t completed_logical_program_counter, + uint32_t next_logical_program_counter, + void *execution_state, + binrwir_execution_frame_t *frame, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_safepoint_discard_incomplete_state( + const binrwir_jit_safepoint_plan_t *plan, + binrwir_jit_safepoint_boundary_t boundary, + uint32_t current_logical_program_counter, + void *execution_state, + binrwir_execution_frame_t *frame, + runtimec_error_t *error); +runtimec_status_t binrwir_jit_safepoint_plan_destroy( + binrwir_jit_safepoint_plan_t *plan); + +_Static_assert(sizeof(binrwir_jit_materialization_entry_t) == 24U, + "JIT materialization entries must remain 24 bytes"); +_Static_assert(sizeof(binrwir_jit_safepoint_descriptor_t) == 16U, + "JIT safepoint descriptors must remain 16 bytes"); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/jit/verifier.c b/runtimec/binrwir_compiler/jit/verifier.c new file mode 100644 index 00000000..9d8b2b0d --- /dev/null +++ b/runtimec/binrwir_compiler/jit/verifier.c @@ -0,0 +1,26 @@ +/* binrwir_compiler/jit/verifier 的失败封闭骨架。 */ + +#include "binrwir_compiler/jit/verifier.h" + +runtimec_status_t binrwir_jit_verify_program( + const binrwir_program_t *program, + const binrwir_jit_target_t *target, + binrwir_jit_verified_program_t *output_verified_program, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error) { + (void)error; + if (output_verified_program != NULL) { + *output_verified_program = (binrwir_jit_verified_program_t){0}; + } + if (refusal != NULL) { + *refusal = (binrwir_jit_refusal_t){ + .reason = BINRWIR_JIT_REFUSAL_INVALID_PROGRAM, + }; + } + if (program == NULL || target == NULL || output_verified_program == NULL || + refusal == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + refusal->reason = BINRWIR_JIT_REFUSAL_NONE; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/jit/verifier.h b/runtimec/binrwir_compiler/jit/verifier.h new file mode 100644 index 00000000..065cd73d --- /dev/null +++ b/runtimec/binrwir_compiler/jit/verifier.h @@ -0,0 +1,58 @@ +#ifndef RUNTIMEC_BINRWIR_JIT_VERIFIER_H +#define RUNTIMEC_BINRWIR_JIT_VERIFIER_H + +/* + * 组件:binrwir_compiler/jit/verifier + * 作用:binrwir_jit eligibility 的 fail-closed 判定:先按中立 BinRWIR 合同验证 + * Program,再判定 operation/backend 支持、所有参数具有非零 V.body 地址、 + * 控制流 guard、binary interface 限制、side effect、call、builtin、 + * handoff、每条 instruction completion、observable boundary 与 source + * program counter map 可构建。guard failure/error 必须可区分未完成状态。 + * 寄存器压力只属于后续 register_allocation,不得进入中立 BinRWIR 合同。 + * 联系:binrwir_compiler/jit/compiler 在 plan 前调用;拒绝结果以结构化 refusal 返回, + * compiler 据此产生 NOT_COMPILABLE。 + * 约束:未知架构或不支持的 operation 返回 RUNTIMEC_STATUS_UNSUPPORTED 并 + * 填写 refusal;非法 BinRWIR 返回 INVALID。顶层 binrwir_jit_compile 只把 + * UNSUPPORTED 映射为成功的 NOT_COMPILABLE;其它错误不得转成拒绝。 + */ + +#include + +#include "contract/status.h" +#include "binrwir/program.h" +#include "binrwir_compiler/jit/backend.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum binrwir_jit_refusal_reason_t { + BINRWIR_JIT_REFUSAL_NONE = 0, + BINRWIR_JIT_REFUSAL_DISABLED = 1, + BINRWIR_JIT_REFUSAL_UNSUPPORTED_TARGET = 2, + BINRWIR_JIT_REFUSAL_UNSUPPORTED_PROGRAM = 3, + BINRWIR_JIT_REFUSAL_INVALID_PROGRAM = 4, + BINRWIR_JIT_REFUSAL_RESOURCE_LIMIT = 5 +} binrwir_jit_refusal_reason_t; +typedef struct binrwir_jit_refusal_t { + binrwir_jit_refusal_reason_t reason; + uint32_t logical_program_counter; + uint32_t detail_code; +} binrwir_jit_refusal_t; + +typedef struct binrwir_jit_verified_program_t { + const binrwir_program_t *program; +} binrwir_jit_verified_program_t; + +runtimec_status_t binrwir_jit_verify_program( + const binrwir_program_t *program, + const binrwir_jit_target_t *target, + binrwir_jit_verified_program_t *output_verified_program, + binrwir_jit_refusal_t *refusal, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/binrwir_compiler/optimization.c b/runtimec/binrwir_compiler/optimization.c new file mode 100644 index 00000000..12e75555 --- /dev/null +++ b/runtimec/binrwir_compiler/optimization.c @@ -0,0 +1,25 @@ +/* 已地址化 BinRWIR 通用派生优化的失败封闭骨架。 */ + +#include "binrwir_compiler/optimization.h" + +#include "binrwir/execution_plan_internal.h" + +runtimec_status_t binrwir_compiler_build_execution_plan( + const runtimec_allocator_t *allocator, + const binrwir_program_t *canonical_program, + uint64_t plan_generation, + uint32_t compiler_revision, + binrwir_execution_plan_t **output_execution_plan, + runtimec_error_t *error) { + (void)plan_generation; + (void)compiler_revision; + (void)error; + if (output_execution_plan != NULL) { + *output_execution_plan = NULL; + } + if (!runtimec_allocator_is_valid(allocator) || canonical_program == NULL || + output_execution_plan == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/binrwir_compiler/optimization.h b/runtimec/binrwir_compiler/optimization.h new file mode 100644 index 00000000..d99630dd --- /dev/null +++ b/runtimec/binrwir_compiler/optimization.h @@ -0,0 +1,38 @@ +#ifndef RUNTIMEC_BINRWIR_COMPILER_OPTIMIZATION_H +#define RUNTIMEC_BINRWIR_COMPILER_OPTIMIZATION_H + +/* + * 组件:BinRWIR Compiler optimization + * 作用:从已经地址化的 canonical BinRWIR Program 生成只读 execution plan, + * 只承载类型固化、dispatch 融合、控制流与 source logical program + * counter 映射。 + * 联系:仅由 binrwir_compiler/compiler.c 调用;binrwir_jit 与 vmbin 只消费完成的 plan。 + * 约束:不得改变 Program 语义、Read/Write authority、alias 顺序或 source + * logical program counter 观察边界,不得选择或执行目标。不得重复编码 + * instruction、解析或缓存 V.body 地址,也不得建立中间值存储模型。 + */ + +#include + +#include "binrwir/execution_plan.h" +#include "binrwir/program.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +runtimec_status_t binrwir_compiler_build_execution_plan( + const runtimec_allocator_t *allocator, + const binrwir_program_t *canonical_program, + uint64_t plan_generation, + uint32_t compiler_revision, + binrwir_execution_plan_t **output_execution_plan, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/allocator.c b/runtimec/contract/allocator.c new file mode 100644 index 00000000..a8b94312 --- /dev/null +++ b/runtimec/contract/allocator.c @@ -0,0 +1,45 @@ +/* + * 组件:contract/allocator 的实现 + * 作用:对显式分配器做参数校验后转发;不缓存、不统计、不分配自身状态。 + * 联系:host/vmbin/vmkv/BinRWIR Compiler/engine 的冷路径分配经这里;测试可注入 + * 计数或失败分配器,验证内存耗尽与失败封闭行为。 + */ + +#include "contract/allocator.h" + +static bool runtimec_alignment_is_valid(size_t alignment) { + return alignment != 0U && (alignment & (alignment - 1U)) == 0U; +} + +void runtimec_allocator_reset(runtimec_allocator_t *allocator) { + if (allocator != NULL) { + allocator->context = NULL; + allocator->allocate = NULL; + allocator->deallocate = NULL; + } +} + +bool runtimec_allocator_is_valid(const runtimec_allocator_t *allocator) { + return allocator != NULL && allocator->allocate != NULL && + allocator->deallocate != NULL; +} + +void *runtimec_allocator_allocate(const runtimec_allocator_t *allocator, + size_t size, size_t alignment) { + if (!runtimec_allocator_is_valid(allocator) || size == 0U || + !runtimec_alignment_is_valid(alignment)) { + return NULL; + } + return allocator->allocate(allocator->context, size, alignment); +} + +runtimec_status_t runtimec_allocator_deallocate( + const runtimec_allocator_t *allocator, void *allocation, + size_t size, size_t alignment) { + if (allocation == NULL || !runtimec_allocator_is_valid(allocator) || + size == 0U || !runtimec_alignment_is_valid(alignment)) { + return RUNTIMEC_STATUS_INVALID; + } + allocator->deallocate(allocator->context, allocation, size, alignment); + return RUNTIMEC_STATUS_SUCCESS; +} diff --git a/runtimec/contract/allocator.h b/runtimec/contract/allocator.h new file mode 100644 index 00000000..2ee68e39 --- /dev/null +++ b/runtimec/contract/allocator.h @@ -0,0 +1,53 @@ +#ifndef RUNTIMEC_CONTRACT_ALLOCATOR_H +#define RUNTIMEC_CONTRACT_ALLOCATOR_H + +/* + * 组件:contract/allocator + * 作用:所有冷路径动态内存的显式来源。指令执行热路径不得动态分配; + * 分配器不得藏进全局状态或线程局部存储。 + * 联系:contract 层只依赖自身;host、vmbin、vmkv、BinRWIR Compiler、engine 的 arena、 + * Program 存储、code memory 之外的冷分配都经本接口,便于测试注入、 + * 内存耗尽分类与 session 级生命周期。 + * 约束:分配/释放必须成对且 size/alignment 一致;alignment 为 2 的幂。 + */ + +#include +#include + +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*runtimec_allocate_function_t)(void *context, size_t size, + size_t alignment); +typedef void (*runtimec_deallocate_function_t)(void *context, + void *allocation, + size_t size, + size_t alignment); + +/* 显式分配器:context 由实现解释,两个函数指针必须同时存在。 */ +typedef struct runtimec_allocator { + void *context; + runtimec_allocate_function_t allocate; + runtimec_deallocate_function_t deallocate; +} runtimec_allocator_t; + +/* 置为空分配器(两个函数指针为 NULL);空分配器不是有效分配器。 */ +void runtimec_allocator_reset(runtimec_allocator_t *allocator); +/* 两个函数指针均非空才算有效。 */ +bool runtimec_allocator_is_valid(const runtimec_allocator_t *allocator); +/* size==0、alignment 非 2 的幂或分配器无效时返回 NULL,不调用实现。 */ +void *runtimec_allocator_allocate(const runtimec_allocator_t *allocator, + size_t size, size_t alignment); +/* 参数无效时返回 INVALID;仅在释放函数确实被调用后返回 SUCCESS。 */ +runtimec_status_t runtimec_allocator_deallocate( + const runtimec_allocator_t *allocator, void *allocation, + size_t size, size_t alignment); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/checkpoint.c b/runtimec/contract/checkpoint.c new file mode 100644 index 00000000..eb621406 --- /dev/null +++ b/runtimec/contract/checkpoint.c @@ -0,0 +1,38 @@ +/* + * 组件:contract/checkpoint 的实现 + * 作用:checkpoint 的空值构造与形状校验;不执行提交、不接触 kvspace。 + * 联系:engine/route 组装后交给 host 的 checkpoint 能力;host 只执行 + * 已组织好的提交,不自行决定 durable 状态。 + */ + +#include "contract/checkpoint.h" + +void runtimec_checkpoint_reset(runtimec_checkpoint_t *checkpoint) { + if (checkpoint != NULL) { + checkpoint->contract_version = runtimec_contract_version(); + runtimec_source_revision_reset(&checkpoint->source_revision); + checkpoint->sequence = 0U; + checkpoint->runtime_identifier = runtimec_string_view_make(NULL, 0U); + checkpoint->vthread_identifier = runtimec_string_view_make(NULL, 0U); + checkpoint->frame_identifier = runtimec_string_view_make(NULL, 0U); + checkpoint->logical_program_counter = + runtimec_string_view_make(NULL, 0U); + checkpoint->persistent_status = runtimec_string_view_make(NULL, 0U); + checkpoint->outcome = RUNTIMEC_STATUS_SUCCESS; + runtimec_error_reset(&checkpoint->error); + } +} + +bool runtimec_checkpoint_is_valid(const runtimec_checkpoint_t *checkpoint) { + return checkpoint != NULL && + checkpoint->contract_version.binary_interface == + RUNTIMEC_CONTRACT_BINARY_INTERFACE_VERSION && + runtimec_status_is_valid(checkpoint->outcome) && + runtimec_string_view_is_valid(checkpoint->runtime_identifier) && + runtimec_string_view_is_valid(checkpoint->vthread_identifier) && + runtimec_string_view_is_valid(checkpoint->frame_identifier) && + runtimec_string_view_is_valid( + checkpoint->logical_program_counter) && + runtimec_string_view_is_valid(checkpoint->persistent_status) && + runtimec_error_is_valid(&checkpoint->error); +} diff --git a/runtimec/contract/checkpoint.h b/runtimec/contract/checkpoint.h new file mode 100644 index 00000000..c78c835b --- /dev/null +++ b/runtimec/contract/checkpoint.h @@ -0,0 +1,52 @@ +#ifndef RUNTIMEC_CONTRACT_CHECKPOINT_H +#define RUNTIMEC_CONTRACT_CHECKPOINT_H + +/* + * 组件:contract/checkpoint + * 作用:一次 durable 提交所需的逻辑身份与状态:契约版本、source + * revision、单调 sequence、runtime/vthread/frame 逻辑标识符、逻辑 + * 程序计数器、durable status、outcome 与错误。checkpoint 只携带 + * 持久逻辑身份;禁止写入进程指针、机器返回地址、intern 标识符或 + * 机器码地址。 + * 联系:contract 只依赖自身;engine/route 在每条成功指令后组织 + * checkpoint,经 host port 的 checkpoint 函数提交;host 适配层负责 + * 把它映射到 kvspace 的程序计数器、状态和错误槽。vmbin/BinRWIR JIT + * 不直接提交,只把结构化 outcome 交给 route。 + * 约束:所有字符串字段都是 borrowed view;序列号由调用方单调递增。 + */ + +#include +#include + +#include "contract/error.h" +#include "contract/snapshot.h" +#include "contract/status.h" +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_checkpoint { + runtimec_version_t contract_version; + runtimec_source_revision_t source_revision; /* 提交所对应的 source 世代 */ + uint64_t sequence; /* 单调提交序号,用于幂等/对账 */ + runtimec_string_view_t runtime_identifier; + runtimec_string_view_t vthread_identifier; + runtimec_string_view_t frame_identifier; + runtimec_string_view_t logical_program_counter; + runtimec_string_view_t persistent_status; /* running/paused/waiting/done/error */ + runtimec_status_t outcome; /* 本次提交的组件结果 */ + runtimec_error_t error; /* 失败时的错误详情 */ +} runtimec_checkpoint_t; + +/* 构造空 checkpoint:版本置当前值、视图置空、结果为成功、错误清空。 */ +void runtimec_checkpoint_reset(runtimec_checkpoint_t *checkpoint); +/* 形状校验:二进制接口版本、结果枚举、全部视图与错误自洽。 */ +bool runtimec_checkpoint_is_valid(const runtimec_checkpoint_t *checkpoint); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/error.c b/runtimec/contract/error.c new file mode 100644 index 00000000..03889105 --- /dev/null +++ b/runtimec/contract/error.c @@ -0,0 +1,43 @@ +/* + * 组件:contract/error 的实现 + * 作用:错误值的重置、写入与自洽校验;不分配、不读外部状态。 + * 联系:host 适配层把 kvspace 错误翻译成本域错误,vmkv/vmbin/engine 只 + * 透传或补充逻辑程序计数器,不得抹去原错误分类。 + */ + +#include "contract/error.h" + +void runtimec_error_reset(runtimec_error_t *error) { + if (error != NULL) { + error->status = RUNTIMEC_STATUS_SUCCESS; + error->domain = RUNTIMEC_ERROR_DOMAIN_NONE; + error->code = 0; + error->message = runtimec_string_view_make(NULL, 0U); + error->logical_program_counter = runtimec_string_view_make(NULL, 0U); + } +} + +void runtimec_error_set(runtimec_error_t *error, runtimec_status_t status, + runtimec_error_domain_t domain, int64_t code, + runtimec_string_view_t message, + runtimec_string_view_t logical_program_counter) { + if (error != NULL) { + error->status = status; + error->domain = domain; + error->code = code; + error->message = message; + error->logical_program_counter = logical_program_counter; + } +} + +bool runtimec_error_is_valid(const runtimec_error_t *error) { + return error != NULL && runtimec_status_is_valid(error->status) && + error->domain >= RUNTIMEC_ERROR_DOMAIN_NONE && + error->domain <= RUNTIMEC_ERROR_DOMAIN_EXTENSION && + runtimec_string_view_is_valid(error->message) && + runtimec_string_view_is_valid(error->logical_program_counter); +} + +bool runtimec_error_is_set(const runtimec_error_t *error) { + return error != NULL && error->status != RUNTIMEC_STATUS_SUCCESS; +} diff --git a/runtimec/contract/error.h b/runtimec/contract/error.h new file mode 100644 index 00000000..49548f89 --- /dev/null +++ b/runtimec/contract/error.h @@ -0,0 +1,61 @@ +#ifndef RUNTIMEC_CONTRACT_ERROR_H +#define RUNTIMEC_CONTRACT_ERROR_H + +/* + * 组件:contract/error + * 作用:跨组件统一的错误形状:component status + 稳定错误域 + 稳定 code + + * 借用消息视图 + 逻辑程序计数器。执行或编译错误必须保留稳定分类 + * 与对应的 source 程序计数器。 + * 联系:contract 层只依赖自身;host 用 HOST/STORAGE/CHECKPOINT/EXTENSION + * 域包装 kvspace/扩展错误,vmkv(编译诊断)、vmbin(执行错误)、 + * engine/route(提交错误)都通过本结构把错误交给上层。 + * 注意:message 与 logical_program_counter 都是借用视图,生命周期归调用链上的 + * 缓冲区所有;不得在这里分配或释放。 + */ + +#include +#include + +#include "contract/status.h" +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 错误来源域;跨域报告便于上层区分契约错误、宿主错误与扩展错误。 */ +typedef enum runtimec_error_domain { + RUNTIMEC_ERROR_DOMAIN_NONE = 0, + RUNTIMEC_ERROR_DOMAIN_CONTRACT = 1, + RUNTIMEC_ERROR_DOMAIN_HOST = 2, + RUNTIMEC_ERROR_DOMAIN_STORAGE = 3, + RUNTIMEC_ERROR_DOMAIN_CHECKPOINT = 4, + RUNTIMEC_ERROR_DOMAIN_EXTENSION = 5 +} runtimec_error_domain_t; + +/* 跨组件错误值;所有字段都是普通数据或借用视图,可安全拷贝。 */ +struct runtimec_error { + runtimec_status_t status; /* 组件状态,见 contract/status.h */ + runtimec_error_domain_t domain; /* 错误来源域 */ + int64_t code; /* 稳定错误码,禁止用 -1 复用多义 */ + runtimec_string_view_t message; /* borrowed UTF-8 消息 */ + runtimec_string_view_t logical_program_counter; +}; + +/* 置为空错误(SUCCESS/NONE/0/空视图);不得依赖调用方预清零。 */ +void runtimec_error_reset(runtimec_error_t *error); +/* 写入一条错误;视图不复制,调用方须保证其生命周期覆盖本次调用链。 */ +void runtimec_error_set(runtimec_error_t *error, runtimec_status_t status, + runtimec_error_domain_t domain, int64_t code, + runtimec_string_view_t message, + runtimec_string_view_t logical_program_counter); +/* 结构是否自洽(状态枚举、域范围、视图有效性)。 */ +bool runtimec_error_is_valid(const runtimec_error_t *error); +/* 是否携带非 SUCCESS 状态。 */ +bool runtimec_error_is_set(const runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/host_services.c b/runtimec/contract/host_services.c new file mode 100644 index 00000000..62f89996 --- /dev/null +++ b/runtimec/contract/host_services.c @@ -0,0 +1,55 @@ +/* host 能力表的纯生命周期与形状校验。 */ + +#include "contract/host_services.h" + +void runtimec_host_value_header_reset(runtimec_host_value_header_t *header) { + if (header != NULL) { + header->value_size = 0U; + header->value_revision = 0U; + header->flags = 0U; + header->reserved = 0U; + } +} + +void runtimec_handoff_request_reset(runtimec_handoff_request_t *request) { + if (request != NULL) { + request->operation = runtimec_string_view_make(NULL, 0U); + runtimec_checkpoint_reset(&request->checkpoint); + } +} + +bool runtimec_handoff_request_is_valid( + const runtimec_handoff_request_t *request) { + return request != NULL && request->operation.size != 0U && + runtimec_string_view_is_valid(request->operation) && + runtimec_checkpoint_is_valid(&request->checkpoint); +} + +void runtimec_host_services_initialize(runtimec_host_services_t *services) { + if (services != NULL) { + services->binary_interface_version = + RUNTIMEC_HOST_SERVICES_BINARY_INTERFACE_VERSION; + services->context = NULL; + runtimec_allocator_reset(&services->allocator); + services->read = NULL; + services->read_header = NULL; + services->write = NULL; + services->write_partial = NULL; + services->remove = NULL; + services->list = NULL; + services->watch = NULL; + services->signal = NULL; + services->source_revision = NULL; + services->resolve_static_xvalue_address = NULL; + services->checkpoint = NULL; + services->handoff = NULL; + } +} + +bool runtimec_host_services_is_valid( + const runtimec_host_services_t *services) { + return services != NULL && + services->binary_interface_version == + RUNTIMEC_HOST_SERVICES_BINARY_INTERFACE_VERSION && + runtimec_allocator_is_valid(&services->allocator); +} diff --git a/runtimec/contract/host_services.h b/runtimec/contract/host_services.h new file mode 100644 index 00000000..5719a223 --- /dev/null +++ b/runtimec/contract/host_services.h @@ -0,0 +1,156 @@ +#ifndef RUNTIMEC_CONTRACT_HOST_SERVICES_H +#define RUNTIMEC_CONTRACT_HOST_SERVICES_H + +/* + * host 向 engine、vmkv 与 vmbin 提供的唯一外部能力表。能力表只暴露 + * kvspace 操作,不暴露 kvspace 结构。静态 key 可被解析为生命周期受 snapshot + * 约束的 XValue 基址,V.body 地址只能由固定头长度安全推导;动态 key 不在当前 + * binary interface 中。缺失能力必须返回 NOT_IMPLEMENTED;二进制接口版本必须 + * 精确一致,不提供旧布局兼容路径。 + */ + +#include +#include +#include + +#include "kvspace/xvalue_head.h" + +#include "contract/allocator.h" +#include "contract/checkpoint.h" +#include "contract/error.h" +#include "contract/snapshot.h" +#include "contract/status.h" +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUNTIMEC_HOST_SERVICES_BINARY_INTERFACE_VERSION 3U +#define RUNTIMEC_XVALUE_ADDRESS_NONE UINT64_C(0) +#define RUNTIMEC_XVALUE_BODY_OFFSET_BYTES KVSPACE_XVALUE_HEAD_LEN +#define RUNTIMEC_VALUE_BODY_ADDRESS_NONE UINT64_C(0) + +typedef uint64_t runtimec_xvalue_address_t; +typedef uint64_t runtimec_value_body_address_t; + +typedef struct runtimec_host_value_header { + uint64_t value_size; + uint64_t value_revision; + uint32_t flags; + uint32_t reserved; +} runtimec_host_value_header_t; + +typedef runtimec_status_t (*runtimec_host_list_visitor_function_t)( + void *context, runtimec_key_view_t key, runtimec_error_t *error); + +typedef struct runtimec_handoff_request { + runtimec_string_view_t operation; + runtimec_checkpoint_t checkpoint; +} runtimec_handoff_request_t; + +typedef runtimec_status_t (*runtimec_host_read_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, runtimec_bytes_buffer_t *output, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_read_header_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, runtimec_host_value_header_t *output, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_write_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, runtimec_bytes_view_t value, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_write_partial_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, uint64_t offset, + runtimec_bytes_view_t value, runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_remove_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_list_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t prefix, + runtimec_host_list_visitor_function_t visitor, + void *visitor_context, runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_watch_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, runtimec_bytes_view_t expected, + uint64_t timeout_nanoseconds, runtimec_bytes_buffer_t *observed, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_signal_function_t)( + void *context, runtimec_key_view_t key, runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_source_revision_function_t)( + void *context, runtimec_key_view_t function_key, + runtimec_source_revision_t *revision, runtimec_error_t *error); +/* + * 只解析静态 key。SUCCESS 必须返回非零的 XValue 进程内基址,且 host 必须 + * 保证该地址在 snapshot.source_revision 失效前保持有效和不变。动态 key、 + * 尚未分配的 key,或无法提供上述生命周期保证的 key 必须返回 UNSUPPORTED, + * 并把 output_xvalue_address 保持为 RUNTIMEC_XVALUE_ADDRESS_NONE。 + */ +typedef runtimec_status_t +(*runtimec_host_resolve_static_xvalue_address_function_t)( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, + runtimec_xvalue_address_t *output_xvalue_address, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_checkpoint_function_t)( + void *context, const runtimec_checkpoint_t *checkpoint, + runtimec_error_t *error); +typedef runtimec_status_t (*runtimec_host_handoff_function_t)( + void *context, const runtimec_handoff_request_t *request, + runtimec_error_t *error); + +typedef struct runtimec_host_services { + uint32_t binary_interface_version; + void *context; + runtimec_allocator_t allocator; + runtimec_host_read_function_t read; + runtimec_host_read_header_function_t read_header; + runtimec_host_write_function_t write; + runtimec_host_write_partial_function_t write_partial; + runtimec_host_remove_function_t remove; + runtimec_host_list_function_t list; + runtimec_host_watch_function_t watch; + runtimec_host_signal_function_t signal; + runtimec_host_source_revision_function_t source_revision; + runtimec_host_resolve_static_xvalue_address_function_t + resolve_static_xvalue_address; + runtimec_host_checkpoint_function_t checkpoint; + runtimec_host_handoff_function_t handoff; +} runtimec_host_services_t; + +void runtimec_host_value_header_reset(runtimec_host_value_header_t *header); +void runtimec_handoff_request_reset(runtimec_handoff_request_t *request); +bool runtimec_handoff_request_is_valid( + const runtimec_handoff_request_t *request); +void runtimec_host_services_initialize(runtimec_host_services_t *services); +bool runtimec_host_services_is_valid( + const runtimec_host_services_t *services); + +#ifdef __cplusplus +static_assert(KVSPACE_XVALUE_HEAD_LEN == 8U, + "KVspace XValue heads must remain 8 bytes"); +static_assert(RUNTIMEC_XVALUE_BODY_OFFSET_BYTES == 8U, + "V.body must remain 8 bytes after the XValue base"); +static_assert(sizeof(runtimec_xvalue_address_t) == 8U, + "XValue addresses must remain 64 bits"); +static_assert(sizeof(runtimec_value_body_address_t) == 8U, + "V.body addresses must remain 64 bits"); +#else +_Static_assert(KVSPACE_XVALUE_HEAD_LEN == 8U, + "KVspace XValue heads must remain 8 bytes"); +_Static_assert(RUNTIMEC_XVALUE_BODY_OFFSET_BYTES == 8U, + "V.body must remain 8 bytes after the XValue base"); +_Static_assert(sizeof(runtimec_xvalue_address_t) == 8U, + "XValue addresses must remain 64 bits"); +_Static_assert(sizeof(runtimec_value_body_address_t) == 8U, + "V.body addresses must remain 64 bits"); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/snapshot.c b/runtimec/contract/snapshot.c new file mode 100644 index 00000000..0aa179ad --- /dev/null +++ b/runtimec/contract/snapshot.c @@ -0,0 +1,47 @@ +/* + * 组件:contract/snapshot 的实现 + * 作用:版本、source revision 与快照的构造/重置/形状校验;纯值操作。 + * 联系:engine 在取 Program 前构造快照;host 提供 source_revision; + * vmkv 在编译前后校验同一快照,拒绝事实变化期间产生的 Program; + * compilation repository key 也引用它。 + */ + +#include "contract/snapshot.h" + +runtimec_version_t runtimec_contract_version(void) { + runtimec_version_t version; + version.major = (uint16_t)RUNTIMEC_CONTRACT_VERSION_MAJOR; + version.minor = (uint16_t)RUNTIMEC_CONTRACT_VERSION_MINOR; + version.patch = (uint16_t)RUNTIMEC_CONTRACT_VERSION_PATCH; + version.binary_interface = + (uint16_t)RUNTIMEC_CONTRACT_BINARY_INTERFACE_VERSION; + return version; +} + +void runtimec_source_revision_reset(runtimec_source_revision_t *revision) { + if (revision != NULL) { + revision->instance_identifier = 0U; + revision->generation = 0U; + revision->schema_version = 0U; + revision->instruction_set_version = 0U; + } +} + +void runtimec_snapshot_reset(runtimec_snapshot_t *snapshot) { + if (snapshot != NULL) { + snapshot->contract_version = runtimec_contract_version(); + runtimec_source_revision_reset(&snapshot->source_revision); + snapshot->function_key = runtimec_key_view_make(NULL, 0U); + snapshot->logical_program_counter = + runtimec_string_view_make(NULL, 0U); + } +} + +bool runtimec_snapshot_is_valid(const runtimec_snapshot_t *snapshot) { + return snapshot != NULL && + snapshot->contract_version.binary_interface == + RUNTIMEC_CONTRACT_BINARY_INTERFACE_VERSION && + runtimec_key_view_is_valid(snapshot->function_key) && + runtimec_string_view_is_valid( + snapshot->logical_program_counter); +} diff --git a/runtimec/contract/snapshot.h b/runtimec/contract/snapshot.h new file mode 100644 index 00000000..7e74f228 --- /dev/null +++ b/runtimec/contract/snapshot.h @@ -0,0 +1,67 @@ +#ifndef RUNTIMEC_CONTRACT_SNAPSHOT_H +#define RUNTIMEC_CONTRACT_SNAPSHOT_H + +/* + * 组件:contract/snapshot + * 作用:一次编译/执行所依据的权威快照:契约版本、store 实例、source + * generation、schema/指令集版本、函数键与逻辑程序计数器。kvspace + * 明文是唯一持久语义事实源;snapshot 只用于判断派生状态是否仍对应 + * 当前事实,不能替代或修改事实源。 + * 联系:contract 层只依赖自身;host 提供原始事实,engine 在编译/执行前 + * 取快照,vmkv/vmbin/BinRWIR Compiler 只消费快照与 generation,不自己读 + * kvspace;engine 用本结构构造 compilation repository key。 + * 约束:snapshot 不携带进程地址或 kvspace 内部句柄。 + */ + +#include +#include + +#include "contract/view.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUNTIMEC_CONTRACT_VERSION_MAJOR 1U +#define RUNTIMEC_CONTRACT_VERSION_MINOR 0U +#define RUNTIMEC_CONTRACT_VERSION_PATCH 0U +#define RUNTIMEC_CONTRACT_BINARY_INTERFACE_VERSION 1U + +/* 契约二进制接口版本;跨进程或跨库边界时用于拒绝不一致的快照。 */ +typedef struct runtimec_version { + uint16_t major; + uint16_t minor; + uint16_t patch; + uint16_t binary_interface; +} runtimec_version_t; + +/* 权威 source 的世代:store 实例 + generation + schema/指令集版本。 + * 任一变化都必须让派生 Program、view、JIT plan 与机器码失效。 */ +typedef struct runtimec_source_revision { + uint64_t instance_identifier; + uint64_t generation; + uint32_t schema_version; + uint32_t instruction_set_version; +} runtimec_source_revision_t; + +/* 一次编译/执行的权威身份;逻辑位置与函数键均为借用视图。 */ +typedef struct runtimec_snapshot { + runtimec_version_t contract_version; + runtimec_source_revision_t source_revision; + runtimec_key_view_t function_key; + runtimec_string_view_t logical_program_counter; +} runtimec_snapshot_t; + +runtimec_version_t runtimec_contract_version(void); +/* 归零为「未知来源」;不是有效 source revision,但可作为默认构造值。 */ +void runtimec_source_revision_reset(runtimec_source_revision_t *revision); +/* 置为空快照(版本 + 空视图);不分配。 */ +void runtimec_snapshot_reset(runtimec_snapshot_t *snapshot); +/* 形状校验:二进制接口版本匹配且两个视图有效;不查询外部事实。 */ +bool runtimec_snapshot_is_valid(const runtimec_snapshot_t *snapshot); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/status.c b/runtimec/contract/status.c new file mode 100644 index 00000000..8ac1a5a6 --- /dev/null +++ b/runtimec/contract/status.c @@ -0,0 +1,47 @@ +/* + * 组件:contract/status 的实现 + * 作用:只做枚举范围、失败分类与诊断名的纯函数判断;不读 kvspace、不分配。 + * 联系:被 host/vmbin/vmkv/BinRWIR Compiler/engine/interface 共同调用,是组件边界的 + * 唯一状态词汇表。 + */ + +#include "contract/status.h" + +bool runtimec_status_is_valid(runtimec_status_t status) { + return status >= RUNTIMEC_STATUS_SUCCESS && + status <= RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY; +} + +bool runtimec_status_is_failure(runtimec_status_t status) { + return status == RUNTIMEC_STATUS_UNSUPPORTED || + status == RUNTIMEC_STATUS_NOT_IMPLEMENTED || + status == RUNTIMEC_STATUS_INVALID || + status == RUNTIMEC_STATUS_OUT_OF_MEMORY || + status == RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY || + status == RUNTIMEC_STATUS_ERROR; +} + +const char *runtimec_status_name(runtimec_status_t status) { + switch (status) { + case RUNTIMEC_STATUS_SUCCESS: + return "success"; + case RUNTIMEC_STATUS_YIELD: + return "yield"; + case RUNTIMEC_STATUS_WAIT: + return "wait"; + case RUNTIMEC_STATUS_UNSUPPORTED: + return "unsupported"; + case RUNTIMEC_STATUS_NOT_IMPLEMENTED: + return "not_implemented"; + case RUNTIMEC_STATUS_INVALID: + return "invalid"; + case RUNTIMEC_STATUS_OUT_OF_MEMORY: + return "out_of_memory"; + case RUNTIMEC_STATUS_ERROR: + return "error"; + case RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY: + return "insufficient_capacity"; + default: + return "invalid_status"; + } +} diff --git a/runtimec/contract/status.h b/runtimec/contract/status.h new file mode 100644 index 00000000..44c5a2a3 --- /dev/null +++ b/runtimec/contract/status.h @@ -0,0 +1,39 @@ +#ifndef RUNTIMEC_CONTRACT_STATUS_H +#define RUNTIMEC_CONTRACT_STATUS_H + +/* + * 组件:contract/status + * 作用:声明跨组件统一的一次调用结果辅助函数。状态枚举定义在唯一公共头 + * kvlang/runtimec.h;SUCCESS 只能表示请求真实完成。 + * 联系:host、vmbin、vmkv、BinRWIR Compiler 与 engine 共用同一状态词汇。 + * 它不是三类状态中的另外两类: + * - execution exit(binrwir/execution_exit.h); + * - durable status(host/engine 通过 checkpoint 提交的 vthread 状态)。 + * 三者的转换只能由 engine/route.c 与 interface/public_interface.c + * 显式完成,其他组件不得猜测或改写状态类别。 + * 约束:失败路径必须把输出参数清零或恢复到文档化状态;不得用同一个 -1 + * 同时表示 unsupported、yield、invalid 与 error。 + */ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_error runtimec_error_t; + +/* 枚举值是否落在已定义范围内(用于校验跨组件边界传入的状态)。 */ +bool runtimec_status_is_valid(runtimec_status_t status); +/* 是否为失败类;YIELD 与 WAIT 是结构化控制流。 */ +bool runtimec_status_is_failure(runtimec_status_t status); +/* 稳定诊断名;未定义值返回 "invalid_status",不返回 NULL。 */ +const char *runtimec_status_name(runtimec_status_t status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/contract/view.c b/runtimec/contract/view.c new file mode 100644 index 00000000..afba9e0c --- /dev/null +++ b/runtimec/contract/view.c @@ -0,0 +1,62 @@ +/* + * 组件:contract/view 的实现 + * 作用:视图/缓冲的零分配构造与形状校验。 + * 联系:被 host 与 vmbin/vmkv/BinRWIR Compiler 的公共契约共同使用;本文件不 + * 依赖任何上层组件,也不触碰 kvspace。 + */ + +#include "contract/view.h" + +#include + +runtimec_bytes_view_t runtimec_bytes_view_make(const void *data, size_t size) { + runtimec_bytes_view_t view; + view.data = (const uint8_t *)data; + view.size = size; + return view; +} + +runtimec_string_view_t runtimec_string_view_make(const char *data, size_t size) { + runtimec_string_view_t view; + view.data = data; + view.size = size; + return view; +} + +runtimec_string_view_t runtimec_string_view_from_c_string(const char *value) { + return runtimec_string_view_make(value, value == NULL ? 0U : strlen(value)); +} + +runtimec_key_view_t runtimec_key_view_make(const char *data, size_t size) { + runtimec_key_view_t view; + view.data = data; + view.size = size; + return view; +} + +runtimec_key_view_t runtimec_key_view_from_c_string(const char *value) { + return runtimec_key_view_make(value, value == NULL ? 0U : strlen(value)); +} + +void runtimec_bytes_buffer_reset(runtimec_bytes_buffer_t *buffer) { + if (buffer != NULL) { + buffer->size = 0U; + } +} + +bool runtimec_bytes_view_is_valid(runtimec_bytes_view_t view) { + return view.size == 0U || view.data != NULL; +} + +bool runtimec_bytes_buffer_is_valid(const runtimec_bytes_buffer_t *buffer) { + return buffer != NULL && buffer->size <= buffer->capacity && + (buffer->capacity == 0U || buffer->data != NULL); +} + +bool runtimec_string_view_is_valid(runtimec_string_view_t view) { + return view.size == 0U || view.data != NULL; +} + +bool runtimec_key_view_is_valid(runtimec_key_view_t view) { + return view.size == 0U || view.data != NULL; +} diff --git a/runtimec/contract/view.h b/runtimec/contract/view.h new file mode 100644 index 00000000..8afc985e --- /dev/null +++ b/runtimec/contract/view.h @@ -0,0 +1,73 @@ +#ifndef RUNTIMEC_CONTRACT_VIEW_H +#define RUNTIMEC_CONTRACT_VIEW_H + +/* + * 组件:contract/view + * 作用:跨组件只读视图与可变字节缓冲的普通数据定义。视图只借用调用方 + * 内存,不拥有、不释放;缓冲的 data/capacity 也由调用方管理, + * 任何函数都不得通过视图隐式分配。 + * 联系:contract 层只依赖自身;host port 的入参与 vmbin/vmkv/BinRWIR Compiler 的 + * 字符串/路径/字节输入都使用这些视图,避免在执行组件边界传递 owning + * std::string 或 kvspace 类型。 + * 约束:size 为 0 时 data 可以为 NULL;size 非 0 时 data 必须非 NULL。 + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUNTIMEC_BYTES_VIEW_EMPTY { NULL, 0U } +#define RUNTIMEC_STRING_VIEW_EMPTY { NULL, 0U } +#define RUNTIMEC_KEY_VIEW_EMPTY { NULL, 0U } +#define RUNTIMEC_STRING_VIEW_LITERAL(value) { (value), sizeof(value) - 1U } +#define RUNTIMEC_KEY_VIEW_LITERAL(value) { (value), sizeof(value) - 1U } + +/* 只读字节视图;不拥有内存。 */ +typedef struct runtimec_bytes_view { + const uint8_t *data; + size_t size; +} runtimec_bytes_view_t; + +/* 调用方提供的可写字节缓冲;size 为已用长度,capacity 为容量。 */ +typedef struct runtimec_bytes_buffer { + uint8_t *data; + size_t size; + size_t capacity; +} runtimec_bytes_buffer_t; + +/* 只读字符串视图;不保证 NUL 结尾,长度以 size 为准。 */ +typedef struct runtimec_string_view { + const char *data; + size_t size; +} runtimec_string_view_t; + +/* kvspace 键视图;与字符串视图同形,语义上表示路径/函数键。 */ +typedef struct runtimec_key_view { + const char *data; + size_t size; +} runtimec_key_view_t; + +/* 由裸指针与长度构造视图;不做拷贝、不做 NUL 检查。 */ +runtimec_bytes_view_t runtimec_bytes_view_make(const void *data, size_t size); +runtimec_string_view_t runtimec_string_view_make(const char *data, size_t size); +/* 由 NUL 结尾字符串构造视图;NULL 视为空视图。 */ +runtimec_string_view_t runtimec_string_view_from_c_string(const char *value); +runtimec_key_view_t runtimec_key_view_make(const char *data, size_t size); +runtimec_key_view_t runtimec_key_view_from_c_string(const char *value); +/* 只把已用长度归零,不释放 data、不改变 capacity。 */ +void runtimec_bytes_buffer_reset(runtimec_bytes_buffer_t *buffer); +/* 以下校验只检查形状(size/data 一致性),不检查内存可读性。 */ +bool runtimec_bytes_view_is_valid(runtimec_bytes_view_t view); +bool runtimec_bytes_buffer_is_valid(const runtimec_bytes_buffer_t *buffer); +bool runtimec_string_view_is_valid(runtimec_string_view_t view); +bool runtimec_key_view_is_valid(runtimec_key_view_t view); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/documentation/architecture/README.md b/runtimec/documentation/architecture/README.md new file mode 100644 index 00000000..bc4aa495 --- /dev/null +++ b/runtimec/documentation/architecture/README.md @@ -0,0 +1,18 @@ +# runtimec architecture documents + +本目录只描述 runtimec 的唯一 BinRWIR Compiler 主干。`runtimec/README.md` 是总纲, +本目录把静态 key 到 XValue 基址、vmkv 唯一执行 `argaddr = xvalue_address + 8`、组件 +所有权、`opid + argidx + argaddr` 编码、编译生命周期、每条 logical +instruction 的强制完成边界、完成提交/未完成丢弃和 Read/Write 地址别名展开为可审查文档。 + +JIT 仅是 `binrwir_compiler/jit/` 的内部机器码实现,不是独立 VM 或并列顶层组件。 + +- `runtimec-architecture.md`:组件所有权、compilation 生命周期、状态与依赖规则。 +- `capability-map.md`:每项 capability 的唯一所有者与禁止职责。 +- `runtimec-trunk.mmd`:从明文 RWIR、XValue 基址解析、固定 `+8` 派生到完成提交或未完成丢弃的完整主干。 +- `runtimec-execution.mmd`:XValue 基址验证、VMKV 地址派生、编译、执行与 JIT completion/discard 时序。 +- `runtimec-dependencies.mmd`:允许依赖和明确禁止的反向依赖。 +- `rwir-binrwir-aliasing.mmd`:同地址 Read/Write 别名在 completed commit 与 incomplete discard 中的行为。 + +四张 `.mmd` 是图形事实源;同名 `.png` 与 `.svg` 必须由当前图源同步生成,禁止保留与 +图源不一致的旧渲染物。 diff --git a/runtimec/documentation/architecture/capability-map.md b/runtimec/documentation/architecture/capability-map.md new file mode 100644 index 00000000..1d33efa6 --- /dev/null +++ b/runtimec/documentation/architecture/capability-map.md @@ -0,0 +1,56 @@ +# runtimec Capability Ownership + +每项 capability 只有一个最终所有者。共享合同可以被多个组件读取,但任何组件都不能 +复制所有权、建立第二执行路径或在执行期改变 Compiler 的决定。 + +| Capability | Final owner | Required direction | +|---|---|---| +| Public create, execute, destroy | `interface/` | 只接受与返回结构化公共类型 | +| Status and error vocabulary | `include/kvlang/runtimec.h`, `contract/` | `RUNTIMEC_STATUS_SUCCESS` 是唯一正常调用状态 | +| Five-language behavior decisions | `specification/five-language-behavior-matrix.md` | 先裁决行为,再进入 operation manifest | +| myrwircap capability records | `specification/rwir_catalog.def` | 每个 capability 恰好一条 canonical 映射 | +| Stable operation identity | `specification/` | 显式 identifier;不得使用数组下标或声明顺序 | +| Plaintext RWIR reading and validation | `vmkv/` | 从 kvspace 读取 operation name、参数类型与参数名 | +| Static key dereference | `host/` | 参数 key 只解引用为非零、生命周期有效的 64 位 XValue 基址;不得决定 body 偏移 | +| XValue body address derivation | `vmkv/` | 唯一验证固定 8 字节 head ABI、检查 `+8` 溢出并生成最终 `argaddr` | +| Canonical BinRWIR Program | `binrwir/program.*` | 进程内、不可变、地址失效时整体失效 | +| Hot instruction identity | `binrwir/instruction.*` | 每条 instruction 只保留稳定 `opid` | +| Addressed arguments | `binrwir/argument.*` | 每个参数只保留 `argidx + uint64 argaddr`,完整且不截断 | +| Signature and alias policy | `binrwir/signature.*` | `(opid, argidx)` 声明类型、方向与权限;相同 `argaddr` 证明实际 alias | +| Shared execution frame and exit | `binrwir/execution_frame.*`, `binrwir/execution_exit.h` | vmbin 与内部 JIT executor 使用同一结构合同 | +| Execution plan representation | `binrwir/execution_plan.*` | 不含 body cache 或通用寄存器分配的可删除派生物 | +| Plan generation and target decision | `binrwir_compiler/` | 编译阶段一次决定 JIT 或 VMBIN | +| Unified compilation | `binrwir_compiler/` | 同时持有 Program、plan、target 与可选机器码 | +| Compilation cache and leases | `engine/compilation_repository.h` | repository 只缓存已完成 compilation | +| Execution dispatch and commit ordering | `engine/route.c` | 只在 completed state 全部写回后推进 logical program counter/checkpoint | +| Address-driven baseline execution | `vmbin/` | 直接读写 `argaddr` 指向的 body;不拥有 value cache 或 register file | +| Machine-code eligibility | `binrwir_compiler/jit/verifier.h` | 编译阶段产生 COMPILED、NOT_COMPILABLE 或 ERROR 判定 | +| Target-neutral lowering | `binrwir_compiler/jit/lower.h` | 只消费已验证 BinRWIR 与 execution plan | +| x86_64 and AArch64 generation | `binrwir_compiler/jit/backend/` | 同一个 backend 合同的并列实现 | +| Mandatory instruction-completion boundary | `binrwir_compiler/jit/safepoint.*` | 每条 logical instruction completion 都是不可关闭的 recovery/observable boundary | +| Completed-state materialization | `binrwir_compiler/jit/safepoint.*` | 只把完整完成指令的全部 dirty state 提交到原 `argaddr` | +| Incomplete-state discard | `binrwir_compiler/jit/safepoint.*` | guard failure、error、recovery/resume 丢弃当前 speculative register/spill,不写回 | +| JIT register allocation | `binrwir_compiler/jit/register_allocation.*` | 唯一可在当前逻辑指令内部提升 body 到 register/spill 的组件 | +| Machine-code execution | `binrwir_jit_execute` | 只接受已生成 code;不能编译、改选目标、提交未完成状态或执行 fallback | +| Dynamic key policy | `specification/`, `vmkv/` | 生命周期 ABI 建立前 map/struct 动态 key 明确 `UNSUPPORTED` | +| kvspace access and checkpoint storage | `host/` | 只通过窄主机服务合同访问;静态 key 查询不返回任意 body 地址 | +| Exported symbol set | `interface/exports.txt` | 每个平台必须精确匹配 | + +## Rejected ownership + +- BinRWIR 不属于 vmbin、内部 JIT 或 vmkv。 +- BinRWIR 不拥有 RWIR 明文字段、key、xvalue、`V.body` 或 register file。 +- vmkv 不拥有优化、execution plan、target decision 或 execution。 +- host 不拥有 XValue head 大小或 body 偏移策略;`+8` 只属于 vmkv。 +- Compiler 不读取 RWIR,不访问 kvspace,不执行 Program。 +- repository 不编译、不调用执行器、不改变 target。 +- route 不做 JIT eligibility,不实现 operation 语义。 +- JIT 只属于 `binrwir_compiler/` 内部,不拥有语言语义,不依赖 vmbin,不在执行时改变目标。 +- JIT 不得关闭 instruction-completion boundary,不得 materialize speculative/partial state。 +- 未完成状态 discard 后不得推进 logical program counter 或 checkpoint。 +- vmbin 不拥有 canonical BinRWIR,不选择 JIT,不缓存 xvalue/body,也不建立 software register file。 +- 零 XValue 基址、`+8` 溢出、非 8 字节 head ABI、动态 key 或无法证明生命周期不得通过 + 间接查找或 JIT 特例进入可执行 compilation。 +- host 不检查 BinRWIR instruction。 +- 公共头不暴露内部 runtimec 结构。 +- 任何组件都不能创建第三执行器或第二公开执行入口。 diff --git a/runtimec/documentation/architecture/runtimec-architecture.md b/runtimec/documentation/architecture/runtimec-architecture.md new file mode 100644 index 00000000..6bed35c0 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-architecture.md @@ -0,0 +1,193 @@ +# runtimec BinRWIR Compiler Architecture + +runtimec 只允许一条从 RWIR specification 到 execution 的主干。任何组件都不能建立旁路、 +第二次目标判定或第三执行器。 + +## Unique trunk + +```text +myrwircaps + plaintext RWIR in kvspace + -> vmkv validates RWIR and requests each static key + -> host dereferences static keys to nonzero uint64 XValue base addresses + -> vmkv validates the fixed 8-byte XValue head and derives argaddr = xvalue_address + 8 + -> vmkv emits addressed canonical BinRWIR + -> binrwir_compiler builds execution plan and attempts internal JIT compilation + -> compile-time target decision: JIT code or VMBIN plan + -> compilation repository caches the complete compilation + -> engine route reads the decided target before execution + -> binrwir_jit_execute or vmbin_execute + -> route commits complete effects and checkpoint +``` + +这条顺序同时是职责边界:vmkv 之前是语言事实,vmkv 输出是中立 canonical Program, +Compiler 输出是可执行 compilation,route 只消费已完成的 compilation。 + +## Ownership + +| Component | Owns | Must not own | +|---|---|---| +| `specification/` | 五语言行为决策、RWIR operation facts、myrwircap 到 canonical BinRWIR 的唯一映射 | 执行策略、缓存或机器码 | +| `vmkv/` | 明文 RWIR 读取、验证、`opid`/`argidx` 映射、XValue 基址资格验证、固定 8 字节 head 的唯一 `+8` 派生、`argaddr` 生成 | 动态 key fallback、任意 body 偏移、优化、execution plan、执行目标或机器码 | +| `binrwir/` | 进程内 canonical Program、`opid`、`argidx + uint64 argaddr`、signature、execution frame、execution exit、execution plan 合同 | RWIR 明文字段、V.body 副本、register file、vmkv/vmbin/JIT 策略 | +| `binrwir_compiler/` | execution plan 生成、内部 JIT compilation 请求、统一 compilation 与目标判定 | RWIR 解析、执行或 checkpoint 提交 | +| `binrwir_compiler/jit/` | eligibility、lowering、register/spill、强制 instruction-completion safepoint、机器码生命周期与执行 | 未完成状态写回、独立 VM 身份、RWIR、vmbin 依赖、语言语义或执行期目标选择 | +| `engine/compilation_repository` | compilation 所有权、索引、租约与失效 | 编译、执行或目标改选 | +| `engine/route` | repository 查询、未命中编译、执行前目标读取、唯一执行调度、effect 与 checkpoint 提交 | operation 语义或编译 eligibility | +| `vmbin/` | 通过 `argaddr` 直接读写 kvspace body 的基线执行 | xvalue/value cache、register file、BinRWIR 所有权、JIT 选择或持久事实 | +| `host/` | kvspace 与外部能力适配、静态 key 到 64 位 XValue 基址的解引用 | body 偏移决定、`+8` 派生、BinRWIR 检查或执行策略 | +| `interface/` | 公共请求翻译与精确导出 | 内部结构、语言语义或执行策略 | + +## Specification mapping + +RWIR 是 kvspace 中的明文可恢复格式:调用保存 operation name,参数保存类型、名称和 +Read/Write 方向。每个 myrwircap 必须恰好拥有一个 canonical BinRWIR operation 映射。 +映射记录显式保存规范 operation name、稳定 `opid`、每个参数的稳定 `argidx`、Read/Write +参数结构和行为决策引用。 + +operation identifier 是 specification 数据,不是 `myrwircaps` 数组下标,也不能由文件 +顺序、声明顺序或生成顺序计算。重新排序或插入记录不能改变既有 identity。缺失记录、 +同一 capability 的重复记录、identifier 重复、签名不一致或歧义都使 compilation 立即 +失败。vmkv 不提供默认 operation,也不把不完整映射变成可执行 Program。 + +## Canonical BinRWIR + +BinRWIR 是顶层中立模块,不属于 vmbin。它是从当前进程 kvspace 派生的二进制执行格式, +不能持久化,也不能在地址生命周期失效后继续使用。每条热 instruction 只保存: + +```text +opid + argidx + uint64 argaddr + argidx + uint64 argaddr + ... +``` + +`opid` 替代 RWIR operation name。`argidx` 在 canonical signature 中恢复参数顺序、类型和 +Read/Write 权限。参数 key 解引用得到 64 位 XValue 基址,不是任意 body 地址;XValue head +ABI 固定为 8 字节,只有 vmkv 验证该 ABI、检查溢出并执行 +`argaddr = xvalue_address + 8`,所以 `argaddr` 直接指向 kvspace `V.body`。BinRWIR 不复制 +operation name、参数类型、参数名、key 文本、XValue 或 body。RWIR 可以具有不固定参数 +数量;确定调用必须保存全部参数对,二进制编码不能改变参数顺序或观察边界。 + +BinRWIR 保存的唯一寻址信息是 vmkv 派生的最终 64 位 `argaddr`,不缓存 XValue 基址、 +XValue 或 `V.body`;vmbin 也只消费最终 `argaddr`。 +vthread `pc`、`status` 和 `/lib` 函数源等地址生命周期长的 key 属于当前优化范围。map、 +struct 等动态拼接 key 可能尚未分配或发生地址变化;key/xvalue 生命周期 ABI 建立前, +vmkv 必须返回 `RUNTIMEC_STATUS_UNSUPPORTED`,不得生成零地址、间接 lookup 或 fallback。 + +## Compilation lifecycle + +BinRWIR Compiler 的唯一内容输入是 canonical `binrwir_program_t`。它按固定顺序完成: + +1. 验证 Program、manifest version 和 binary interface generation。 +2. 构建 `binrwir_execution_plan_t`。 +3. 若允许 JIT,调用内部 `jit/` 的 verifier、lowering 与 backend 编译合同。 +4. 形成 `binrwir_compilation_t`,其中 execution target 已不可变。 + +通用 execution plan 可包含紧凑 dispatch、`opid` 特化、静态 `argaddr` 调度、保持写回 +边界的指令融合和控制流信息。每个 source logical instruction completion 都必须保留为 +不可关闭的 recovery/observable boundary,即使多个 handler 被融合也不能合并完成边界。 +plan 不得包含 xvalue 副本、body cache 或通用寄存器分配。register allocation 只属于 +内部 JIT lowering。所有优化都必须保留 +Read/Write authority、别名可见性和 source logical program counter 观察边界。 + +内部 JIT 编译判定严格如下;顶层 compilation 对象只在前两种成功方向产生: + +| JIT compilation outcome | `runtimec_status_t` | Execution target | Required content | +|---|---|---|---| +| `COMPILED` | `RUNTIMEC_STATUS_SUCCESS` | `JIT` | Program、plan、非空机器码 | +| `NOT_COMPILABLE` | `RUNTIMEC_STATUS_SUCCESS` | `VMBIN` | Program、plan、空机器码 | +| `ERROR` | 明确错误 | 无 | 不产生可执行 compilation | + +`NOT_COMPILABLE` 表示 JIT eligibility 已成功判定为不适用。它是建立 VMBIN compilation +的正常编译结果。非法 BinRWIR、映射错误、内存不足与内部错误进入 `ERROR`,必须原样 +停止,不能变成任何可执行 compilation。 + +## Repository and route + +repository 只缓存完整 compilation。缓存键绑定 authoritative snapshot、RWIR manifest +version、canonical encoding revision、BinRWIR binary interface generation、compiler +revision;完整 `snapshot.source_revision` 的变化使全部 `argaddr` 失效。租约保证 Program 中的 `argaddr`、plan 和可选机器码 +在执行期间一起有效。地址失效后不得继续租用 compilation,必须从明文 RWIR 和当前 +kvspace body 地址重新生成。 +内部 JIT 不拥有独立 compilation cache 或独立编译入口;机器码只作为完整 compilation +的一部分进入 repository。 + +route 在执行前调用 `binrwir_compilation_get_view`,读取 Compiler 已决定的 +`binrwir_execution_target_t`: + +- `JIT`:只调用 `binrwir_jit_execute`; +- `VMBIN`:只调用 `vmbin_execute`; +- 其它值:立即报告非法 compilation。 + +route 不在执行时调用 eligibility,不在一个执行入口失败后调用另一个入口,也不修改 +compilation。执行错误沿所选入口返回并停止。 + +## Facts and derived state + +kvspace 明文保存可恢复语义事实:RWIR、全部 `V.body`、frame facts、logical program +counter、status、error 与 recovery state。canonical Program、execution plan、机器码、 +JIT 寄存器状态、profile 和 repository entry 都是可删除并重建的派生状态。 + +vmbin 是 address-driven memory executor:每次从 `argaddr` 指向的 body 读取,并在逻辑 +instruction 完成边界前把全部 Write 结果写回对应 body;它不拥有 value cache 或 register +file。每条 logical instruction completion 都是不可关闭的 recovery/observable boundary。 + +JIT 是唯一允许在当前逻辑指令内部临时把 body 提升到机器寄存器或 spill 的路径。它只能 +对完整完成指令执行 `COMMIT_COMPLETED_STATE`:先把该指令的全部 completed dirty state +materialize 到原 `argaddr`,全部写回成功后再推进 logical program counter/checkpoint。 +该提交是完成边界的唯一成功出口。 + +guard failure、error、recovery 或 resume 遇到当前指令未完成时,JIT 必须执行 +`DISCARD_INCOMPLETE_STATE`,丢弃全部 speculative/partial register 与 spill state。discard +不含 materialization entry,不写任何 `argaddr`,也不推进 logical program counter 或 +checkpoint。恢复从最后一个已提交完成边界的 kvspace body 重建。 + +## Read/Write aliasing + +RWIR 与 BinRWIR 只有 Read 和 Write 两种参数方向。两个不同 `argidx` 持有相同 `argaddr` +表示实际 Read/Write alias;权限和许可仍由 `(opid, argidx)` 的 canonical signature 决定。 +写入该 body 后,后续 Read 必须观察到新值。vmbin 不能保留旧 body 快照;JIT 必须在当前 +指令的 speculative state 内维护 alias dependency。完整指令才可提交全部 aliased dirty +state;未完成指令必须全部丢弃。未声明 alias 必须失败。 + +## Dependencies + +```text +interface -> public header + contract + engine +engine/route -> host + vmkv + binrwir_compiler + binrwir + binrwir_compiler/jit executor + vmbin +engine/compilation_repository -> binrwir_compiler + contract +binrwir_compiler -> contract + binrwir + internal jit +vmkv -> contract + host address-resolution contract + binrwir build contracts + generated specification facts +vmbin -> contract + binrwir execution contracts +binrwir_compiler/jit -> contract + binrwir execution contracts +host -> contract + external kvspace interface +``` + +`binrwir/` 不依赖任一执行器。vmkv 不依赖 BinRWIR Compiler 或执行器。vmbin 不依赖 +`binrwir_compiler/jit/`;内部 JIT 也不依赖 vmbin。除了 route,engine 文件不能同时 +看见两个确定执行入口。 + +## Acceptance + +- 每个 myrwircap 恰好映射一个 canonical operation;stable identifier 与数组位置无关。 +- vmkv 将 operation name 映射为 `opid`,将每个参数映射为 `argidx + uint64 argaddr`。 +- host 只返回静态 key 的非零 64 位 XValue 基址;vmkv 唯一以固定 8 字节 head 和溢出检查 + 生成 `argaddr = xvalue_address + 8`。 +- BinRWIR 不保存参数类型、参数名、key 文本、xvalue/body 副本或通用寄存器状态。 +- BinRWIR 只缓存静态地址解析结果;vmbin 消费地址,并在每条逻辑 instruction 完成前把 Write 放回 kvspace。 +- 零 XValue 基址、`+8` 溢出、非 8 字节 head ABI、动态 key 或无法证明生命周期均失败 + 封闭,不产生 Program。 +- 每条 logical instruction completion 都是不可关闭的 recovery/observable boundary。 +- JIT 只提交完整完成指令;未完成 register/spill state 只能丢弃,不能 materialize。 +- logical program counter/checkpoint 只在 completed state 全部写回成功后推进。 +- Compiler 总是先生成 plan,再形成一个确定 execution target 的 compilation。 +- repository 缓存 compilation,而不是分散缓存 Program、plan 与 code。 +- route 在执行前读取目标,并只调用一个执行入口。 +- `NOT_COMPILABLE` 返回 success 并选择 VMBIN;`ERROR` 立即停止。 +- BinRWIR 保持顶层中立,内部 JIT 不依赖 vmbin。 +- 未实现能力明确返回 `RUNTIMEC_STATUS_NOT_IMPLEMENTED`,输出先清空。 +- 不存在第三执行器、旧入口、兼容别名或隐藏执行路径。 +- 删除全部派生缓存(Program、plan、机器码、JIT 状态、profile、repository)后,从 + kvspace 明文 RWIR 与当前 `V.body` 重建并继续执行(#499)。 +- AArch64 backend 与 x86_64 实现同一合同,不允许在 x86_64 完成后长期保留 AArch64 空桩 + (#541)。 diff --git a/runtimec/documentation/architecture/runtimec-dependencies.mmd b/runtimec/documentation/architecture/runtimec-dependencies.mmd new file mode 100644 index 00000000..1ee897b2 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-dependencies.mmd @@ -0,0 +1,65 @@ +flowchart LR + SPECIFICATION["specification
operation name, opid, argidx, signature"] + INTERFACE["public interface"] + ROUTE["engine route"] + REPOSITORY["compilation repository
address-qualified lease"] + HOST["host services
RWIR load and static key dereference
return XValue base only; no body offset choice"] + KVSPACE["external kvspace
RWIR and XValue base
fixed 8-byte head then authoritative V.body"] + VMKV["vmkv
only owner of checked xvalue_address + 8
addressed BinRWIR emission or fail closed"] + BINRWIR["top-level process-local BinRWIR
opid plus argidx and uint64 argaddr"] + VMBIN["vmbin
address-driven memory executor"] + + subgraph BINRWIR_COMPILER["binrwir_compiler"] + COMPILER["compiler and address-preserving optimization"] + subgraph JIT["internal JIT"] + JIT_COMPILER["verifier, lowering, and register allocation"] + BACKENDS["x86_64 and AArch64 backends"] + JIT_SAFEPOINT["mandatory instruction-completion boundary
commit completed or discard incomplete"] + JIT_EXECUTOR["machine-code executor
speculative register and spill state"] + DISCARDED["discarded incomplete state
no argaddr write and no checkpoint advance"] + JIT_COMPILER --> BACKENDS + JIT_EXECUTOR --> JIT_SAFEPOINT + JIT_SAFEPOINT -->|"DISCARD_INCOMPLETE_STATE"| DISCARDED + end + COMPILER --> JIT_COMPILER + end + + INTERFACE --> ROUTE + ROUTE --> HOST + ROUTE --> REPOSITORY + ROUTE --> VMKV + ROUTE --> COMPILER + ROUTE --> BINRWIR + ROUTE --> JIT_EXECUTOR + ROUTE --> VMBIN + HOST --> KVSPACE + VMKV --> SPECIFICATION + VMKV -->|"request static key dereference"| HOST + HOST -->|"nonzero uint64 xvalue_address"| VMKV + VMKV --> BINRWIR + COMPILER --> BINRWIR + REPOSITORY --> COMPILER + VMBIN --> BINRWIR + JIT_COMPILER --> BINRWIR + JIT_EXECUTOR --> BINRWIR + VMBIN -. "direct final argaddr load and store; no XValue or body cache" .-> KVSPACE + JIT_SAFEPOINT -. "COMMIT_COMPLETED_STATE only after full instruction completion" .-> KVSPACE + + BINRWIR -. "forbidden ownership dependency" .-> VMBIN + BINRWIR -. "forbidden ownership dependency" .-> JIT + VMKV -. "forbidden execution dependency" .-> VMBIN + VMKV -. "forbidden execution dependency" .-> JIT + VMBIN -. "forbidden peer dependency" .-> JIT + JIT -. "forbidden peer dependency" .-> VMBIN + REPOSITORY -. "forbidden execution dependency" .-> VMBIN + REPOSITORY -. "forbidden execution dependency" .-> JIT_EXECUTOR + + classDef specification fill:#fff3cd,stroke:#9a6700,color:#3d2e00 + classDef neutral fill:#ede9fe,stroke:#7c3aed,color:#2e1065 + classDef compiler fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e + classDef executor fill:#ecfdf5,stroke:#059669,color:#052e16 + class SPECIFICATION,KVSPACE specification + class BINRWIR neutral + class VMKV,COMPILER,REPOSITORY,JIT_COMPILER,BACKENDS compiler + class VMBIN,JIT_EXECUTOR,JIT_SAFEPOINT executor + class DISCARDED neutral diff --git a/runtimec/documentation/architecture/runtimec-dependencies.png b/runtimec/documentation/architecture/runtimec-dependencies.png new file mode 100644 index 00000000..77fb5475 Binary files /dev/null and b/runtimec/documentation/architecture/runtimec-dependencies.png differ diff --git a/runtimec/documentation/architecture/runtimec-dependencies.svg b/runtimec/documentation/architecture/runtimec-dependencies.svg new file mode 100644 index 00000000..25aa61a8 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-dependencies.svg @@ -0,0 +1 @@ +

binrwir_compiler

internal JIT

DISCARD_INCOMPLETE_STATE

request static key dereference

nonzero uint64 xvalue_address

direct final argaddr load and store; no XValue or body cache

COMMIT_COMPLETED_STATE only after full instruction completion

forbidden ownership dependency

forbidden ownership dependency

forbidden execution dependency

forbidden execution dependency

forbidden peer dependency

forbidden peer dependency

forbidden execution dependency

forbidden execution dependency

specification
operation name, opid, argidx, signature

public interface

engine route

compilation repository
address-qualified lease

host services
RWIR load and static key dereference
return XValue base only; no body offset choice

external kvspace
RWIR and XValue base
fixed 8-byte head then authoritative V.body

vmkv
only owner of checked xvalue_address + 8
addressed BinRWIR emission or fail closed

top-level process-local BinRWIR
opid plus argidx and uint64 argaddr

vmbin
address-driven memory executor

compiler and address-preserving optimization

verifier, lowering, and register allocation

x86_64 and AArch64 backends

mandatory instruction-completion boundary
commit completed or discard incomplete

machine-code executor
speculative register and spill state

discarded incomplete state
no argaddr write and no checkpoint advance

\ No newline at end of file diff --git a/runtimec/documentation/architecture/runtimec-execution.mmd b/runtimec/documentation/architecture/runtimec-execution.mmd new file mode 100644 index 00000000..8ab29c10 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-execution.mmd @@ -0,0 +1,110 @@ +sequenceDiagram + participant Client as C client + participant Interface as Public interface + participant Route as engine route + participant Host as Host and kvspace services + participant Repository as Compilation repository + participant Vmkv as vmkv + participant Compiler as binrwir_compiler + participant JitCompiler as Internal JIT compiler + participant JitExecute as binrwir_jit_execute + participant VmbinExecute as vmbin_execute + participant XValue as KVspace XValue: 8-byte head plus V.body + + Client->>Interface: structured execute request + Interface->>Route: validated request + Route->>Host: obtain authoritative snapshot and source revision + Host-->>Route: complete snapshot.source_revision + Route->>Repository: find compilation for exact snapshot identity + alt valid compilation found + Repository-->>Route: immutable compilation lease with valid argaddr values + else compilation not found or source revision changed + Repository-->>Route: not found + Route->>Vmkv: validate RWIR and build addressed BinRWIR + Vmkv->>Host: open plaintext function source for snapshot + Host-->>Vmkv: borrowed RWIR source bytes + loop every Read or Write parameter + Vmkv->>Host: dereference static parameter key to XValue base + alt nonzero XValue base exists and address lifetime is supported + Host-->>Vmkv: nonzero uint64 xvalue_address only + Vmkv->>Vmkv: require fixed 8-byte head ABI and check addition overflow + alt head is exactly 8 bytes and xvalue_address plus 8 does not overflow + Vmkv->>Vmkv: argaddr = xvalue_address + 8 + else non-8-byte head ABI or addition overflow + break fail closed before BinRWIR creation + Vmkv-->>Route: explicit failure and no Program + Route-->>Interface: structured failure result + Interface-->>Client: failure + end + end + else zero base, dynamic map or struct key, or unsupported lifetime + Host-->>Vmkv: no usable XValue base + break unsupported stops before BinRWIR creation + Vmkv-->>Route: RUNTIMEC_STATUS_UNSUPPORTED and no Program + Route-->>Interface: structured unsupported result + Interface-->>Client: unsupported + end + end + end + Vmkv-->>Route: opid plus complete argidx and argaddr pairs + Route->>Compiler: compile addressed canonical Program + Compiler->>Compiler: build plan without value cache or general register allocation + alt JIT enabled + Compiler->>JitCompiler: verify, lower, allocate machine registers, and emit + alt COMPILED + JitCompiler-->>Compiler: generated machine code + Compiler-->>Route: complete JIT-target compilation + else NOT_COMPILABLE + JitCompiler-->>Compiler: successful refusal decision + Compiler-->>Route: complete VMBIN-target compilation + else ERROR + JitCompiler-->>Compiler: explicit compilation error + break compilation stops without fallback + Compiler-->>Route: error and no compilation + Route-->>Interface: structured error + Interface-->>Client: error + end + end + else JIT disabled + Compiler-->>Route: complete VMBIN-target compilation + end + opt completed compilation + Route->>Repository: insert complete address-qualified compilation + Repository-->>Route: immutable compilation lease + end + end + + Route->>Route: read immutable execution target once + alt JIT target + Route->>JitExecute: execute existing machine code + JitExecute->>XValue: load V.body through final argaddr + JitExecute->>JitExecute: speculative register and spill computation + alt current logical instruction fully completed + Note over JitExecute,XValue: Every instruction completion is a mandatory recovery and observable boundary + JitExecute->>XValue: COMMIT_COMPLETED_STATE with all completed dirty values + XValue-->>JitExecute: every original argaddr write succeeded + JitExecute-->>Route: completed execution exit + Route->>Host: advance logical program counter and checkpoint + else guard failure, error, recovery, or resume before completion + JitExecute->>JitExecute: DISCARD_INCOMPLETE_STATE for all register and spill state + Note over JitExecute,XValue: No materialization entry and no partial body write + JitExecute-->>Route: incomplete exit from last committed boundary + Route->>Host: retain logical program counter and checkpoint + end + else VMBIN target + Route->>VmbinExecute: execute opid with argidx and argaddr pairs + VmbinExecute->>XValue: direct V.body Read through final argaddr + alt logical instruction completed + VmbinExecute->>XValue: direct V.body Write before completion boundary + Note over VmbinExecute,XValue: Every instruction completion is a mandatory recovery and observable boundary + XValue-->>VmbinExecute: completed body effects + VmbinExecute-->>Route: completed execution exit + Route->>Host: advance only after every completed body write succeeds + else execution error before completion + VmbinExecute-->>Route: incomplete error exit + Route->>Host: retain logical program counter and checkpoint + end + end + Route->>Repository: release compilation lease + Route-->>Interface: structured result + Interface-->>Client: status and result diff --git a/runtimec/documentation/architecture/runtimec-execution.png b/runtimec/documentation/architecture/runtimec-execution.png new file mode 100644 index 00000000..bd739eb7 Binary files /dev/null and b/runtimec/documentation/architecture/runtimec-execution.png differ diff --git a/runtimec/documentation/architecture/runtimec-execution.svg b/runtimec/documentation/architecture/runtimec-execution.svg new file mode 100644 index 00000000..47beeefa --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-execution.svg @@ -0,0 +1 @@ +KVspace XValue: 8-byte head plus V.bodyvmbin_executebinrwir_jit_executeInternal JIT compilerbinrwir_compilervmkvCompilation repositoryHost and kvspace servicesengine routePublic interfaceC clientKVspace XValue: 8-byte head plus V.bodyvmbin_executebinrwir_jit_executeInternal JIT compilerbinrwir_compilervmkvCompilation repositoryHost and kvspace servicesengine routePublic interfaceC clientbreak[fail closed before BinRWIR creation]alt[head is exactly 8 bytes and xvalue_address plus 8 does not overflow][non-8-byte head ABI or addition overflow]break[unsupported stops before BinRWIR creation]alt[nonzero XValue base exists and address lifetime is supported][zero base, dynamic map or struct key, or unsupported lifetime]loop[every Read or Write parameter]break[compilation stops without fallback]alt[COMPILED][NOT_COMPILABLE][ERROR]alt[JIT enabled][JIT disabled]opt[completed compilation]alt[valid compilation found][compilation not found or source revision changed]Every instruction completion is a mandatory recovery and observable boundaryNo materialization entry and no partial body writealt[current logical instruction fully completed][guard failure, error, recovery, or resume before completion]Every instruction completion is a mandatory recovery and observable boundaryalt[logical instruction completed][execution error before completion]alt[JIT target][VMBIN target]structured execute requestvalidated requestobtain authoritative snapshot and source revisioncomplete snapshot.source_revisionfind compilation for exact snapshot identityimmutable compilation lease with valid argaddr valuesnot foundvalidate RWIR and build addressed BinRWIRopen plaintext function source for snapshotborrowed RWIR source bytesdereference static parameter key to XValue basenonzero uint64 xvalue_address onlyrequire fixed 8-byte head ABI and check addition overflowargaddr = xvalue_address + 8explicit failure and no Programstructured failure resultfailureno usable XValue baseRUNTIMEC_STATUS_UNSUPPORTED and no Programstructured unsupported resultunsupportedopid plus complete argidx and argaddr pairscompile addressed canonical Programbuild plan without value cache or general register allocationverify, lower, allocate machine registers, and emitgenerated machine codecomplete JIT-target compilationsuccessful refusal decisioncomplete VMBIN-target compilationexplicit compilation errorerror and no compilationstructured errorerrorcomplete VMBIN-target compilationinsert complete address-qualified compilationimmutable compilation leaseread immutable execution target onceexecute existing machine codeload V.body through final argaddrspeculative register and spill computationCOMMIT_COMPLETED_STATE with all completed dirty valuesevery original argaddr write succeededcompleted execution exitadvance logical program counter and checkpointDISCARD_INCOMPLETE_STATE for all register and spill stateincomplete exit from last committed boundaryretain logical program counter and checkpointexecute opid with argidx and argaddr pairsdirect V.body Read through final argaddrdirect V.body Write before completion boundarycompleted body effectscompleted execution exitadvance only after every completed body write succeedsincomplete error exitretain logical program counter and checkpointrelease compilation leasestructured resultstatus and result \ No newline at end of file diff --git a/runtimec/documentation/architecture/runtimec-trunk.mmd b/runtimec/documentation/architecture/runtimec-trunk.mmd new file mode 100644 index 00000000..2867d2c7 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-trunk.mmd @@ -0,0 +1,64 @@ +flowchart TB + RWIR["RWIR in kvspace
plaintext operation name
typed and named Read or Write parameters"] + SPECIFICATION["myrwircaps and specification
operation name to opid
parameter to argidx"] + VMKV_VALIDATE["vmkv
validate RWIR and request static key dereference"] + HOST["host static key dereference
return uint64 XValue base only
no body offset choice"] + ADDRESS_KIND{"Every XValue base
nonzero and lifetime proven?"} + BODY_ABI{"XValue head ABI exactly 8 bytes
and +8 cannot overflow?"} + VMKV_DERIVE["vmkv only
derive argaddr = xvalue_address + 8"] + FAIL_CLOSED["INVALID or UNSUPPORTED
zero base, overflow, non-8-byte head,
dynamic key, or unproven lifetime
no BinRWIR and no compilation"] + PROGRAM["process-local canonical BinRWIR
opid plus repeated argidx and uint64 argaddr
no key, xvalue, body, or register file"] + + subgraph BINRWIR_COMPILER["binrwir_compiler"] + PLAN["build address-preserving execution plan
no body cache or general register allocation"] + JIT_POLICY{"JIT enabled?"} + JIT_COMPILE["internal JIT compiler
lower and allocate machine registers"] + JIT_OUTCOME{"JIT compilation outcome"} + JIT_COMPILATION["COMPILED
JIT target with machine code"] + VMBIN_COMPILATION["NOT_COMPILABLE or JIT disabled
VMBIN address plan"] + PLAN --> JIT_POLICY + JIT_POLICY -->|"yes"| JIT_COMPILE --> JIT_OUTCOME + JIT_POLICY -->|"no"| VMBIN_COMPILATION + JIT_OUTCOME -->|"COMPILED"| JIT_COMPILATION + JIT_OUTCOME -->|"NOT_COMPILABLE"| VMBIN_COMPILATION + end + + ERROR["ERROR
stop without compilation"] + REPOSITORY["compilation repository
address-lifetime-qualified lease"] + ROUTE{"engine route
read decided target once"} + JIT_EXECUTE["binrwir_jit_execute
speculative register and spill state
inside current logical instruction only"] + JIT_BOUNDARY{"Current logical instruction
fully completed?"} + COMMIT_COMPLETED["COMMIT_COMPLETED_STATE
materialize every completed dirty body
to original argaddr"] + DISCARD_INCOMPLETE["DISCARD_INCOMPLETE_STATE
drop all speculative or partial register and spill state
zero body write"] + VMBIN_EXECUTE["vmbin_execute
address-driven memory execution
mandatory boundary at every instruction completion"] + BODY["kvspace XValue
8-byte head then V.body at final argaddr
only authoritative value residency"] + CONTROL["advance logical program counter and checkpoint
only after every completed body write succeeds"] + RETAIN["retain last committed program counter and checkpoint
recover or resume only from kvspace"] + + RWIR --> VMKV_VALIDATE + SPECIFICATION --> VMKV_VALIDATE + VMKV_VALIDATE --> HOST + HOST --> ADDRESS_KIND + ADDRESS_KIND -->|"yes: static key"| BODY_ABI + BODY_ABI -->|"yes"| VMKV_DERIVE --> PROGRAM --> PLAN + ADDRESS_KIND -->|"no: zero, dynamic, or unproven"| FAIL_CLOSED + BODY_ABI -->|"no: ABI mismatch or overflow"| FAIL_CLOSED + JIT_OUTCOME -->|"ERROR"| ERROR + JIT_COMPILATION --> REPOSITORY + VMBIN_COMPILATION --> REPOSITORY + REPOSITORY --> ROUTE + ROUTE -->|"JIT target only"| JIT_EXECUTE --> JIT_BOUNDARY + ROUTE -->|"VMBIN target only"| VMBIN_EXECUTE + JIT_BOUNDARY -->|"yes: mandatory completion boundary"| COMMIT_COMPLETED --> BODY + JIT_BOUNDARY -->|"no: guard failure, error, recovery, or resume"| DISCARD_INCOMPLETE --> RETAIN + VMBIN_EXECUTE -->|"completed direct load and store"| BODY + BODY -->|"all completed writes succeeded"| CONTROL + + classDef fact fill:#fff3cd,stroke:#9a6700,color:#3d2e00 + classDef compile fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e + classDef execute fill:#ecfdf5,stroke:#059669,color:#052e16 + classDef error fill:#fee2e2,stroke:#dc2626,color:#450a0a + class RWIR,SPECIFICATION,BODY fact + class VMKV_VALIDATE,HOST,VMKV_DERIVE,PROGRAM,PLAN,JIT_POLICY,JIT_COMPILE,JIT_OUTCOME,JIT_COMPILATION,VMBIN_COMPILATION,REPOSITORY compile + class ROUTE,JIT_EXECUTE,JIT_BOUNDARY,COMMIT_COMPLETED,VMBIN_EXECUTE,CONTROL execute + class ERROR,FAIL_CLOSED,DISCARD_INCOMPLETE,RETAIN error diff --git a/runtimec/documentation/architecture/runtimec-trunk.png b/runtimec/documentation/architecture/runtimec-trunk.png new file mode 100644 index 00000000..1642723a Binary files /dev/null and b/runtimec/documentation/architecture/runtimec-trunk.png differ diff --git a/runtimec/documentation/architecture/runtimec-trunk.svg b/runtimec/documentation/architecture/runtimec-trunk.svg new file mode 100644 index 00000000..61102244 --- /dev/null +++ b/runtimec/documentation/architecture/runtimec-trunk.svg @@ -0,0 +1 @@ +

binrwir_compiler

yes

no

COMPILED

NOT_COMPILABLE

yes: static key

yes

no: zero, dynamic, or unproven

no: ABI mismatch or overflow

ERROR

JIT target only

VMBIN target only

yes: mandatory completion boundary

no: guard failure, error, recovery, or resume

completed direct load and store

all completed writes succeeded

RWIR in kvspace
plaintext operation name
typed and named Read or Write parameters

myrwircaps and specification
operation name to opid
parameter to argidx

vmkv
validate RWIR and request static key dereference

host static key dereference
return uint64 XValue base only
no body offset choice

Every XValue base
nonzero and lifetime proven?

XValue head ABI exactly 8 bytes
and +8 cannot overflow?

vmkv only
derive argaddr = xvalue_address + 8

INVALID or UNSUPPORTED
zero base, overflow, non-8-byte head,
dynamic key, or unproven lifetime
no BinRWIR and no compilation

process-local canonical BinRWIR
opid plus repeated argidx and uint64 argaddr
no key, xvalue, body, or register file

build address-preserving execution plan
no body cache or general register allocation

JIT enabled?

internal JIT compiler
lower and allocate machine registers

JIT compilation outcome

COMPILED
JIT target with machine code

NOT_COMPILABLE or JIT disabled
VMBIN address plan

ERROR
stop without compilation

compilation repository
address-lifetime-qualified lease

engine route
read decided target once

binrwir_jit_execute
speculative register and spill state
inside current logical instruction only

Current logical instruction
fully completed?

COMMIT_COMPLETED_STATE
materialize every completed dirty body
to original argaddr

DISCARD_INCOMPLETE_STATE
drop all speculative or partial register and spill state
zero body write

vmbin_execute
address-driven memory execution
mandatory boundary at every instruction completion

kvspace XValue
8-byte head then V.body at final argaddr
only authoritative value residency

advance logical program counter and checkpoint
only after every completed body write succeeds

retain last committed program counter and checkpoint
recover or resume only from kvspace

\ No newline at end of file diff --git a/runtimec/documentation/architecture/rwir-binrwir-aliasing.mmd b/runtimec/documentation/architecture/rwir-binrwir-aliasing.mmd new file mode 100644 index 00000000..955ab164 --- /dev/null +++ b/runtimec/documentation/architecture/rwir-binrwir-aliasing.mmd @@ -0,0 +1,68 @@ +flowchart LR + subgraph RWIR["RWIR plaintext in kvspace"] + OPERATION_NAME["operation name"] + READ_PARAMETER["Read parameter
type plus name plus key"] + WRITE_PARAMETER["Write parameter
type plus name plus key"] + end + + subgraph SPECIFICATION["Canonical specification"] + OPID["stable opid"] + READ_ARGIDX["Read argidx and permission"] + WRITE_ARGIDX["Write argidx and permission"] + ALIAS_POLICY["explicit Read and Write alias policy"] + end + + VMKV_VALIDATE["vmkv
validate RWIR and request static key dereference"] + HOST["host
dereference static key to nonzero uint64 XValue base
never choose body offset"] + XVALUE["kvspace XValue
64-bit base plus fixed 8-byte head"] + VMKV_DERIVE["vmkv only
compute argaddr = xvalue_address + 8
with overflow check"] + + subgraph BINRWIR["Process-local BinRWIR"] + BINARY_OPERATION["opid only"] + READ_ARGUMENT["Read argidx plus uint64 argaddr"] + WRITE_ARGUMENT["Write argidx plus uint64 argaddr"] + end + + BODY["one authoritative kvspace V.body
at XValue base + 8
same nonzero 64-bit argaddr"] + VMBIN["vmbin
direct load and store
no value cache or register file"] + JIT["internal JIT current instruction
speculative register and spill state
alias reads observe speculative writes"] + COMPLETION{"Logical instruction
fully completed?"} + COMMIT["COMMIT_COMPLETED_STATE
materialize all aliased dirty values
then advance checkpoint"] + DISCARD["DISCARD_INCOMPLETE_STATE
drop partial register and spill state
no body write or checkpoint advance"] + DYNAMIC["fail closed with no BinRWIR
zero base, overflow, non-8-byte head ABI,
dynamic key, or unproven lifetime"] + + OPERATION_NAME --> VMKV_VALIDATE + READ_PARAMETER --> VMKV_VALIDATE + WRITE_PARAMETER --> VMKV_VALIDATE + OPID --> VMKV_VALIDATE + READ_ARGIDX --> VMKV_VALIDATE + WRITE_ARGIDX --> VMKV_VALIDATE + ALIAS_POLICY --> VMKV_VALIDATE + VMKV_VALIDATE --> HOST + HOST --> XVALUE --> VMKV_DERIVE + VMKV_DERIVE --> BINARY_OPERATION + VMKV_DERIVE --> READ_ARGUMENT + VMKV_DERIVE --> WRITE_ARGUMENT + READ_ARGUMENT -->|"same argaddr"| BODY + WRITE_ARGUMENT -->|"same argaddr"| BODY + READ_ARGIDX -. "Read only" .-> READ_ARGUMENT + WRITE_ARGIDX -. "Write only" .-> WRITE_ARGUMENT + ALIAS_POLICY -. "permits equal addresses" .-> BODY + BODY -->|"later Read observes prior Write"| VMBIN + BODY -->|"initial Read through argaddr"| JIT + JIT --> COMPLETION + COMPLETION -->|"yes: mandatory observable boundary"| COMMIT --> BODY + COMPLETION -->|"no: guard failure, error, recovery, or resume"| DISCARD + HOST -->|"zero, dynamic, or lifetime cannot be proven"| DYNAMIC + VMKV_DERIVE -->|"head ABI mismatch or +8 overflow"| DYNAMIC + + classDef plaintext fill:#fef3c7,stroke:#d97706,color:#451a03 + classDef read fill:#dbeafe,stroke:#2563eb,color:#172554 + classDef write fill:#fee2e2,stroke:#dc2626,color:#450a0a + classDef shared fill:#dcfce7,stroke:#16a34a,color:#052e16 + classDef unsupported fill:#f3f4f6,stroke:#4b5563,color:#111827 + class OPERATION_NAME,READ_PARAMETER,WRITE_PARAMETER,OPID,READ_ARGIDX,WRITE_ARGIDX,ALIAS_POLICY plaintext + class READ_ARGUMENT read + class WRITE_ARGUMENT write + class XVALUE,BODY,VMBIN,JIT,COMPLETION,COMMIT shared + class DYNAMIC,DISCARD unsupported diff --git a/runtimec/documentation/architecture/rwir-binrwir-aliasing.png b/runtimec/documentation/architecture/rwir-binrwir-aliasing.png new file mode 100644 index 00000000..059b64cd Binary files /dev/null and b/runtimec/documentation/architecture/rwir-binrwir-aliasing.png differ diff --git a/runtimec/documentation/architecture/rwir-binrwir-aliasing.svg b/runtimec/documentation/architecture/rwir-binrwir-aliasing.svg new file mode 100644 index 00000000..39d2c447 --- /dev/null +++ b/runtimec/documentation/architecture/rwir-binrwir-aliasing.svg @@ -0,0 +1 @@ +

Process-local BinRWIR

Canonical specification

RWIR plaintext in kvspace

same argaddr

same argaddr

Read only

Write only

permits equal addresses

later Read observes prior Write

initial Read through argaddr

yes: mandatory observable boundary

no: guard failure, error, recovery, or resume

zero, dynamic, or lifetime cannot be proven

head ABI mismatch or +8 overflow

operation name

Read parameter
type plus name plus key

Write parameter
type plus name plus key

stable opid

Read argidx and permission

Write argidx and permission

explicit Read and Write alias policy

vmkv
validate RWIR and request static key dereference

host
dereference static key to nonzero uint64 XValue base
never choose body offset

kvspace XValue
64-bit base plus fixed 8-byte head

vmkv only
compute argaddr = xvalue_address + 8
with overflow check

opid only

Read argidx plus uint64 argaddr

Write argidx plus uint64 argaddr

one authoritative kvspace V.body
at XValue base + 8
same nonzero 64-bit argaddr

vmbin
direct load and store
no value cache or register file

internal JIT current instruction
speculative register and spill state
alias reads observe speculative writes

Logical instruction
fully completed?

COMMIT_COMPLETED_STATE
materialize all aliased dirty values
then advance checkpoint

DISCARD_INCOMPLETE_STATE
drop partial register and spill state
no body write or checkpoint advance

fail closed with no BinRWIR
zero base, overflow, non-8-byte head ABI,
dynamic key, or unproven lifetime

\ No newline at end of file diff --git a/runtimec/engine/compilation_repository.c b/runtimec/engine/compilation_repository.c new file mode 100644 index 00000000..cad936b5 --- /dev/null +++ b/runtimec/engine/compilation_repository.c @@ -0,0 +1,90 @@ +/* engine/compilation_repository 的失败封闭骨架。 */ + +#include "engine/compilation_repository.h" + +runtimec_status_t runtimec_compilation_repository_create( + runtimec_allocator_t allocator, + size_t capacity, + runtimec_compilation_repository_t **output_repository, + runtimec_error_t *error) { + (void)error; + if (output_repository != NULL) { + *output_repository = NULL; + } + if (!runtimec_allocator_is_valid(&allocator) || capacity == 0U || + output_repository == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_destroy( + runtimec_compilation_repository_t *repository) { + if (repository == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_find( + runtimec_compilation_repository_t *repository, + const runtimec_compilation_repository_key_t *key, + runtimec_compilation_repository_lookup_result_t *output_lookup_result, + runtimec_compilation_repository_lease_t *output_lease, + runtimec_error_t *error) { + (void)error; + if (output_lookup_result != NULL) { + *output_lookup_result = RUNTIMEC_COMPILATION_REPOSITORY_LOOKUP_INVALID; + } + if (output_lease != NULL) { + *output_lease = (runtimec_compilation_repository_lease_t){0}; + } + if (repository == NULL || key == NULL || output_lookup_result == NULL || + output_lease == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_insert( + runtimec_compilation_repository_t *repository, + const runtimec_compilation_repository_key_t *key, + binrwir_compilation_t *compilation, + runtimec_compilation_repository_lease_t *output_lease, + runtimec_error_t *error) { + (void)error; + if (output_lease != NULL) { + *output_lease = (runtimec_compilation_repository_lease_t){0}; + } + if (repository == NULL || key == NULL || compilation == NULL || + output_lease == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_release( + runtimec_compilation_repository_t *repository, + runtimec_compilation_repository_lease_t *lease) { + if (repository == NULL || lease == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_invalidate( + runtimec_compilation_repository_t *repository, + const runtimec_snapshot_t *snapshot) { + if (repository == NULL || snapshot == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_compilation_repository_clear( + runtimec_compilation_repository_t *repository) { + if (repository == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/engine/compilation_repository.h b/runtimec/engine/compilation_repository.h new file mode 100644 index 00000000..794bb19e --- /dev/null +++ b/runtimec/engine/compilation_repository.h @@ -0,0 +1,95 @@ +#ifndef RUNTIMEC_ENGINE_COMPILATION_REPOSITORY_H +#define RUNTIMEC_ENGINE_COMPILATION_REPOSITORY_H + +/* + * 组件:engine/compilation_repository + * 作用:缓存完整 BinRWIR compilation 并提供租约。find 只查询不可变 + * compilation;insert 只接收 BinRWIR Compiler 已完成的 compilation; + * release 归还 lease;invalidate/clear 撤销新获取资格,但不得释放仍被 + * lease 持有的 compilation。 + * 联系:route 在编码和编译前查询,在 BinRWIR Compiler 完成后插入;lease + * 保证 canonical Program、执行计划与可选机器码在执行期间同时有效。 + * 约束:repository 只持有 compilation 的所有权、租约与索引,不调用 vmkv、 + * vmbin 或 BinRWIR JIT,也不改变已确定的执行目标。Program 中的 argaddr + * 只在 key.snapshot 对应的 source revision 有效期间可用;revision 失效 + * 必须使整份 compilation 失效,禁止只刷新地址或保留 plan/机器码。 + */ + +#include "binrwir_compiler/compiler.h" +#include "contract/allocator.h" +#include "contract/snapshot.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_compilation_repository_t + runtimec_compilation_repository_t; + +typedef struct runtimec_compilation_repository_key_t { + runtimec_snapshot_t snapshot; + uint32_t rwir_manifest_version; + uint32_t canonical_encoding_revision; + uint32_t binrwir_binary_interface_generation; + uint32_t compiler_revision; +} runtimec_compilation_repository_key_t; + +/* + * find 只在调用期间借用 key 中的视图。insert 必须深拷贝 function key 与 + * logical program counter 字节;lease.snapshot 的视图指向 repository + * 拥有的不可变副本,并只在 lease 释放前有效。find 必须先确认完整 source + * revision 仍匹配,不能只比较 generation 或复用旧 Program 的 body 地址。 + */ + +typedef enum runtimec_compilation_repository_lookup_result_t { + RUNTIMEC_COMPILATION_REPOSITORY_LOOKUP_INVALID = 0, + RUNTIMEC_COMPILATION_REPOSITORY_LOOKUP_FOUND = 1, + RUNTIMEC_COMPILATION_REPOSITORY_LOOKUP_NOT_FOUND = 2 +} runtimec_compilation_repository_lookup_result_t; + +typedef struct runtimec_compilation_repository_lease_t { + const binrwir_compilation_t *compilation; + runtimec_snapshot_t snapshot; + void *token; +} runtimec_compilation_repository_lease_t; + +runtimec_status_t runtimec_compilation_repository_create( + runtimec_allocator_t allocator, + size_t capacity, + runtimec_compilation_repository_t **output_repository, + runtimec_error_t *error); + +runtimec_status_t runtimec_compilation_repository_destroy( + runtimec_compilation_repository_t *repository); + +runtimec_status_t runtimec_compilation_repository_find( + runtimec_compilation_repository_t *repository, + const runtimec_compilation_repository_key_t *key, + runtimec_compilation_repository_lookup_result_t *output_lookup_result, + runtimec_compilation_repository_lease_t *output_lease, + runtimec_error_t *error); + +runtimec_status_t runtimec_compilation_repository_insert( + runtimec_compilation_repository_t *repository, + const runtimec_compilation_repository_key_t *key, + binrwir_compilation_t *compilation, + runtimec_compilation_repository_lease_t *output_lease, + runtimec_error_t *error); + +runtimec_status_t runtimec_compilation_repository_release( + runtimec_compilation_repository_t *repository, + runtimec_compilation_repository_lease_t *lease); + +runtimec_status_t runtimec_compilation_repository_invalidate( + runtimec_compilation_repository_t *repository, + const runtimec_snapshot_t *snapshot); + +runtimec_status_t runtimec_compilation_repository_clear( + runtimec_compilation_repository_t *repository); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/engine/route.c b/runtimec/engine/route.c new file mode 100644 index 00000000..520b9a63 --- /dev/null +++ b/runtimec/engine/route.c @@ -0,0 +1,37 @@ +/* + * 组件:engine/route 的实现 + * 作用:唯一执行策略入口;当前 fail-closed,返回 NOT_IMPLEMENTED 并 + * 把结构化 execution exit 标为 ERROR。 + * 联系:这是唯一同时看到 vmkv、vmbin 与 BinRWIR JIT 的文件,并且只有这里 + * 可以读取 BinRWIR Compiler 已确定的执行目标和提交 checkpoint。执行期 + * 不重新编译、解析 argaddr,所选入口失败后也不切换至另一执行器。 + */ + +#include "binrwir_compiler/compiler.h" +#include "binrwir_compiler/jit/executor.h" +#include "engine/compilation_repository.h" +#include "engine/route.h" +#include "vmbin/interpreter.h" +#include "vmkv/encoder.h" + +runtimec_status_t runtimec_route_execute( + runtimec_session_t *session, + const runtimec_route_request_t *request, + runtimec_route_result_t *output_result, + runtimec_error_t *error) { + (void)session; + (void)request; + (void)error; + if (output_result != NULL) { + *output_result = (runtimec_route_result_t){0}; + output_result->status = RUNTIMEC_STATUS_INVALID; + output_result->execution_exit.status = RUNTIMEC_STATUS_INVALID; + output_result->execution_exit.kind = BINRWIR_EXECUTION_EXIT_ERROR; + } + if (session == NULL || request == NULL || output_result == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + output_result->status = RUNTIMEC_STATUS_NOT_IMPLEMENTED; + output_result->execution_exit.status = RUNTIMEC_STATUS_NOT_IMPLEMENTED; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/engine/route.h b/runtimec/engine/route.h new file mode 100644 index 00000000..98b44eb4 --- /dev/null +++ b/runtimec/engine/route.h @@ -0,0 +1,61 @@ +#ifndef RUNTIMEC_ENGINE_ROUTE_H +#define RUNTIMEC_ENGINE_ROUTE_H + +/* + * 组件:engine/route + * 作用:唯一执行策略所有者:取权威快照 -> 查 compilation repository -> + * 未命中则 vmkv 等价编码 canonical BinRWIR -> BinRWIR Compiler 编译 -> + * 执行前读取 binrwir_execution_target_t -> + * BINRWIR_EXECUTION_TARGET_JIT 只调用 binrwir_jit_execute,VMBIN + * 目标只调用 vmbin_execute -> 只提交成功指令的 + * effects/checkpoint -> 观察 status/source generation -> 继续、暂停、 + * handoff、done、invalidate 或 error。 + * 联系:只有 route.c 同时包含 vmkv、vmbin 与 BinRWIR JIT;host port 只经本层 + * 提交 durable effect;公共接口层把请求翻译成本层请求。 + * 约束:NOT_COMPILABLE 是已完成的编译判定并选择 VMBIN;ERROR 直接失败。 + * 执行前必须确认 repository lease 的完整 source revision 仍有效。 + * 执行期不重新编译、不重新解析地址、不重新选择目标,也不允许所选入口 + * 失败后切换至另一执行器。其它文件不得绕过 route 自行选择执行器或提交 + * checkpoint。 + */ + +#include +#include +#include "contract/status.h" +#include "contract/view.h" +#include "engine/session.h" +#include "binrwir/execution_exit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum runtimec_route_kind_t { + RUNTIMEC_ROUTE_FUNCTION = 0, + RUNTIMEC_ROUTE_PROGRAM_COUNTER = 1, + RUNTIMEC_ROUTE_VTHREAD = 2, + RUNTIMEC_ROUTE_BOOTSTRAP = 3 +} runtimec_route_kind_t; +typedef struct runtimec_route_request_t { + runtimec_route_kind_t kind; + runtimec_string_view_t target; + const runtimec_string_view_t *arguments; + size_t argument_count; + uint64_t instruction_budget; +} runtimec_route_request_t; +typedef struct runtimec_route_result_t { + runtimec_status_t status; + binrwir_execution_exit_t execution_exit; + runtimec_string_view_t return_value; +} runtimec_route_result_t; +runtimec_status_t runtimec_route_execute( + runtimec_session_t *session, + const runtimec_route_request_t *request, + runtimec_route_result_t *output_result, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/engine/session.c b/runtimec/engine/session.c new file mode 100644 index 00000000..f6d0dded --- /dev/null +++ b/runtimec/engine/session.c @@ -0,0 +1,65 @@ +/* engine/session 的失败封闭骨架。 */ + +#include "engine/session.h" + +#include "binrwir_compiler/compiler.h" +#include "engine/compilation_repository.h" +#include "vmkv/encoder.h" + +struct runtimec_session_t { + runtimec_host_services_t host_services; + runtimec_session_options_t options; + vmkv_encoder_t *vmkv_encoder; + binrwir_compiler_t *binrwir_compiler; + runtimec_compilation_repository_t *compilation_repository; +}; + +runtimec_status_t runtimec_session_create( + const runtimec_host_services_t *host_services, + const runtimec_session_options_t *options, + runtimec_session_t **output_session, + runtimec_error_t *error) { + (void)host_services; + (void)options; + (void)error; + if (output_session != NULL) { + *output_session = NULL; + } + if (!runtimec_host_services_is_valid(host_services) || options == NULL || + options->structure_size != sizeof(*options) || + !runtimec_allocator_is_valid(&options->allocator) || + options->compilation_cache_capacity == 0U || + (options->enable_jit && options->machine_code_capacity == 0U) || + (!options->enable_jit && options->machine_code_capacity != 0U) || + output_session == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_session_destroy(runtimec_session_t *session) { + if (session == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_session_get_host_services( + runtimec_session_t *session, + const runtimec_host_services_t **output_host_services) { + if (output_host_services != NULL) { + *output_host_services = NULL; + } + if (session == NULL || output_host_services == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t runtimec_session_clear_derived_state( + runtimec_session_t *session) { + if (session == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/engine/session.h b/runtimec/engine/session.h new file mode 100644 index 00000000..603e5a31 --- /dev/null +++ b/runtimec/engine/session.h @@ -0,0 +1,53 @@ +#ifndef RUNTIMEC_ENGINE_SESSION_H +#define RUNTIMEC_ENGINE_SESSION_H + +/* + * 组件:engine/session + * 作用:显式 owner graph 的根:host services、vmkv encoder、BinRWIR + * Compiler、compilation repository 与 configuration。JIT compiler 与 + * 机器码存储完全封装在 BinRWIR Compiler 内,不是 session 的直接 owner。 + * BinRWIR/VMBIN 不拥有额外的执行状态池或 V.body cache。禁止用 + * function-static、global 或 thread-local storage 隐藏 owner。 + * 联系:公共接口层创建 session;route 在 session 上执行;多 + * session/测试隔离都从本对象出发。 + * 约束:每条逻辑 instruction 完成前写回全部 V.body,并建立恢复边界,这是 + * 无条件语义,不由 session option 开关控制。当前 scaffold 不宣称 handle + * 线程安全;并发协议必须由 session 的锁序与 generation/lease 设计提供。 + */ + +#include +#include +#include +#include "contract/allocator.h" +#include "contract/host_services.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_session_t runtimec_session_t; +typedef struct runtimec_session_options_t { + uint32_t structure_size; + runtimec_allocator_t allocator; + size_t compilation_cache_capacity; + size_t machine_code_capacity; + bool enable_jit; +} runtimec_session_options_t; +runtimec_status_t runtimec_session_create( + const runtimec_host_services_t *host_services, + const runtimec_session_options_t *options, + runtimec_session_t **output_session, + runtimec_error_t *error); +runtimec_status_t runtimec_session_destroy(runtimec_session_t *session); +runtimec_status_t runtimec_session_get_host_services( + runtimec_session_t *session, + const runtimec_host_services_t **output_host_services); +runtimec_status_t runtimec_session_clear_derived_state( + runtimec_session_t *session); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/host/host.c b/runtimec/host/host.c new file mode 100644 index 00000000..f235bd3f --- /dev/null +++ b/runtimec/host/host.c @@ -0,0 +1,121 @@ +/* host 的严格生命周期与能力表借用。 */ + +#include "host/host_internal.h" + +#include +#include + +static runtimec_status_t runtimec_host_fail( + runtimec_error_t *error, runtimec_status_t status, + const char *message) { + runtimec_error_set( + error, status, RUNTIMEC_ERROR_DOMAIN_HOST, 0, + runtimec_string_view_from_c_string(message), + runtimec_string_view_make(NULL, 0U)); + return status; +} + +runtimec_status_t runtimec_host_create( + const runtimec_host_services_t *services, runtimec_host_t **output, + runtimec_error_t *error) { + runtimec_error_reset(error); + if (output != NULL) { + *output = NULL; + } + if (services == NULL || output == NULL || error == NULL || + !runtimec_host_services_is_valid(services)) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid host create request"); + } + runtimec_host_t *host = runtimec_allocator_allocate( + &services->allocator, sizeof(*host), alignof(runtimec_host_t)); + if (host == NULL) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_OUT_OF_MEMORY, + "host allocation failed"); + } + host->services = *services; + *output = host; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t runtimec_host_destroy( + runtimec_host_t *host, runtimec_error_t *error) { + runtimec_error_reset(error); + if (host == NULL || error == NULL || + !runtimec_host_services_is_valid(&host->services)) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid host destroy request"); + } + runtimec_allocator_t allocator = host->services.allocator; + memset(host, 0, sizeof(*host)); + runtimec_status_t deallocation_status = runtimec_allocator_deallocate( + &allocator, host, sizeof(*host), alignof(runtimec_host_t)); + if (deallocation_status != RUNTIMEC_STATUS_SUCCESS) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_ERROR, + "host deallocation violated the allocator contract"); + } + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t runtimec_host_get_services( + const runtimec_host_t *host, + const runtimec_host_services_t **output, runtimec_error_t *error) { + runtimec_error_reset(error); + if (output != NULL) { + *output = NULL; + } + if (host == NULL || output == NULL || error == NULL || + !runtimec_host_services_is_valid(&host->services)) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid host services request"); + } + *output = &host->services; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t runtimec_host_resolve_static_xvalue_address( + const runtimec_host_t *host, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, + runtimec_xvalue_address_t *output_xvalue_address, + runtimec_error_t *error) { + runtimec_error_reset(error); + if (output_xvalue_address != NULL) { + *output_xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + } + if (host == NULL || snapshot == NULL || output_xvalue_address == NULL || + error == NULL || !runtimec_host_services_is_valid(&host->services) || + !runtimec_snapshot_is_valid(snapshot) || + !runtimec_key_view_is_valid(key) || key.size == 0U) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid static XValue address request"); + } + if (host->services.resolve_static_xvalue_address == NULL) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_NOT_IMPLEMENTED, + "static XValue address resolution is not implemented"); + } + + runtimec_xvalue_address_t resolved_address = + RUNTIMEC_XVALUE_ADDRESS_NONE; + runtimec_status_t status = + host->services.resolve_static_xvalue_address( + host->services.context, snapshot, key, &resolved_address, error); + if (status != RUNTIMEC_STATUS_SUCCESS) { + *output_xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + return status; + } + if (resolved_address == RUNTIMEC_XVALUE_ADDRESS_NONE) { + return runtimec_host_fail( + error, RUNTIMEC_STATUS_ERROR, + "host returned an invalid static XValue address"); + } + + *output_xvalue_address = resolved_address; + return RUNTIMEC_STATUS_SUCCESS; +} diff --git a/runtimec/host/host.h b/runtimec/host/host.h new file mode 100644 index 00000000..3c4f072c --- /dev/null +++ b/runtimec/host/host.h @@ -0,0 +1,39 @@ +#ifndef RUNTIMEC_HOST_HOST_H +#define RUNTIMEC_HOST_HOST_H + +/* + * host 是 runtimec 对外部能力的唯一所有者。它只持有一份经过精确版本 + * 校验的能力表,不拥有 BinRWIR,不参与 BinRWIR Compiler 的 JIT/VMBIN + * 执行目标选择,也不提交 checkpoint。它是静态 key 到稳定 XValue 基址解析 + * 合同的唯一入口;基址只借用到 snapshot source revision 失效为止。 + */ + +#include "contract/error.h" +#include "contract/host_services.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_host runtimec_host_t; + +runtimec_status_t runtimec_host_create( + const runtimec_host_services_t *services, runtimec_host_t **output, + runtimec_error_t *error); +runtimec_status_t runtimec_host_destroy( + runtimec_host_t *host, runtimec_error_t *error); +runtimec_status_t runtimec_host_get_services( + const runtimec_host_t *host, + const runtimec_host_services_t **output, runtimec_error_t *error); +runtimec_status_t runtimec_host_resolve_static_xvalue_address( + const runtimec_host_t *host, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, + runtimec_xvalue_address_t *output_xvalue_address, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/host/host_internal.h b/runtimec/host/host_internal.h new file mode 100644 index 00000000..831be30b --- /dev/null +++ b/runtimec/host/host_internal.h @@ -0,0 +1,10 @@ +#ifndef RUNTIMEC_HOST_HOST_INTERNAL_H +#define RUNTIMEC_HOST_HOST_INTERNAL_H + +#include "host/host.h" + +struct runtimec_host { + runtimec_host_services_t services; +}; + +#endif diff --git a/runtimec/host/kvspace_adapter.c b/runtimec/host/kvspace_adapter.c new file mode 100644 index 00000000..177354b4 --- /dev/null +++ b/runtimec/host/kvspace_adapter.c @@ -0,0 +1,30 @@ +/* kvspace adapter(包括静态 XValue 基址解析)的失败封闭骨架。 */ + +#include "host/kvspace_adapter.h" + +static runtimec_status_t runtimec_kvspace_adapter_fail( + runtimec_error_t *error, runtimec_status_t status, + const char *message) { + runtimec_error_set( + error, status, RUNTIMEC_ERROR_DOMAIN_STORAGE, 0, + runtimec_string_view_from_c_string(message), + runtimec_string_view_make(NULL, 0U)); + return status; +} + +runtimec_status_t runtimec_kvspace_adapter_create_services( + const runtimec_kvspace_adapter_request_t *request, + runtimec_host_services_t *output, runtimec_error_t *error) { + runtimec_error_reset(error); + runtimec_host_services_initialize(output); + if (request == NULL || output == NULL || error == NULL || + request->borrowed_kvspace_handle == NULL || + !runtimec_allocator_is_valid(&request->allocator)) { + return runtimec_kvspace_adapter_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid kvspace adapter request"); + } + return runtimec_kvspace_adapter_fail( + error, RUNTIMEC_STATUS_NOT_IMPLEMENTED, + "kvspace adapter is not implemented"); +} diff --git a/runtimec/host/kvspace_adapter.h b/runtimec/host/kvspace_adapter.h new file mode 100644 index 00000000..ab129d5d --- /dev/null +++ b/runtimec/host/kvspace_adapter.h @@ -0,0 +1,32 @@ +#ifndef RUNTIMEC_HOST_KVSPACE_ADAPTER_H +#define RUNTIMEC_HOST_KVSPACE_ADAPTER_H + +/* + * kvspace adapter 只把一个借用的 kvspace 句柄转换为 host 能力表,包括 + * 静态 key 的稳定 XValue 基址解析。当前尚未连接 kvspace C 函数,因此 + * 唯一入口明确返回 NOT_IMPLEMENTED,不发布空解析器伪装成功。 + */ + +#include "contract/allocator.h" +#include "contract/error.h" +#include "contract/host_services.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct runtimec_kvspace_adapter_request { + runtimec_allocator_t allocator; + void *borrowed_kvspace_handle; +} runtimec_kvspace_adapter_request_t; + +runtimec_status_t runtimec_kvspace_adapter_create_services( + const runtimec_kvspace_adapter_request_t *request, + runtimec_host_services_t *output, runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/include/kvlang/runtimec.h b/runtimec/include/kvlang/runtimec.h new file mode 100644 index 00000000..de132405 --- /dev/null +++ b/runtimec/include/kvlang/runtimec.h @@ -0,0 +1,105 @@ +#ifndef KVLANG_RUNTIMEC_H +#define KVLANG_RUNTIMEC_H + +/* + * runtimec 的唯一公共 C 接口。 + * + * 公共层只负责创建 runtimec、提交一次执行请求和销毁 runtimec。RWIR、 + * BinRWIR、vmkv、vmbin、BinRWIR Compiler 与 kvspace 的内部结构都不在此暴露。 + * 所有可失败调用返回 runtimec_status_t,输出对象在失败前先进入无效状态, + * 错误写入调用方提供的缓冲区。当前骨架只会明确返回未实现,不伪造成功。 + */ + +#include +#include + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(KVLANG_RUNTIMEC_BUILDING_LIBRARY) +# define KVLANG_RUNTIMEC_EXPORT __declspec(dllexport) +# else +# define KVLANG_RUNTIMEC_EXPORT __declspec(dllimport) +# endif +#elif defined(__GNUC__) || defined(__clang__) +# define KVLANG_RUNTIMEC_EXPORT __attribute__((visibility("default"))) +#else +# define KVLANG_RUNTIMEC_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum runtimec_status { + RUNTIMEC_STATUS_SUCCESS = 0, + RUNTIMEC_STATUS_YIELD = 1, + RUNTIMEC_STATUS_WAIT = 2, + RUNTIMEC_STATUS_UNSUPPORTED = 3, + RUNTIMEC_STATUS_NOT_IMPLEMENTED = 4, + RUNTIMEC_STATUS_INVALID = 5, + RUNTIMEC_STATUS_OUT_OF_MEMORY = 6, + RUNTIMEC_STATUS_ERROR = 7, + RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY = 8 +} runtimec_status_t; + +typedef struct kvlang_runtime kvlang_runtime_t; + +typedef struct kvlang_runtime_text_buffer { + char *data; /* 调用方拥有;capacity 为 0 时可以为 NULL。 */ + size_t capacity; /* 包含字符串终止空字符的可写字节数。 */ + size_t length; /* 实际写入字节数,不含字符串终止空字符。 */ + size_t required_length; /* 完整内容字节数,不含字符串终止空字符。 */ +} kvlang_runtime_text_buffer_t; + +/* + * message 容量不足时不写入半条诊断:函数返回 + * RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY,status 与返回值一致, + * operation_status 保存原始失败,required_length 给出完整消息长度。 + */ +typedef struct kvlang_runtime_error { + runtimec_status_t status; /* 与函数返回状态完全一致。 */ + runtimec_status_t operation_status; /* 容量失败时保存原始操作状态。 */ + int64_t code; + kvlang_runtime_text_buffer_t message; +} kvlang_runtime_error_t; + +typedef struct kvlang_runtime_create_request { + const char *data_source_name; +} kvlang_runtime_create_request_t; + +typedef enum kvlang_runtime_entry_kind { + KVLANG_RUNTIME_ENTRY_FUNCTION = 0, + KVLANG_RUNTIME_ENTRY_PROGRAM_COUNTER = 1, + KVLANG_RUNTIME_ENTRY_VTHREAD = 2, + KVLANG_RUNTIME_ENTRY_BOOTSTRAP = 3 +} kvlang_runtime_entry_kind_t; + +typedef struct kvlang_runtime_execute_request { + kvlang_runtime_entry_kind_t entry_kind; + const char *entry_name; + const char *const *arguments; + size_t argument_count; +} kvlang_runtime_execute_request_t; + +typedef struct kvlang_runtime_execute_result { + kvlang_runtime_text_buffer_t value; + kvlang_runtime_text_buffer_t program_counter; +} kvlang_runtime_execute_result_t; + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_create( + const kvlang_runtime_create_request_t *request, + kvlang_runtime_t **output, kvlang_runtime_error_t *error); + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_destroy( + kvlang_runtime_t *runtime, kvlang_runtime_error_t *error); + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_execute( + kvlang_runtime_t *runtime, + const kvlang_runtime_execute_request_t *request, + kvlang_runtime_execute_result_t *output, + kvlang_runtime_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/interface/exports.txt b/runtimec/interface/exports.txt new file mode 100644 index 00000000..94e6dc72 --- /dev/null +++ b/runtimec/interface/exports.txt @@ -0,0 +1,3 @@ +kvlang_runtime_create +kvlang_runtime_destroy +kvlang_runtime_execute diff --git a/runtimec/interface/public_interface.c b/runtimec/interface/public_interface.c new file mode 100644 index 00000000..46e70898 --- /dev/null +++ b/runtimec/interface/public_interface.c @@ -0,0 +1,111 @@ +/* runtimec 公共 C 接口的失败封闭骨架。 */ + +#include + +#include + +static int kvlang_runtime_text_buffer_is_valid( + const kvlang_runtime_text_buffer_t *buffer) { + return buffer != NULL && + (buffer->capacity == 0U || buffer->data != NULL); +} + +static void kvlang_runtime_text_buffer_clear( + kvlang_runtime_text_buffer_t *buffer) { + if (buffer != NULL) { + buffer->length = 0U; + buffer->required_length = 0U; + if (buffer->data != NULL && buffer->capacity != 0U) { + buffer->data[0] = '\0'; + } + } +} + +static runtimec_status_t kvlang_runtime_fail( + kvlang_runtime_error_t *error, runtimec_status_t status, + const char *message) { + if (error == NULL) { + return status; + } + error->status = status; + error->operation_status = status; + error->code = 0; + if (!kvlang_runtime_text_buffer_is_valid(&error->message)) { + error->status = RUNTIMEC_STATUS_INVALID; + error->operation_status = RUNTIMEC_STATUS_INVALID; + error->message.length = 0U; + error->message.required_length = 0U; + return RUNTIMEC_STATUS_INVALID; + } + kvlang_runtime_text_buffer_clear(&error->message); + if (message == NULL) { + return status; + } + size_t message_length = strlen(message); + error->message.required_length = message_length; + if (error->message.capacity <= message_length) { + error->status = RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY; + return RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY; + } + memcpy(error->message.data, message, message_length + 1U); + error->message.length = message_length; + return status; +} + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_create( + const kvlang_runtime_create_request_t *request, + kvlang_runtime_t **output, kvlang_runtime_error_t *error) { + if (output != NULL) { + *output = NULL; + } + if (request == NULL || output == NULL || error == NULL || + !kvlang_runtime_text_buffer_is_valid(&error->message) || + request->data_source_name == NULL || + request->data_source_name[0] == '\0') { + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid runtimec create request"); + } + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_NOT_IMPLEMENTED, + "runtimec creation is not implemented"); +} + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_destroy( + kvlang_runtime_t *runtime, kvlang_runtime_error_t *error) { + if (runtime == NULL || error == NULL || + !kvlang_runtime_text_buffer_is_valid(&error->message)) { + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid runtimec destroy request"); + } + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_NOT_IMPLEMENTED, + "runtimec destruction is not implemented"); +} + +KVLANG_RUNTIMEC_EXPORT runtimec_status_t kvlang_runtime_execute( + kvlang_runtime_t *runtime, + const kvlang_runtime_execute_request_t *request, + kvlang_runtime_execute_result_t *output, + kvlang_runtime_error_t *error) { + if (output != NULL) { + kvlang_runtime_text_buffer_clear(&output->value); + kvlang_runtime_text_buffer_clear(&output->program_counter); + } + if (runtime == NULL || request == NULL || output == NULL || error == NULL || + !kvlang_runtime_text_buffer_is_valid(&error->message) || + !kvlang_runtime_text_buffer_is_valid(&output->value) || + !kvlang_runtime_text_buffer_is_valid(&output->program_counter) || + request->entry_kind < KVLANG_RUNTIME_ENTRY_FUNCTION || + request->entry_kind > KVLANG_RUNTIME_ENTRY_BOOTSTRAP || + request->entry_name == NULL || request->entry_name[0] == '\0' || + (request->argument_count != 0U && request->arguments == NULL)) { + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_INVALID, + "invalid runtimec execute request"); + } + return kvlang_runtime_fail( + error, RUNTIMEC_STATUS_NOT_IMPLEMENTED, + "runtimec execution is not implemented"); +} diff --git a/runtimec/specification/README.md b/runtimec/specification/README.md new file mode 100644 index 00000000..98cb58bd --- /dev/null +++ b/runtimec/specification/README.md @@ -0,0 +1,57 @@ +# runtimec specification + +本目录是 runtimec 语言行为、RWIR operation facts 与 myrwircap 到 canonical BinRWIR +映射的唯一入口。RWIR 是 kvspace 中包含 operation name、参数类型和参数名的明文可恢复 +格式;BinRWIR 是当前进程中只含 `opid` 与重复 `argidx + uint64 argaddr` 的二进制派生格式。 +本目录不保存执行策略,也不把进程内地址编码冻结为持久化格式。 + +- `five-language-behavior-matrix.md`:语言行为决策的唯一事实源。 +- `myrwircap-binrwir-mapping.md`:每个 capability 到 canonical operation 的规范合同。 +- `rwir_catalog.def`:稳定 operation identifier、规范名称、参数结构、canonical 映射与 + 行为决策引用的唯一 manifest 输入。 +- `scalar_operations.def`:已裁决行为到标量 execution family 的派生映射输入。 + +## Required order + +1. 在五语言矩阵中记录 C、Python、Rust、Go、V8/TypeScript 行为、kvlang 选择、边界 + fixture 与非法输入规则。 +2. 为 myrwircap 分配显式稳定 operation identifier。identifier 不得来自数组下标、文件 + 顺序、声明顺序或生成顺序。 +3. 在 `rwir_catalog.def` 中写入 operation name 到 `opid`、参数类型/名称到 `argidx` 的唯一 + canonical 映射与 `behavior-decision` 引用。 +4. 由 vmkv 消费 manifest 并验证明文 RWIR;host 将每个静态参数 key 解引用为非零 64 位 + XValue 基址,vmkv 验证固定 8 字节 XValue head ABI、检查加法溢出,并且唯一执行 + `argaddr = xvalue_address + 8` 后等价生成 canonical BinRWIR。 +5. 由 `binrwir_compiler/` 从 canonical Program 生成 execution plan 和 compilation;JIT + 仅作为该组件的内部编译实现参与目标判定。 + +所有 execution target 都必须保留每条 logical instruction completion 这一不可关闭的 +recovery/observable boundary。JIT 只在当前逻辑指令完整完成后提交全部 completed dirty +state;guard failure、error、recovery 或 resume 必须丢弃当前未完成指令的 speculative/ +partial register 与 spill state,不能写回。logical program counter/checkpoint 仅在完成 +状态全部写回成功后推进。 + +每个 myrwircap 必须恰好出现一次。缺失映射、重复 capability、重复稳定 identifier、参数 +方向不一致、行为引用不存在或任何歧义都必须成为编译错误。禁止默认 operation、顺序推导、 +兼容别名和静默修复。 + +当前只允许 XValue 基址生命周期长且可验证的静态 key,例如 vthread `pc`、`status` 与 +`/lib` 函数源。host 只能返回 XValue 基址,不能任意决定 body 偏移;BinRWIR 与 vmbin 只 +保存或消费 vmkv 派生的最终 `argaddr`,不缓存 XValue 或 `V.body`。零 XValue 基址、`+8` +溢出、非 8 字节 head ABI、动态 key 或无法证明生命周期均失败封闭。map、struct 等动态 +拼接 key 在 key/xvalue 生命周期 ABI 建立前必须返回 `RUNTIMEC_STATUS_UNSUPPORTED`;禁止 +零 `argaddr`、延迟 key lookup 或间接 fallback。 + +当前两个 `.def` manifest 不含 operation 记录。未来所有生成表必须来自这两个输入,禁止 +复制第二张手写表。 + +## #622 alignment + +`kvlang #622` 是语义与实现顺序的总纲。已裁决的六项语义(uint64→float、int→float32、 +混合有/无符号、浮点除零、None 判据、无符号 `neg`/`abs`/`sign`)和两项正确性裁决 +(浮点转整数饱和与 NaN TypeError、浮点成员键格式)必须先写入 +`five-language-behavior-matrix.md`,再由实现消费。`rwir_catalog.def` 是 opcode 与 signature +的单一来源;CI 必须断言没有可与它漂移的第二份表。检查点粒度保持逐指令 `pc` 与 +`status`,`mem_frame` / `segment_may_stop` 不进入 runtimec。所有派生缓存都可从 kvspace +明文 RWIR 与当前 `V.body` 删除重建;AArch64 与 x86_64 是 JIT 后端 peer,不得把 AArch64 +留成长期空桩。 diff --git a/runtimec/specification/five-language-behavior-matrix.md b/runtimec/specification/five-language-behavior-matrix.md new file mode 100644 index 00000000..d76c62c7 --- /dev/null +++ b/runtimec/specification/five-language-behavior-matrix.md @@ -0,0 +1,47 @@ +# 五语言行为决策矩阵 + + + +本文件是 runtimec 语言行为决策的唯一事实源,不是实现进度表。任何行为进入 +`specification/`、`vmkv/`、`vmbin/` 或 `binrwir_compiler/jit/` 前,必须先在这里形成完整 +决策。五种语言一致时,kvlang 遵从共同直觉;五种语言分裂时,必须明确选择 +阵营、边界样例和非法输入结果。 + +`tools/architecture/check_behavior_matrix.py` 校验表结构,并要求产品代码中的 +`behavior-decision: ` 标记引用本表已有决策。没有标记的非占位 +specification manifest 条目直接失败。 + +一个完整 decision 只能通过 `rwir_catalog.def` 中恰好一条 myrwircap mapping 进入 +canonical BinRWIR。mapping 必须使用显式稳定 operation identifier;数组下标、文件顺序、 +声明顺序和生成顺序都不能定义 operation identity。缺失、重复或歧义 mapping 是编译错误。 + +| Decision | Behavior | C | Python | Rust | Go | V8/TypeScript | Agreement | kvlang decision | Boundary fixture | Invalid input | Design record | +|---|---|---|---|---|---|---|---|---|---|---|---| +| integer-no-float-roundtrip | Integer calculation does not pass through floating point | integer operators stay in integer domains | integer operators stay in integer domains | integer operators stay in integer domains | integer operators stay in integer domains | BigInt operators stay in the BigInt domain | consensus | Keep integer calculation in an integer representation from input through result | values above 2^53 retain exact integer results | reject mixed representations unless that operation has its own matrix row | runtimec principle 7 | +| floating-to-integer-truncation | Floating-point to integer conversion truncates toward zero inside the target range and saturates outside it | representable values truncate; out-of-range is undefined | `int` truncates for representable values | `as` truncates and saturates | conversion truncates; out-of-range is implementation-dependent | `Math.trunc` produces a Number, not a fixed-width integer | split | Truncate toward zero inside the target width; saturate to target width for out-of-range and ±Inf; NaN is a TypeError | `1e300` to `int8` saturates to `INT8_MAX`; `-3.9` becomes `-3` | NaN conversion returns TypeError instead of a numeric result | kvlang #622 4.3.1 | +| signed-integer-division | Division of signed integers with a nonzero divisor | quotient truncates toward zero | quotient floors | quotient truncates toward zero | quotient truncates toward zero | Number division is not integer division | split | Follow C, Rust, and Go: truncate the quotient toward zero | `-7 / 3` becomes `-2` | zero divisor returns `RUNTIMEC_STATUS_INVALID` | runtimec principle 7 | +| mixed-numeric-equality | Equality between integer `3` and floating point `3.0` | integer converts for numeric comparison | equal | explicit conversion required between concrete types | concrete typed values require matching types | equal under numeric equality | split | Follow C numeric conversion and compare exact represented values | `3 == 3.0` is true; `9007199254740993` is never rounded through floating point | reject a comparison when exact conversion cannot be represented | runtimec principle 7 | +| character-result | A character-producing operation returns one language value | `char` is an integer type | one-character string | `char` is a Unicode scalar value | rune is an alias of `int32` | one-code-unit string | split | Follow Python and V8/TypeScript: return a string value | code point U+0041 returns `"A"` | reject an invalid Unicode scalar value | runtimec principle 7 | +| read-write-alias | One call reads and writes the same storage through separate parameters | pointer aliasing can expose writes to later reads | object aliasing exposes mutation | explicit borrowing controls aliasing | slice aliasing exposes writes | object aliasing exposes mutation | split | RWIR permits declared read/write aliasing; later reads observe ordered writes | bind `a` and `a_out` to one array during in-place quicksort | undeclared aliasing returns `RUNTIMEC_STATUS_INVALID` | RWIR Read/Write Alias Rule | +| uint64-to-float-promotion | Unsigned 64-bit integer to floating point conversion uses the unsigned numeric value | conversion reads the unsigned value, not a sign-reinterpreted integer | `int` to `float` keeps the numeric value | `as f64` reads the unsigned value | `float64(uint64)` reads the unsigned value | `Number` or `BigInt` conversion keeps the sign | consensus | Convert `uint64` as an unsigned value, never through `int64` | `2^63 + 1` becomes a positive float, never a negative one | none; every finite `uint64` converts without signed reinterpretation | kvlang #622 4.1.1 | +| int-to-float32-single-rounding | Integer to float32 conversion rounds once to the nearest float32 | direct cast to `float` rounds once | no native float32; arithmetic uses float64 | `as f32` rounds once | `float32(int)` rounds once | `Math.fround` rounds once | split | Round once to the float32 target; never round through float64 | an integer whose f64-then-f32 double rounding differs converts through one f32 rounding | none | kvlang #622 4.1.2 | +| mixed-signed-unsigned-arithmetic | Mixed signed and unsigned arithmetic and bitwise operations | usual arithmetic conversions apply | arbitrary-precision integers mix freely | mixed operands require an explicit cast | mixed operands require an explicit conversion | `Number` and `BigInt` do not mix | split | Arithmetic and bitwise operations reject mixed signs; comparisons use exact mathematical values | `u64 + i64` and `u64 & i64` are rejected; comparing `-1` with `UINT64_MAX` makes `-1` smaller | mixed arithmetic or bitwise operands return `RUNTIMEC_STATUS_INVALID`; untyped literal adaptation is unaffected | kvlang #622 4.1.3 | +| float-division-by-zero | Floating-point division by zero | IEEE 754 infinities and NaN | raises `ZeroDivisionError` | IEEE 754 infinities and NaN | IEEE 754 infinities and NaN | IEEE 754 `Infinity` and `NaN` | split | Follow C, Rust, Go, and V8/TypeScript: IEEE 754 infinities and NaN | `1.0 / 0.0` is `+inf`; `-1.0 / 0.0` is `-inf`; `0.0 / 0.0` is `NaN` | none; a zero divisor is valid in floating point | kvlang #622 4.1.4 | +| none-criterion | What counts as the None value | a null pointer is typed and distinct from an empty object | `None` is distinct from the empty string | `Option::None` is distinct from empty values | a nil pointer or interface is distinct from empty values | `null` and `undefined` are distinct from the empty string | consensus | None exactly when langtype is None or a pointer type and the body is empty; an empty string is not None | an empty string with string langtype is a normal value; a pointer langtype with empty body is None | None or pointer langtype with a non-empty body returns `RUNTIMEC_STATUS_INVALID` | kvlang #622 4.1.5 | +| unsigned-neg-abs-sign | Unary neg, abs, and sign for unsigned values | neg wraps; abs and sign are ordinary functions | integers are signed by default | neg wraps; unsigned values do not define abs | neg wraps; unsigned values have no abs concept | `BigInt` neg is exact and sign follows the value | split | neg wraps modulo the width; abs is identity; sign reports by unsigned value | `neg(UINT64_MAX)` is `1`; `abs(UINT64_MAX)` is `UINT64_MAX`; `sign(UINT64_MAX)` is positive | none | kvlang #622 4.1.6 | +| float-member-key-identity | Float-to-text used for member keys and display | formatting differs by implementation | `repr` keeps the `.0` and NaN/inf spellings | `Display` drops integral `.0` and prints NaN/inf | formatting drops integral `.0` and prints NaN/Inf | `String(number)` drops integral `.0` and prints NaN/Infinity | split | Shortest round-trip; integral values drop `.0`; `-0.0` normalizes to `0.0`; exponent below `-4` or above `15` uses exponent form with two digits; NaN is rejected as a key while ±Inf is allowed; display uses `nan`/`inf`/`-inf` | `1.0` and `1` share one key; `-0.0` and `0.0` share one key; `Inf` keys are allowed | NaN member key returns `RUNTIMEC_STATUS_INVALID` | kvlang #622 4.3.2 | + +## Required workflow + +1. Add one complete row with a stable Decision value. +2. Record all five language behaviors; `unknown` and placeholder text are forbidden. +3. Choose `consensus` or `split` and state the exact kvlang behavior. +4. Add a boundary fixture and an invalid-input result. +5. Add `behavior-decision: ` beside the specification or implementation. +6. Only then add executable semantics. + +进入 executable semantics 前,还必须通过 +`myrwircap-binrwir-mapping.md` 定义的集合覆盖、identifier 唯一性和签名等价检查。 + +Changing an established row is a language change. The same change must update fixtures and +its issue or design record; silent reinterpretation is forbidden. diff --git a/runtimec/specification/myrwircap-binrwir-mapping.md b/runtimec/specification/myrwircap-binrwir-mapping.md new file mode 100644 index 00000000..352eff09 --- /dev/null +++ b/runtimec/specification/myrwircap-binrwir-mapping.md @@ -0,0 +1,207 @@ +# myrwircap to addressed BinRWIR mapping + +本规范定义 runtimec 从 `myrwircaps` 与 kvspace 明文 RWIR 进入 canonical BinRWIR 的唯一 +映射。mapping 是进程内编译合同,不是持久化格式,也不是执行器派发表。 + +## 1. Authority chain + +```text +five-language behavior decision + -> one myrwircap specification record + -> RWIR operation name and typed named parameters + -> one canonical opid and stable argidx values + -> host dereferences static keys to nonzero uint64 XValue base addresses + -> vmkv alone validates the fixed 8-byte head and derives argaddr = xvalue_address + 8 + -> addressed canonical BinRWIR +``` + +前一层不完整时,后一层不得产生输出。 + +## 2. RWIR source format + +RWIR 是 kvspace 中的明文、可恢复存储格式。每条调用至少表达: + +- operation name; +- 每个参数的类型; +- 每个参数的名称; +- 每个参数的 Read 或 Write 方向; +- 参数名称对应的 kvspace key。 + +这些明文字段是重建 BinRWIR 的权威输入。它们不复制进热 BinRWIR instruction。 + +## 3. Required mapping record + +每个 myrwircap 必须在 `rwir_catalog.def` 中拥有一条且只有一条记录。每条记录至少包含: + +| Field | Requirement | +|---|---| +| Canonical RWIR operation name | 与 RWIR specification 和 `myrwircaps` 中的名称完全一致 | +| Stable RWIR operation identifier | 显式分配,长期稳定,不依赖任何容器位置 | +| Canonical BinRWIR `opid` | 显式分配;热 instruction 中唯一保留的 operation identity | +| Read parameter contract | 参数数量规则、顺序、类型、名称与读取权限 | +| Write parameter contract | 参数数量规则、顺序、类型、名称与写入权限 | +| Canonical `argidx` | 在该 `opid` signature 中唯一标识每个参数及其方向 | +| Alias policy | 哪些不同 `argidx` 允许、要求或禁止指向同一 body 地址 | +| Behavior decision | 引用五语言矩阵中已存在的稳定 decision | + +record 是 operation 与 parameter identity 的唯一来源。生成表的位置不是 identity。 + +## 4. Canonical binary shape + +vmkv 对一次已确定调用生成的热二进制语义形状只有: + +```text +opid + argidx + uint64 argaddr + argidx + uint64 argaddr + ... +``` + +- `opid` 取代 RWIR operation name。 +- `argidx` 取代热路径中的参数类型、参数名和方向字段;消费者按 `(opid, argidx)` 查询 + canonical signature。 +- 参数 key 解引用得到的是 64 位 XValue 基址,不是任意 `V.body` 地址。 +- XValue head ABI 固定为 8 字节;只有 vmkv 执行带溢出检查的 + `argaddr = xvalue_address + 8`,因此 `argaddr` 直接指向 kvspace `V.body`。 +- BinRWIR 不保存 operation name、参数类型、参数名、key 文本、xvalue 或 body 副本。 +- RWIR 参数数量可以不固定;一次具体调用必须编码全部参数对,不能截断或拆分。 + +公共二进制布局固定为:instruction header 共 4 字节且只含 `opid`;parameter record +在偏移 0 保存 32 位 `argidx`,偏移 4 是必须为零的 32 位地址对齐填充,偏移 8 保存 +64 位 `argaddr`。填充不是语义字段。Read/Write span、source logical program counter 与 +可变记录 framing 是 Program 私有元数据,只通过非二进制 view 暴露,不得成为 +`binrwir_instruction_t` 的字段。 + +BinRWIR 是进程内派生物。进程重启、body relocation 或完整 `snapshot.source_revision` 变化后, +旧 Program 立即无效,必须从 kvspace 中的 RWIR 与当前 body 地址重新生成。 + +## 5. Exact mapping rules + +1. **Total**:runtimec `myrwircaps` 中的每个 capability 都必须有记录。 +2. **Single-valued**:一个 capability 只能产生一个 canonical `opid`。 +3. **Unique identity**:stable RWIR identifier 与 canonical `opid` 都不得重复。 +4. **Stable argument identity**:一个 signature 内每个参数只能有一个 `argidx`,重排其它 + manifest 记录不能改变它。 +5. **Signature preserving**:Read/Write 数量、顺序、类型约束和 alias policy 必须等价。 +6. **Behavior preserving**:canonical operation 必须引用同一个行为决策,不得近似替换。 +7. **Address derivation**:host 只返回非零 64 位 XValue 基址,不能选择 body 偏移;vmkv + 唯一验证固定 8 字节 head ABI,并以溢出检查执行 `xvalue_address + 8`。 +8. **Address validity**:每个 emitted `argaddr` 必须非零、直接指向 `V.body`,并在 + compilation 租约期间有效。 +9. **Body uniqueness**:地址只定位 kvspace 中唯一权威的 `V.body`;任何层都不得复制 body + 作为 BinRWIR 状态。 +10. **No fallback**:旧名称、兼容 identifier、自动名称归一化、零地址和延迟间接 key + lookup 都不建立映射。 + +已删除 identifier 永久保留为不可复用。语义发生不兼容变化时必须分配新 identifier,并 +更新 manifest version;不能在旧 identifier 下静默改变语义。 + +## 6. Address lifetime scope + +当前 BinRWIR 只接受可证明地址生命周期长于 compilation 租约的静态 key: + +- vthread `pc`; +- vthread `status`; +- `/lib` 下函数源等长期存在的 key/xvalue body。 + +host 对 key 解引用只返回 `uint64 xvalue_address`。vmkv 验证 XValue head 固定为 8 字节, +检查无符号地址加法不会溢出,再生成最终 `uint64 argaddr`。BinRWIR 只保存该最终地址, +vmbin 只消费它;两者都不缓存 key 对象、XValue 或 `V.body`。vmbin 每次使用参数时都从 +`argaddr` 读取当前 body;写参数必须写回同一地址。 + +map、struct 等能力可由运行时数据动态拼接 key。目标 key 可能尚未分配,已解析地址也可能 +因 key/xvalue 生命周期变化而失效。在生命周期 ABI 明确定义 pin、generation、relocation +和 invalidation 以前,这些参数必须返回 `RUNTIMEC_STATUS_UNSUPPORTED`,并且不产生 +canonical Program 或 compilation。JIT 不能绕过该限制。 + +## 7. vmkv obligations + +vmkv 对每条 RWIR instruction 执行以下固定流程: + +1. 读取明文 operation name 与全部 typed, named Read/Write parameters。 +2. 在 generated specification table 中找到唯一 myrwircap record 与 `opid`。 +3. 验证参数数量、名称、类型、方向和 alias declaration。 +4. 为每个参数取得 canonical `argidx`。 +5. 请求 host 解引用参数 key;host 只能返回 64 位 XValue 基址,不能返回自行选择偏移的 + body 地址。 +6. 验证 XValue 基址非零、生命周期可证明且 XValue head ABI 恰好为 8 字节。 +7. 检查 `xvalue_address + 8` 不溢出,并由 vmkv 唯一执行该加法得到直接指向 `V.body` 的 + `uint64 argaddr`。 +8. 按 source 参数顺序写入全部 `argidx + argaddr`,并保留 source logical program counter。 +9. finalize 前验证每条 instruction、每个参数和每个地址都可追溯到唯一 mapping 与租约。 + +vmkv 不能进行指令融合、控制流优化、register allocation、机器码生成或 execution target +选择。它也不能为动态 key 生成占位地址、运行时间接查找描述或 fallback Program。 + +## 8. Read/Write alias rule + +Read 与 Write 参数保持不同 `argidx`。若 signature 明确允许 alias,vmkv 可以让二者保存 +同一个 `argaddr`: + +```text +Read argidx -> same uint64 argaddr -> one kvspace V.body +Write argidx -> same uint64 argaddr -> one kvspace V.body +``` + +写入后,后续 Read 必须从该地址观察到新 body。vmbin 不得保留旧 body snapshot;JIT 必须 +在当前逻辑指令的 speculative state 内维护 alias dependency。只有该指令完整完成后, +全部 aliased dirty state 才能一起 materialize;未完成状态必须丢弃。未声明 alias 而地址 +相同属于 `RUNTIMEC_STATUS_INVALID`。 + +## 9. VMBIN and JIT boundary + +VMBIN 是 address-driven memory executor: + +- 不拥有 xvalue cache、body cache 或 software register file; +- Read 从 `argaddr` 指向的 kvspace body 取得当前值; +- 每条逻辑 instruction 完成前,Write 结果必须已位于对应 body; +- 每条 logical instruction completion 都是不可关闭的 recovery/observable boundary; +- dispatch、`opid` 特化和融合不能消除 body 写回、合并完成边界或延迟 checkpoint 语义。 + +JIT 是唯一允许在当前逻辑指令内部把 body 临时提升到机器寄存器或 spill 并执行 register +allocation 的路径。每条 logical instruction completion 都必须建立 safepoint,且只有两种 +互斥动作: + +- `COMMIT_COMPLETED_STATE`:仅当指令完整完成时,把该指令全部 completed dirty state + materialize 到原 `argaddr`;全部写回成功后才能推进 logical program counter/checkpoint。 +- `DISCARD_INCOMPLETE_STATE`:guard failure、error、recovery 或 resume 遇到当前指令未完成 + 时,丢弃全部 speculative/partial register 与 spill state;不含 materialization entry, + 不写任何 `argaddr`,也不推进 logical program counter/checkpoint。 + +恢复只从最后一个已提交完成边界的 kvspace body 重建,不能从机器状态恢复。 + +## 10. Failure classification + +以下 mapping 错误必须返回明确编译错误,不得产生 Program: + +- capability 缺失、重复或存在歧义; +- stable RWIR identifier、`opid` 或 signature 内 `argidx` 重复; +- operation name、参数名称、类型、方向或 alias policy 不一致; +- `behavior-decision` 缺失或引用不存在; +- identifier 来自数组下标、文件偏移、声明次序或运行时注册次序; +- manifest version 与 Program identity 不一致。 + +合法 RWIR 使用当前不支持的动态 key 地址模型时返回 `RUNTIMEC_STATUS_UNSUPPORTED`;这 +不是 `NOT_COMPILABLE`。`NOT_COMPILABLE` 只表示已经成功生成合法 addressed BinRWIR, +内部 JIT eligibility 判定不适用,因而应建立 VMBIN compilation。 + +零 XValue 基址、`xvalue_address + 8` 溢出、非 8 字节 XValue head ABI 或无法证明地址 +生命周期都必须在 Program 产生前失败封闭。host 返回任意 body 偏移也违反合同;vmkv +不得接受、猜测、修正或建立 fallback。 + +## 11. Acceptance checks + +- 对 `myrwircaps` 与 `rwir_catalog.def` 做集合相等检查。 +- 对 stable RWIR identifier、`opid` 和每个 signature 的 `argidx` 做唯一性检查。 +- 每条 generated instruction 只含 `opid` 和完整 `argidx + uint64 argaddr` 参数对。 +- host fixture 只返回非零 64 位 XValue 基址,不返回 body 地址或偏移选择。 +- vmkv fixture 唯一执行 `argaddr = xvalue_address + 8`;每个结果非零、直接指向 kvspace + `V.body`,且受 compilation 地址生命周期约束。 +- 两个允许 alias 的不同 `argidx` 可指向同一地址,后续 Read 观察先前 Write。 +- BinRWIR、execution plan 与 VMBIN 不含 body cache 或 register file。 +- 每条 logical instruction completion 都产生不可关闭的 recovery/observable boundary。 +- completed-state fixture 只有在全部 dirty body 写回成功后才推进 logical program counter/checkpoint。 +- guard failure、error、recovery 与 resume fixture 丢弃未完成 register/spill,且零写回。 +- 动态 map/struct key fixture 返回 `RUNTIMEC_STATUS_UNSUPPORTED`,不产生 Program。 +- 零 XValue 基址、`+8` 溢出、非 8 字节 head ABI、间接 key fallback 和 JIT 动态 key + bypass fixture 必须失败。 diff --git a/runtimec/specification/rwir_catalog.def b/runtimec/specification/rwir_catalog.def new file mode 100644 index 00000000..c20cbe55 --- /dev/null +++ b/runtimec/specification/rwir_catalog.def @@ -0,0 +1,18 @@ +/* + * 组件:specification/rwir_catalog.def + * 作用:myrwircap 到 canonical BinRWIR operation 的唯一 manifest 输入。 + * 联系:每条记录显式保存稳定 RWIR operation identifier、规范 capability 名称、 + * canonical BinRWIR operation identifier、Read/Write 参数结构与 + * five-language-behavior-matrix.md 的行为决策引用;vmkv 只从本文件生成 + * 等价编码表,binrwir_compiler 再生成 execution plan,并在内部决定是否生成 + * JIT code 或建立 VMBIN target。 + * 约束:每个 myrwircap 必须恰好一条记录。identifier 不得来自数组下标、文件顺序、 + * 声明顺序或生成顺序。缺失、重复、歧义、签名不等价和无效行为引用必须成为 + * 编译错误。本文件不保存执行策略,不冻结 BinRWIR 内存布局。 + * 当前 scaffold 不定义记录。 + */ +#ifndef RUNTIMEC_RWIR_OPERATION +#error "Define RUNTIMEC_RWIR_OPERATION before including rwir_catalog.def" +#endif + +/* The scaffold defines no myrwircap mapping records. */ diff --git a/runtimec/specification/scalar_operations.def b/runtimec/specification/scalar_operations.def new file mode 100644 index 00000000..d0c9281e --- /dev/null +++ b/runtimec/specification/scalar_operations.def @@ -0,0 +1,15 @@ +/* + * 组件:specification/scalar_operations.def + * 作用:已裁决语义规则到标量 execution family 的唯一派生映射输入。 + * 联系:本映射只能引用 rwir_catalog.def 中已经拥有稳定 identifier 与 canonical + * BinRWIR operation 的记录;BinRWIR Compiler 由此生成 execution plan, + * vmbin 与 binrwir_compiler 内部 JIT 消费同一个 compilation 合同。 + * 约束:不得在此文件发明语言行为;five-language-behavior-matrix.md 是唯一 + * 行为事实源。每条记录前必须写 behavior-decision 标记;当前 scaffold + * 不定义标量操作记录。 + */ +#ifndef RUNTIMEC_SCALAR_OPERATION +#error "Define RUNTIMEC_SCALAR_OPERATION before including scalar_operations.def" +#endif + +/* The scaffold defines no scalar operation records. */ diff --git a/runtimec/tests/CMakeLists.txt b/runtimec/tests/CMakeLists.txt new file mode 100644 index 00000000..cafc50cf --- /dev/null +++ b/runtimec/tests/CMakeLists.txt @@ -0,0 +1,117 @@ +# runtimec scaffold acceptance tests. The public surface is one header and one +# structured interface; no historical header or symbol is accepted. +function(runtimec_test_warnings target_name) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options(${target_name} PRIVATE + -Wall -Wextra -Wpedantic -Werror) + elseif(MSVC) + target_compile_options(${target_name} PRIVATE /W4 /WX) + endif() +endfunction() + +add_executable(runtimec_headers_c_test public_headers_c_test.c) +target_compile_features(runtimec_headers_c_test PRIVATE c_std_11) +target_include_directories(runtimec_headers_c_test PRIVATE + "${RUNTIMEC_INCLUDE_DIRECTORY}") +runtimec_test_warnings(runtimec_headers_c_test) +add_test(NAME runtimec.headers.c COMMAND runtimec_headers_c_test) +set_tests_properties(runtimec.headers.c PROPERTIES + LABELS "runtimec;scaffold;headers") + +add_executable(runtimec_contracts_test contracts_test.c) +target_compile_features(runtimec_contracts_test PRIVATE c_std_11) +target_include_directories(runtimec_contracts_test PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + "${RUNTIMEC_INCLUDE_DIRECTORY}") +target_link_libraries(runtimec_contracts_test PRIVATE + runtimec_contract runtimec_binrwir) +runtimec_test_warnings(runtimec_contracts_test) +add_test(NAME runtimec.contracts COMMAND runtimec_contracts_test) +set_tests_properties(runtimec.contracts PROPERTIES + LABELS "runtimec;scaffold;contracts") + +add_executable(runtimec_host_contracts_test host_contracts_test.c) +target_compile_features(runtimec_host_contracts_test PRIVATE c_std_11) +target_include_directories(runtimec_host_contracts_test PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + "${RUNTIMEC_INCLUDE_DIRECTORY}") +target_link_libraries(runtimec_host_contracts_test PRIVATE + runtimec_contract runtimec_host) +runtimec_test_warnings(runtimec_host_contracts_test) +add_test(NAME runtimec.host.contracts COMMAND runtimec_host_contracts_test) +set_tests_properties(runtimec.host.contracts PROPERTIES + LABELS "runtimec;scaffold;host;contracts") + +add_executable(runtimec_binrwir_compiler_contracts_test + binrwir_compiler_contracts_test.c) +target_compile_features(runtimec_binrwir_compiler_contracts_test PRIVATE c_std_11) +target_include_directories(runtimec_binrwir_compiler_contracts_test PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/.." + "${RUNTIMEC_INCLUDE_DIRECTORY}") +target_link_libraries(runtimec_binrwir_compiler_contracts_test PRIVATE + runtimec_contract runtimec_binrwir runtimec_vmkv + runtimec_binrwir_compiler) +runtimec_test_warnings(runtimec_binrwir_compiler_contracts_test) +add_test(NAME runtimec.binrwir_compiler.contracts + COMMAND runtimec_binrwir_compiler_contracts_test) +set_tests_properties(runtimec.binrwir_compiler.contracts PROPERTIES + LABELS "runtimec;scaffold;binrwir_compiler;contracts") + +add_executable(runtimec_fail_closed_test stub_fail_closed_test.c) +target_compile_features(runtimec_fail_closed_test PRIVATE c_std_11) +target_include_directories(runtimec_fail_closed_test PRIVATE + "${RUNTIMEC_INCLUDE_DIRECTORY}") +target_link_libraries(runtimec_fail_closed_test PRIVATE + kvlang_runtimec_scaffold) +runtimec_test_warnings(runtimec_fail_closed_test) +add_test(NAME runtimec.fail_closed COMMAND runtimec_fail_closed_test) +set_tests_properties(runtimec.fail_closed PROPERTIES + LABELS "runtimec;scaffold") + +include(CheckLanguage) +check_language(CXX) +if(CMAKE_CXX_COMPILER) + enable_language(CXX) + add_executable(runtimec_headers_cplusplus_test + public_headers_cplusplus_test.cpp) + target_compile_features(runtimec_headers_cplusplus_test PRIVATE cxx_std_17) + target_include_directories(runtimec_headers_cplusplus_test PRIVATE + "${RUNTIMEC_INCLUDE_DIRECTORY}") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options(runtimec_headers_cplusplus_test PRIVATE + -Wall -Wextra -Wpedantic -Werror) + endif() + add_test(NAME runtimec.headers.cplusplus + COMMAND runtimec_headers_cplusplus_test) + set_tests_properties(runtimec.headers.cplusplus PROPERTIES + LABELS "runtimec;scaffold;headers") +endif() + +find_package(Python3 COMPONENTS Interpreter REQUIRED) +add_test(NAME runtimec.architecture.layers + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/../tools/architecture/check_layers.py" + --source-root "${CMAKE_CURRENT_SOURCE_DIR}/../..") +add_test(NAME runtimec.architecture.names + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/../tools/architecture/check_names.py" + --source-root "${CMAKE_CURRENT_SOURCE_DIR}/../..") +add_test(NAME runtimec.architecture.behavior_matrix + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/../tools/architecture/check_behavior_matrix.py" + --source-root "${CMAKE_CURRENT_SOURCE_DIR}/../..") +set_tests_properties( + runtimec.architecture.layers + runtimec.architecture.names + runtimec.architecture.behavior_matrix + PROPERTIES LABELS "runtimec;scaffold;architecture") + +if(UNIX) + add_test(NAME runtimec.interface.exports + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/../tools/architecture/check_exports.py" + --library "$" + --manifest "${CMAKE_CURRENT_SOURCE_DIR}/../interface/exports.txt") + set_tests_properties(runtimec.interface.exports PROPERTIES + LABELS "runtimec;scaffold;interface") +endif() diff --git a/runtimec/tests/binrwir_compiler_contracts_test.c b/runtimec/tests/binrwir_compiler_contracts_test.c new file mode 100644 index 00000000..4a6dd541 --- /dev/null +++ b/runtimec/tests/binrwir_compiler_contracts_test.c @@ -0,0 +1,485 @@ +/* Verify BinRWIR Compiler and its JIT compile-versus-execute boundaries. */ + +#include +#include + +#include "binrwir_compiler/compiler.h" +#include "binrwir_compiler/jit/compiler.h" +#include "binrwir_compiler/jit/execution_interface.h" +#include "binrwir_compiler/jit/executor.h" +#include "binrwir_compiler/jit/register_allocation.h" +#include "binrwir_compiler/jit/safepoint.h" +#include "binrwir_compiler/optimization.h" +#include "vmkv/encoder.h" + +#define RUNTIMEC_ASSERT_FUNCTION_TYPE(function_name, function_pointer_type) \ + _Static_assert(_Generic(&(function_name), function_pointer_type: 1, \ + default: 0), \ + "unexpected declaration for " #function_name) + +#define RUNTIMEC_EXPECT(condition) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: expectation failed: %s\n", __FILE__, \ + __LINE__, #condition); \ + ++failures; \ + } \ + } while (0) + +typedef runtimec_status_t (*binrwir_compile_function_t)( + binrwir_compiler_t *, const binrwir_program_t *, + binrwir_compilation_t **, runtimec_error_t *); +typedef runtimec_status_t (*binrwir_compiler_revision_function_t)( + const binrwir_compiler_t *, uint32_t *); +typedef runtimec_status_t (*binrwir_build_execution_plan_function_t)( + const runtimec_allocator_t *, const binrwir_program_t *, uint64_t, + uint32_t, binrwir_execution_plan_t **, runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_compile_function_t)( + binrwir_jit_compiler_t *, const binrwir_program_t *, + const binrwir_execution_plan_t *, + binrwir_jit_compilation_result_t *, runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_execute_function_t)( + const binrwir_jit_code_t *, binrwir_execution_frame_t *, + binrwir_execution_exit_t *, runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_register_allocation_create_function_t)( + const binrwir_jit_verified_program_t *, const binrwir_execution_plan_t *, + const binrwir_jit_target_t *, const runtimec_allocator_t *, + binrwir_jit_register_allocation_t **, binrwir_jit_refusal_t *, + runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_safepoint_plan_create_function_t)( + const binrwir_jit_verified_program_t *, + const binrwir_jit_register_allocation_t *, const runtimec_allocator_t *, + binrwir_jit_safepoint_plan_t **, binrwir_jit_refusal_t *, + runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_safepoint_commit_function_t)( + const binrwir_jit_safepoint_plan_t *, + binrwir_jit_safepoint_boundary_t, uint32_t, uint32_t, void *, + binrwir_execution_frame_t *, runtimec_error_t *); +typedef runtimec_status_t (*binrwir_jit_safepoint_discard_function_t)( + const binrwir_jit_safepoint_plan_t *, + binrwir_jit_safepoint_boundary_t, uint32_t, void *, + binrwir_execution_frame_t *, runtimec_error_t *); +typedef runtimec_status_t (*vmkv_encoder_revision_function_t)( + const vmkv_encoder_t *, uint32_t *); + +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_compiler_compile, binrwir_compile_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_compiler_get_revision, binrwir_compiler_revision_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_compiler_build_execution_plan, + binrwir_build_execution_plan_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE(binrwir_jit_compile, binrwir_jit_compile_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE(binrwir_jit_execute, binrwir_jit_execute_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_jit_register_allocation_create, + binrwir_jit_register_allocation_create_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_jit_safepoint_plan_create, + binrwir_jit_safepoint_plan_create_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_jit_safepoint_commit_completed_state, + binrwir_jit_safepoint_commit_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + binrwir_jit_safepoint_discard_incomplete_state, + binrwir_jit_safepoint_discard_function_t); +RUNTIMEC_ASSERT_FUNCTION_TYPE( + vmkv_encoder_get_canonical_encoding_revision, + vmkv_encoder_revision_function_t); + +_Static_assert(BINRWIR_EXECUTION_TARGET_INVALID == 0, + "invalid execution target changed"); +_Static_assert(BINRWIR_EXECUTION_TARGET_VMBIN == 1, + "vmbin execution target changed"); +_Static_assert(BINRWIR_EXECUTION_TARGET_JIT == 2, + "JIT execution target changed"); + +_Static_assert(BINRWIR_JIT_COMPILATION_OUTCOME_INVALID == 0, + "invalid JIT compilation outcome changed"); +_Static_assert(BINRWIR_JIT_COMPILATION_OUTCOME_COMPILED == 1, + "compiled JIT outcome changed"); +_Static_assert(BINRWIR_JIT_COMPILATION_OUTCOME_NOT_COMPILABLE == 2, + "not-compilable JIT outcome changed"); +_Static_assert(BINRWIR_JIT_COMPILATION_OUTCOME_ERROR == 3, + "error JIT outcome changed"); + +_Static_assert(sizeof(binrwir_jit_body_origin_t) == 16U, + "JIT body origins must remain sixteen bytes"); +_Static_assert(sizeof(binrwir_jit_location_mapping_t) == 24U, + "JIT location mappings must remain twenty-four bytes"); +_Static_assert(sizeof(binrwir_jit_materialization_entry_t) == 24U, + "JIT materialization entries must remain twenty-four bytes"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION == 1, + "instruction-completion boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_PAUSE == 2, + "pause boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_HANDOFF == 3, + "handoff boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_CALL == 4, + "call boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_EXTERNAL_OBSERVATION == 5, + "external-observation boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_RECOVERY == 6, + "recovery boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_RESUME == 7, + "resume boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_GUARD_FAILURE == 8, + "guard-failure boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_BOUNDARY_ERROR == 9, + "error boundary changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_ACTION_COMMIT_COMPLETED_STATE == 1, + "completed-state commit action changed"); +_Static_assert(BINRWIR_JIT_SAFEPOINT_ACTION_DISCARD_INCOMPLETE_STATE == 2, + "incomplete-state discard action changed"); +_Static_assert(BINRWIR_JIT_MATERIALIZATION_FLAG_COMPLETED_DIRTY == 1, + "completed dirty materialization flag changed"); +_Static_assert(BINRWIR_JIT_WRITE_ALIAS_ACTION_UPDATE_READ_LOCATION == 1, + "write alias update action changed"); +_Static_assert(BINRWIR_JIT_WRITE_ALIAS_ACTION_INVALIDATE_READ_LOCATION == 2, + "write alias invalidation action changed"); + +static int failures; + +static void *test_allocate(void *context, size_t size, size_t alignment) { + (void)context; + (void)size; + (void)alignment; + return NULL; +} + +static void test_deallocate( + void *context, void *allocation, size_t size, size_t alignment) { + (void)context; + (void)allocation; + (void)size; + (void)alignment; +} + +typedef struct static_xvalue_resolution_context { + runtimec_xvalue_address_t xvalue_address; + runtimec_status_t status; + size_t call_count; +} static_xvalue_resolution_context_t; + +static runtimec_status_t resolve_static_xvalue_address( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, + runtimec_xvalue_address_t *output_xvalue_address, + runtimec_error_t *error) { + static_xvalue_resolution_context_t *resolution_context = context; + + (void)snapshot; + (void)key; + (void)error; + ++resolution_context->call_count; + if (resolution_context->status != RUNTIMEC_STATUS_SUCCESS) { + *output_xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + return resolution_context->status; + } + *output_xvalue_address = resolution_context->xvalue_address; + return RUNTIMEC_STATUS_SUCCESS; +} + +static void test_vmkv_xvalue_address_encoding(void) { + static_xvalue_resolution_context_t resolution_context = { + .xvalue_address = UINT64_C(0x1000), + .status = RUNTIMEC_STATUS_SUCCESS, + .call_count = 0U, + }; + runtimec_host_services_t host_services; + runtimec_snapshot_t snapshot; + const vmkv_rwir_argument_view_t source_argument = { + .parameter_type_name = RUNTIMEC_STRING_VIEW_LITERAL("int64"), + .parameter_name = RUNTIMEC_STRING_VIEW_LITERAL("value"), + .key = RUNTIMEC_KEY_VIEW_LITERAL("/value"), + .key_addressability = VMKV_KEY_ADDRESSABILITY_STATIC, + }; + vmkv_rwir_argument_view_t dynamic_argument = source_argument; + binrwir_argument_t encoded_argument = { + .argument_index = UINT32_MAX, + .reserved_32_bits = UINT32_MAX, + .body_address = UINT64_MAX, + }; + + runtimec_host_services_initialize(&host_services); + host_services.context = &resolution_context; + host_services.allocator = (runtimec_allocator_t){ + .context = NULL, + .allocate = test_allocate, + .deallocate = test_deallocate, + }; + host_services.resolve_static_xvalue_address = + resolve_static_xvalue_address; + runtimec_snapshot_reset(&snapshot); + const vmkv_encode_request_t request = { + .host_services = &host_services, + .function_path = RUNTIMEC_STRING_VIEW_LITERAL("/lib/test"), + .expected_snapshot = snapshot, + }; + + RUNTIMEC_EXPECT(vmkv_encode_static_argument( + &request, &source_argument, 7U, &encoded_argument, NULL) == + RUNTIMEC_STATUS_SUCCESS); + RUNTIMEC_EXPECT(resolution_context.call_count == 1U); + RUNTIMEC_EXPECT(encoded_argument.argument_index == 7U); + RUNTIMEC_EXPECT(encoded_argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(encoded_argument.body_address == UINT64_C(0x1008)); + + dynamic_argument.key_addressability = VMKV_KEY_ADDRESSABILITY_DYNAMIC; + RUNTIMEC_EXPECT(vmkv_encode_static_argument( + &request, &dynamic_argument, 7U, &encoded_argument, NULL) == + RUNTIMEC_STATUS_UNSUPPORTED); + RUNTIMEC_EXPECT(resolution_context.call_count == 1U); + RUNTIMEC_EXPECT(encoded_argument.argument_index == 0U); + RUNTIMEC_EXPECT(encoded_argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(encoded_argument.body_address == + BINRWIR_BODY_ADDRESS_NONE); + + resolution_context.xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + encoded_argument = (binrwir_argument_t){ + .argument_index = UINT32_MAX, + .reserved_32_bits = UINT32_MAX, + .body_address = UINT64_MAX, + }; + RUNTIMEC_EXPECT(vmkv_encode_static_argument( + &request, &source_argument, 7U, &encoded_argument, NULL) == + RUNTIMEC_STATUS_ERROR); + RUNTIMEC_EXPECT(resolution_context.call_count == 2U); + RUNTIMEC_EXPECT(encoded_argument.argument_index == 0U); + RUNTIMEC_EXPECT(encoded_argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(encoded_argument.body_address == + BINRWIR_BODY_ADDRESS_NONE); + + resolution_context.xvalue_address = UINT64_MAX - 7U; + encoded_argument = (binrwir_argument_t){ + .argument_index = UINT32_MAX, + .reserved_32_bits = UINT32_MAX, + .body_address = UINT64_MAX, + }; + RUNTIMEC_EXPECT(vmkv_encode_static_argument( + &request, &source_argument, 7U, &encoded_argument, NULL) == + RUNTIMEC_STATUS_ERROR); + RUNTIMEC_EXPECT(resolution_context.call_count == 3U); + RUNTIMEC_EXPECT(encoded_argument.argument_index == 0U); + RUNTIMEC_EXPECT(encoded_argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(encoded_argument.body_address == + BINRWIR_BODY_ADDRESS_NONE); + + resolution_context.xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + resolution_context.status = RUNTIMEC_STATUS_UNSUPPORTED; + encoded_argument = (binrwir_argument_t){ + .argument_index = UINT32_MAX, + .reserved_32_bits = UINT32_MAX, + .body_address = UINT64_MAX, + }; + RUNTIMEC_EXPECT(vmkv_encode_static_argument( + &request, &source_argument, 7U, &encoded_argument, NULL) == + RUNTIMEC_STATUS_UNSUPPORTED); + RUNTIMEC_EXPECT(resolution_context.call_count == 4U); + RUNTIMEC_EXPECT(encoded_argument.argument_index == 0U); + RUNTIMEC_EXPECT(encoded_argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(encoded_argument.body_address == + BINRWIR_BODY_ADDRESS_NONE); +} + +static void test_compiler_configuration(void) { + const runtimec_allocator_t allocator = { + .context = NULL, + .allocate = test_allocate, + .deallocate = test_deallocate, + }; + binrwir_compiler_t *compiler = + (binrwir_compiler_t *)(uintptr_t)1U; + binrwir_compiler_configuration_t configuration = { + .structure_size = sizeof(configuration), + .compiler_revision = 1U, + .enable_jit = false, + .jit_machine_code_capacity = 0U, + .jit_target = {0}, + }; + + RUNTIMEC_EXPECT(binrwir_compiler_create( + &allocator, &configuration, &compiler, NULL) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(compiler == NULL); + + configuration.compiler_revision = 0U; + RUNTIMEC_EXPECT(binrwir_compiler_create( + &allocator, &configuration, &compiler, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(compiler == NULL); + configuration.compiler_revision = 1U; + + configuration.jit_machine_code_capacity = 1U; + RUNTIMEC_EXPECT(binrwir_compiler_create( + &allocator, &configuration, &compiler, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(compiler == NULL); + configuration.jit_machine_code_capacity = 0U; + + configuration.enable_jit = true; + RUNTIMEC_EXPECT(binrwir_compiler_create( + &allocator, &configuration, &compiler, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(compiler == NULL); + + configuration.jit_machine_code_capacity = 1U; + configuration.jit_target.architecture = BINRWIR_JIT_ARCHITECTURE_X86_64; + RUNTIMEC_EXPECT(binrwir_compiler_create( + &allocator, &configuration, &compiler, NULL) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(compiler == NULL); +} + +static void test_compiler_revision_failure_shape(void) { + uint32_t compiler_revision = 0U; + + RUNTIMEC_EXPECT(binrwir_compiler_get_revision( + NULL, &compiler_revision) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(compiler_revision == UINT32_MAX); +} + +static void test_encoder_revision_failure_shape(void) { + uint32_t canonical_encoding_revision = 0U; + + RUNTIMEC_EXPECT(vmkv_encoder_get_canonical_encoding_revision( + NULL, &canonical_encoding_revision) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(canonical_encoding_revision == UINT32_MAX); +} + +static void test_compilation_shapes(void) { + const binrwir_jit_compilation_result_t not_compilable = { + .outcome = BINRWIR_JIT_COMPILATION_OUTCOME_NOT_COMPILABLE, + .code = NULL, + .refusal = {.reason = BINRWIR_JIT_REFUSAL_UNSUPPORTED_PROGRAM}, + }; + binrwir_compilation_view_t view = { + .execution_target = BINRWIR_EXECUTION_TARGET_JIT, + .canonical_program = (const binrwir_program_t *)(uintptr_t)1U, + .execution_plan = (const binrwir_execution_plan_t *)(uintptr_t)1U, + .binrwir_jit_code = (const binrwir_jit_code_t *)(uintptr_t)1U, + }; + + RUNTIMEC_EXPECT(not_compilable.outcome == + BINRWIR_JIT_COMPILATION_OUTCOME_NOT_COMPILABLE); + RUNTIMEC_EXPECT(not_compilable.code == NULL); + RUNTIMEC_EXPECT(binrwir_compilation_get_view(NULL, &view, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(view.execution_target == BINRWIR_EXECUTION_TARGET_INVALID); + RUNTIMEC_EXPECT(view.canonical_program == NULL); + RUNTIMEC_EXPECT(view.execution_plan == NULL); + RUNTIMEC_EXPECT(view.binrwir_jit_code == NULL); +} + +static void test_optimization_failure_shape(void) { + binrwir_execution_plan_t *execution_plan = + (binrwir_execution_plan_t *)(uintptr_t)1U; + + RUNTIMEC_EXPECT(binrwir_compiler_build_execution_plan( + NULL, NULL, 0U, 0U, &execution_plan, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(execution_plan == NULL); +} + +static void test_jit_failure_shapes(void) { + binrwir_jit_compilation_result_t compilation_result = { + .outcome = BINRWIR_JIT_COMPILATION_OUTCOME_NOT_COMPILABLE, + .code = (binrwir_jit_code_t *)(uintptr_t)1U, + }; + binrwir_execution_exit_t execution_exit = { + .kind = BINRWIR_EXECUTION_EXIT_DONE, + .status = RUNTIMEC_STATUS_SUCCESS, + }; + + RUNTIMEC_EXPECT(binrwir_jit_compile(NULL, NULL, NULL, &compilation_result, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(compilation_result.outcome == + BINRWIR_JIT_COMPILATION_OUTCOME_INVALID); + RUNTIMEC_EXPECT(compilation_result.code == NULL); + + RUNTIMEC_EXPECT(binrwir_jit_execute(NULL, NULL, &execution_exit, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(execution_exit.kind == BINRWIR_EXECUTION_EXIT_ERROR); + RUNTIMEC_EXPECT(execution_exit.status == RUNTIMEC_STATUS_INVALID); +} + +static void test_jit_register_and_safepoint_failure_shapes(void) { + binrwir_jit_register_allocation_t *register_allocation = + (binrwir_jit_register_allocation_t *)(uintptr_t)1U; + binrwir_jit_safepoint_plan_t *safepoint_plan = + (binrwir_jit_safepoint_plan_t *)(uintptr_t)1U; + binrwir_jit_refusal_t refusal = { + .reason = BINRWIR_JIT_REFUSAL_NONE, + }; + binrwir_jit_execution_interface_t execution_interface = { + .binary_interface_major_version = UINT16_MAX, + }; + binrwir_execution_frame_t frame = { + .logical_program_counter = 11U, + }; + + RUNTIMEC_EXPECT(binrwir_jit_register_allocation_create( + NULL, NULL, NULL, NULL, ®ister_allocation, &refusal, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(register_allocation == NULL); + RUNTIMEC_EXPECT(refusal.reason == BINRWIR_JIT_REFUSAL_INVALID_PROGRAM); + + refusal.reason = BINRWIR_JIT_REFUSAL_NONE; + RUNTIMEC_EXPECT(binrwir_jit_safepoint_plan_create( + NULL, NULL, NULL, &safepoint_plan, &refusal, NULL) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(safepoint_plan == NULL); + RUNTIMEC_EXPECT(refusal.reason == BINRWIR_JIT_REFUSAL_INVALID_PROGRAM); + + RUNTIMEC_EXPECT(binrwir_jit_safepoint_commit_completed_state( + NULL, BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION, 11U, 12U, + (void *)(uintptr_t)1U, &frame, NULL) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(frame.logical_program_counter == 11U); + + RUNTIMEC_EXPECT(binrwir_jit_safepoint_commit_completed_state( + (const binrwir_jit_safepoint_plan_t *)(uintptr_t)1U, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_ERROR, 11U, 12U, + (void *)(uintptr_t)1U, &frame, NULL) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(frame.logical_program_counter == 11U); + + RUNTIMEC_EXPECT(binrwir_jit_safepoint_commit_completed_state( + (const binrwir_jit_safepoint_plan_t *)(uintptr_t)1U, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION, 11U, 12U, + (void *)(uintptr_t)1U, &frame, NULL) == RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(frame.logical_program_counter == 11U); + + RUNTIMEC_EXPECT(binrwir_jit_safepoint_discard_incomplete_state( + (const binrwir_jit_safepoint_plan_t *)(uintptr_t)1U, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION, 11U, + (void *)(uintptr_t)1U, &frame, NULL) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(frame.logical_program_counter == 11U); + + RUNTIMEC_EXPECT(binrwir_jit_safepoint_discard_incomplete_state( + (const binrwir_jit_safepoint_plan_t *)(uintptr_t)1U, + BINRWIR_JIT_SAFEPOINT_BOUNDARY_ERROR, 11U, + (void *)(uintptr_t)1U, &frame, NULL) == RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(frame.logical_program_counter == 11U); + + RUNTIMEC_EXPECT(binrwir_jit_execution_interface(&execution_interface) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(execution_interface.binary_interface_major_version == 0U); + RUNTIMEC_EXPECT(execution_interface.value_body_address_size_bytes == 0U); + RUNTIMEC_EXPECT(execution_interface.argument_index_size_bytes == 0U); + RUNTIMEC_EXPECT(execution_interface.safepoint_contract_revision == 0U); +} + +int main(void) { + test_vmkv_xvalue_address_encoding(); + test_compiler_configuration(); + test_compiler_revision_failure_shape(); + test_encoder_revision_failure_shape(); + test_compilation_shapes(); + test_optimization_failure_shape(); + test_jit_failure_shapes(); + test_jit_register_and_safepoint_failure_shapes(); + if (failures != 0) { + fprintf(stderr, "binrwir_compiler_contracts_test: %d failure(s)\n", failures); + return 1; + } + return 0; +} diff --git a/runtimec/tests/contracts_test.c b/runtimec/tests/contracts_test.c new file mode 100644 index 00000000..806b65e4 --- /dev/null +++ b/runtimec/tests/contracts_test.c @@ -0,0 +1,140 @@ +/* Freeze only compact cross-component contracts selected by the design. */ + +#include +#include +#include + +#include + +#include "binrwir/argument.h" +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" +#include "binrwir/instruction.h" +#include "binrwir/program.h" +#include "binrwir/signature.h" +#include "contract/host_services.h" + +_Static_assert(RUNTIMEC_STATUS_SUCCESS == 0, "success must remain zero"); +_Static_assert(RUNTIMEC_STATUS_YIELD == 1, "yield status changed"); +_Static_assert(RUNTIMEC_STATUS_WAIT == 2, "wait status changed"); +_Static_assert(RUNTIMEC_STATUS_UNSUPPORTED == 3, "unsupported status changed"); +_Static_assert(RUNTIMEC_STATUS_NOT_IMPLEMENTED == 4, + "not-implemented status changed"); +_Static_assert(RUNTIMEC_STATUS_INVALID == 5, "invalid status changed"); +_Static_assert(RUNTIMEC_STATUS_OUT_OF_MEMORY == 6, + "out-of-memory status changed"); +_Static_assert(RUNTIMEC_STATUS_ERROR == 7, "error status changed"); +_Static_assert(RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY == 8, + "insufficient-capacity status changed"); + +_Static_assert(RUNTIMEC_HOST_SERVICES_BINARY_INTERFACE_VERSION == 3U, + "host services binary interface must remain version 3"); +_Static_assert(RUNTIMEC_XVALUE_BODY_OFFSET_BYTES == 8U, + "V.body must remain eight bytes after the XValue base"); +_Static_assert(sizeof(runtimec_xvalue_address_t) == 8U, + "XValue addresses must remain 64 bits"); +_Static_assert(sizeof(runtimec_value_body_address_t) == 8U, + "V.body addresses must remain 64 bits"); +_Static_assert(sizeof(binrwir_argument_t) == 16U, + "BinRWIR arguments must remain sixteen bytes"); +_Static_assert(offsetof(binrwir_argument_t, argument_index) == 0U, + "BinRWIR argidx offset changed"); +_Static_assert(offsetof(binrwir_argument_t, body_address) == 8U, + "BinRWIR argaddr offset changed"); +_Static_assert(sizeof(binrwir_alias_rule_t) == 12U, + "alias rules must remain twelve bytes"); +_Static_assert(sizeof(binrwir_instruction_t) == 4U, + "BinRWIR instruction heads must contain only opid"); +_Static_assert(offsetof(binrwir_instruction_t, operation_identifier) == 0U, + "BinRWIR opid offset changed"); +_Static_assert(sizeof(binrwir_program_identity_t) == 32U, + "program identities must remain thirty-two bytes"); +_Static_assert(sizeof(binrwir_execution_exit_t) == 24U, + "structured exits must contain only control state"); + +static int failures; + +#define RUNTIMEC_EXPECT(condition) \ + do { \ + if (!(condition)) { \ + ++failures; \ + } \ + } while (0) + +static void test_plain_data_copy_round_trip(void) { + const binrwir_argument_t initial_argument = { + .body_address = UINT64_C(0x1000), + .argument_index = 7U, + .reserved_32_bits = 0U, + }; + const binrwir_execution_exit_t initial_exit = {0}; + binrwir_argument_t copied_argument; + binrwir_execution_exit_t copied_exit; + + memcpy(&copied_argument, &initial_argument, sizeof(copied_argument)); + memcpy(&copied_exit, &initial_exit, sizeof(copied_exit)); + RUNTIMEC_EXPECT(memcmp(&copied_argument, &initial_argument, + sizeof(copied_argument)) == 0); + RUNTIMEC_EXPECT(memcmp(&copied_exit, &initial_exit, + sizeof(copied_exit)) == 0); +} + +static void test_addressed_argument_failure_shape(void) { + binrwir_argument_t argument = { + .body_address = UINT64_MAX, + .argument_index = UINT32_MAX, + .reserved_32_bits = UINT32_MAX, + }; + + RUNTIMEC_EXPECT(binrwir_argument_create( + 3U, BINRWIR_BODY_ADDRESS_NONE, &argument) == RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(argument.body_address == BINRWIR_BODY_ADDRESS_NONE); + RUNTIMEC_EXPECT(argument.argument_index == 0U); + RUNTIMEC_EXPECT(argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(binrwir_argument_validate(&argument) == + RUNTIMEC_STATUS_INVALID); + + RUNTIMEC_EXPECT(binrwir_argument_create( + BINRWIR_ARGUMENT_INDEX_NONE, UINT64_C(0x1000), &argument) == + RUNTIMEC_STATUS_INVALID); + RUNTIMEC_EXPECT(argument.argument_index == 0U); + RUNTIMEC_EXPECT(argument.reserved_32_bits == 0U); + RUNTIMEC_EXPECT(argument.body_address == BINRWIR_BODY_ADDRESS_NONE); + + argument.body_address = UINT64_C(0x1000); + argument.argument_index = BINRWIR_ARGUMENT_INDEX_NONE; + argument.reserved_32_bits = 0U; + RUNTIMEC_EXPECT(binrwir_argument_validate(&argument) == + RUNTIMEC_STATUS_INVALID); +} + +static void test_instruction_argument_count_overflow(void) { + binrwir_instruction_build_request_t request = { + .operation_identifier = 1U, + .read_arguments = {.data = NULL, .count = UINT32_MAX}, + .write_arguments = {.data = NULL, .count = 1U}, + }; + + RUNTIMEC_EXPECT(binrwir_instruction_validate_build_request(&request) == + RUNTIMEC_STATUS_INVALID); +} + +static void test_host_services_version(void) { + runtimec_host_services_t services; + + runtimec_host_services_initialize(&services); + RUNTIMEC_EXPECT(services.binary_interface_version == 3U); + services.binary_interface_version = 2U; + RUNTIMEC_EXPECT(!runtimec_host_services_is_valid(&services)); +} + +int main(void) { + kvlang_runtime_t *opaque_runtime = NULL; + + (void)opaque_runtime; + test_plain_data_copy_round_trip(); + test_addressed_argument_failure_shape(); + test_instruction_argument_count_overflow(); + test_host_services_version(); + return failures == 0 ? 0 : 1; +} diff --git a/runtimec/tests/host_contracts_test.c b/runtimec/tests/host_contracts_test.c new file mode 100644 index 00000000..37b1a36a --- /dev/null +++ b/runtimec/tests/host_contracts_test.c @@ -0,0 +1,106 @@ +/* Verify that host exposes stable XValue bases without deriving V.body. */ + +#include +#include + +#include "host/host.h" + +#define RUNTIMEC_EXPECT(condition) \ + do { \ + if (!(condition)) { \ + ++failures; \ + } \ + } while (0) + +typedef struct host_resolution_context { + runtimec_xvalue_address_t xvalue_address; + runtimec_status_t status; +} host_resolution_context_t; + +static int failures; + +static void *test_allocate(void *context, size_t size, size_t alignment) { + (void)context; + (void)alignment; + return malloc(size); +} + +static void test_deallocate( + void *context, void *allocation, size_t size, size_t alignment) { + (void)context; + (void)size; + (void)alignment; + free(allocation); +} + +static runtimec_status_t resolve_static_xvalue_address( + void *context, const runtimec_snapshot_t *snapshot, + runtimec_key_view_t key, + runtimec_xvalue_address_t *output_xvalue_address, + runtimec_error_t *error) { + host_resolution_context_t *resolution_context = context; + + (void)snapshot; + (void)key; + (void)error; + if (resolution_context->status != RUNTIMEC_STATUS_SUCCESS) { + *output_xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + return resolution_context->status; + } + *output_xvalue_address = resolution_context->xvalue_address; + return RUNTIMEC_STATUS_SUCCESS; +} + +int main(void) { + host_resolution_context_t resolution_context = { + .xvalue_address = UINT64_C(0x1000), + .status = RUNTIMEC_STATUS_SUCCESS, + }; + runtimec_host_services_t services; + runtimec_host_t *host = NULL; + runtimec_snapshot_t snapshot; + runtimec_error_t error; + runtimec_xvalue_address_t output_address = UINT64_MAX; + const runtimec_key_view_t value_key = + RUNTIMEC_KEY_VIEW_LITERAL("/value"); + const runtimec_key_view_t dynamic_key = + RUNTIMEC_KEY_VIEW_LITERAL("/dynamic"); + + runtimec_host_services_initialize(&services); + services.context = &resolution_context; + services.allocator = (runtimec_allocator_t){ + .context = NULL, + .allocate = test_allocate, + .deallocate = test_deallocate, + }; + services.resolve_static_xvalue_address = resolve_static_xvalue_address; + runtimec_snapshot_reset(&snapshot); + runtimec_error_reset(&error); + + RUNTIMEC_EXPECT(runtimec_host_create(&services, &host, &error) == + RUNTIMEC_STATUS_SUCCESS); + RUNTIMEC_EXPECT(host != NULL); + + RUNTIMEC_EXPECT(runtimec_host_resolve_static_xvalue_address( + host, &snapshot, value_key, &output_address, &error) == + RUNTIMEC_STATUS_SUCCESS); + RUNTIMEC_EXPECT(output_address == UINT64_C(0x1000)); + + resolution_context.xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + output_address = UINT64_MAX; + RUNTIMEC_EXPECT(runtimec_host_resolve_static_xvalue_address( + host, &snapshot, value_key, &output_address, &error) == + RUNTIMEC_STATUS_ERROR); + RUNTIMEC_EXPECT(output_address == RUNTIMEC_XVALUE_ADDRESS_NONE); + + resolution_context.status = RUNTIMEC_STATUS_UNSUPPORTED; + output_address = UINT64_MAX; + RUNTIMEC_EXPECT(runtimec_host_resolve_static_xvalue_address( + host, &snapshot, dynamic_key, &output_address, &error) == + RUNTIMEC_STATUS_UNSUPPORTED); + RUNTIMEC_EXPECT(output_address == RUNTIMEC_XVALUE_ADDRESS_NONE); + + RUNTIMEC_EXPECT(runtimec_host_destroy(host, &error) == + RUNTIMEC_STATUS_SUCCESS); + return failures == 0 ? 0 : 1; +} diff --git a/runtimec/tests/public_headers_c_test.c b/runtimec/tests/public_headers_c_test.c new file mode 100644 index 00000000..e3fcef75 --- /dev/null +++ b/runtimec/tests/public_headers_c_test.c @@ -0,0 +1,30 @@ +/* The only public C header must expose the structured runtimec interface. */ + +#include + +#define RUNTIMEC_ASSERT_FUNCTION_TYPE(function_name, function_pointer_type) \ + _Static_assert(_Generic(&(function_name), function_pointer_type: 1, \ + default: 0), \ + "unexpected declaration for " #function_name) + +typedef runtimec_status_t (*runtimec_create_function)( + const kvlang_runtime_create_request_t *, kvlang_runtime_t **, + kvlang_runtime_error_t *); +typedef runtimec_status_t (*runtimec_destroy_function)( + kvlang_runtime_t *, kvlang_runtime_error_t *); +typedef runtimec_status_t (*runtimec_execute_function)( + kvlang_runtime_t *, const kvlang_runtime_execute_request_t *, + kvlang_runtime_execute_result_t *, kvlang_runtime_error_t *); + +RUNTIMEC_ASSERT_FUNCTION_TYPE(kvlang_runtime_create, runtimec_create_function); +RUNTIMEC_ASSERT_FUNCTION_TYPE(kvlang_runtime_destroy, runtimec_destroy_function); +RUNTIMEC_ASSERT_FUNCTION_TYPE(kvlang_runtime_execute, runtimec_execute_function); + +_Static_assert(RUNTIMEC_STATUS_SUCCESS == 0, + "the only successful status must remain zero"); +_Static_assert(sizeof(kvlang_runtime_t *) == sizeof(void *), + "the runtime handle must remain opaque and pointer-sized"); + +int main(void) { + return 0; +} diff --git a/runtimec/tests/public_headers_cplusplus_test.cpp b/runtimec/tests/public_headers_cplusplus_test.cpp new file mode 100644 index 00000000..5f16cfcd --- /dev/null +++ b/runtimec/tests/public_headers_cplusplus_test.cpp @@ -0,0 +1,25 @@ +/* The C interface must be directly consumable from C++17. */ + +#include + +#include + +static_assert(std::is_same_v< + decltype(&kvlang_runtime_create), + runtimec_status_t (*)(const kvlang_runtime_create_request_t *, + kvlang_runtime_t **, + kvlang_runtime_error_t *)>); +static_assert(std::is_same_v< + decltype(&kvlang_runtime_destroy), + runtimec_status_t (*)(kvlang_runtime_t *, + kvlang_runtime_error_t *)>); +static_assert(std::is_same_v< + decltype(&kvlang_runtime_execute), + runtimec_status_t (*)(kvlang_runtime_t *, + const kvlang_runtime_execute_request_t *, + kvlang_runtime_execute_result_t *, + kvlang_runtime_error_t *)>); + +int main() { + return 0; +} diff --git a/runtimec/tests/stub_fail_closed_test.c b/runtimec/tests/stub_fail_closed_test.c new file mode 100644 index 00000000..15c6e691 --- /dev/null +++ b/runtimec/tests/stub_fail_closed_test.c @@ -0,0 +1,100 @@ +/* The unimplemented public interface must fail without producing output. */ + +#include +#include +#include + +#include + +static int failures; + +#define RUNTIMEC_EXPECT(condition) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: expectation failed: %s\n", __FILE__, \ + __LINE__, #condition); \ + ++failures; \ + } \ + } while (0) + +static kvlang_runtime_error_t prepare_error(char *message, size_t capacity) { + kvlang_runtime_error_t error = {0}; + error.message.data = message; + error.message.capacity = capacity; + return error; +} + +int main(void) { + char error_message[128] = "dirty"; + char value[16] = "dirty"; + char program_counter[16] = "dirty"; + kvlang_runtime_error_t error = prepare_error( + error_message, sizeof(error_message)); + kvlang_runtime_t *runtime = (kvlang_runtime_t *)(uintptr_t)1U; + kvlang_runtime_t *created_runtime = runtime; + kvlang_runtime_create_request_t create_request = {"shared-memory://test"}; + const char *arguments[] = {"value"}; + kvlang_runtime_execute_request_t execute_request = { + KVLANG_RUNTIME_ENTRY_FUNCTION, "example", arguments, 1U, + }; + kvlang_runtime_execute_result_t result = { + {value, sizeof(value), 5U, 5U}, + {program_counter, sizeof(program_counter), 5U, 5U}, + }; + + RUNTIMEC_EXPECT(kvlang_runtime_create( + &create_request, &created_runtime, &error) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(created_runtime == NULL); + RUNTIMEC_EXPECT(error.status == RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(error.message.length != 0U); + + { + char small_error_message[4] = {'x', 'y', 'z', '\0'}; + kvlang_runtime_error_t small_error = prepare_error( + small_error_message, sizeof(small_error_message)); + created_runtime = runtime; + RUNTIMEC_EXPECT(kvlang_runtime_create( + &create_request, &created_runtime, &small_error) == + RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY); + RUNTIMEC_EXPECT(created_runtime == NULL); + RUNTIMEC_EXPECT(small_error.status == + RUNTIMEC_STATUS_INSUFFICIENT_CAPACITY); + RUNTIMEC_EXPECT(small_error.operation_status == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(small_error.message.length == 0U); + RUNTIMEC_EXPECT(small_error.message.required_length >= + sizeof(small_error_message)); + RUNTIMEC_EXPECT(small_error_message[0] == '\0'); + RUNTIMEC_EXPECT(small_error_message[1] == 'y'); + RUNTIMEC_EXPECT(small_error_message[2] == 'z'); + } + + error = prepare_error(error_message, sizeof(error_message)); + strcpy(value, "dirty"); + strcpy(program_counter, "dirty"); + result.value.length = 5U; + result.value.required_length = 5U; + result.program_counter.length = 5U; + result.program_counter.required_length = 5U; + RUNTIMEC_EXPECT(kvlang_runtime_execute( + runtime, &execute_request, &result, &error) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(result.value.length == 0U); + RUNTIMEC_EXPECT(result.value.required_length == 0U); + RUNTIMEC_EXPECT(value[0] == '\0'); + RUNTIMEC_EXPECT(result.program_counter.length == 0U); + RUNTIMEC_EXPECT(result.program_counter.required_length == 0U); + RUNTIMEC_EXPECT(program_counter[0] == '\0'); + + error = prepare_error(error_message, sizeof(error_message)); + RUNTIMEC_EXPECT(kvlang_runtime_destroy(runtime, &error) == + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + RUNTIMEC_EXPECT(error.status == RUNTIMEC_STATUS_NOT_IMPLEMENTED); + + if (failures != 0) { + fprintf(stderr, "stub_fail_closed_test: %d failure(s)\n", failures); + return 1; + } + return 0; +} diff --git a/runtimec/tools/architecture/check_behavior_matrix.py b/runtimec/tools/architecture/check_behavior_matrix.py new file mode 100644 index 00000000..2c06b74e --- /dev/null +++ b/runtimec/tools/architecture/check_behavior_matrix.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +"""Require five-language decisions before runtimec language semantics.""" + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path + + +EXPECTED_COLUMNS = ( + "Decision", "Behavior", "C", "Python", "Rust", "Go", "V8/TypeScript", + "Agreement", "kvlang decision", "Boundary fixture", "Invalid input", + "Design record", +) +EXPECTED_MATRIX_VERSION = "2" +MATRIX_VERSION_PATTERN = re.compile( + r"" +) +DECISION_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$") +MARKER_PATTERN = re.compile(r"behavior-decision:\s*([a-z][a-z0-9-]*)") +MANIFEST_ENTRY_PATTERN = re.compile( + r"^\s*RUNTIMEC_[A-Z0-9_]*(?:OP|OPERATION)\s*\(" +) +PLACEHOLDER_PATTERN = re.compile(r"\b(?:todo|tbd|unknown|placeholder)\b", re.IGNORECASE) +SCANNED_PRODUCT_DIRECTORIES = ( + "specification", "binrwir", "vmkv", "binrwir_compiler", "vmbin", +) + + +@dataclass(frozen=True, order=True) +class Finding: + path: str + line: int + rule: str + detail: str + + def render(self) -> str: + return f"{self.path}:{self.line}: [{self.rule}] {self.detail}" + + +def parse_arguments(command_arguments: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--source-root", type=Path, + default=Path(__file__).resolve().parents[3], + help="repository source root, or the runtimec directory itself", + ) + parser.add_argument("--quiet", action="store_true", help="suppress success output") + return parser.parse_args(command_arguments) + + +def locate_runtimec(source_root: Path) -> tuple[Path, Path]: + source_root = source_root.resolve() + return ((source_root.parent, source_root) if source_root.name == "runtimec" + else (source_root, source_root / "runtimec")) + + +def display(path: Path, repository_root: Path) -> str: + try: + return path.resolve().relative_to(repository_root).as_posix() + except ValueError: + return path.as_posix() + + +def table_cells(line: str) -> tuple[str, ...]: + return tuple(cell.strip() for cell in line.strip().strip("|").split("|")) + + +def read_decisions( + matrix_path: Path, repository_root: Path, +) -> tuple[set[str], list[Finding]]: + findings: list[Finding] = [] + matrix_display = display(matrix_path, repository_root) + if not matrix_path.is_file(): + return set(), [Finding(matrix_display, 1, "missing-matrix", "matrix is required")] + lines = matrix_path.read_text(encoding="utf-8").splitlines() + version_match = next( + (MATRIX_VERSION_PATTERN.search(line) for line in lines + if MATRIX_VERSION_PATTERN.search(line)), + None, + ) + if version_match is None or version_match.group(1) != EXPECTED_MATRIX_VERSION: + findings.append(Finding( + matrix_display, 1, "missing-version", + f"version {EXPECTED_MATRIX_VERSION} marker is required", + )) + header_index = next( + (index for index, line in enumerate(lines) if table_cells(line) == EXPECTED_COLUMNS), None, + ) + if header_index is None: + return set(), findings + [Finding( + matrix_display, 1, "invalid-header", "required twelve-column table is missing", + )] + + decisions: set[str] = set() + for index in range(header_index + 2, len(lines)): + line = lines[index] + if not line.startswith("|"): + break + cells = table_cells(line) + line_number = index + 1 + if len(cells) != len(EXPECTED_COLUMNS): + findings.append(Finding(matrix_display, line_number, "column-count", + f"expected {len(EXPECTED_COLUMNS)}, found {len(cells)}")) + continue + decision = cells[0] + if not DECISION_PATTERN.fullmatch(decision): + findings.append(Finding(matrix_display, line_number, "invalid-decision", decision)) + elif decision in decisions: + findings.append(Finding(matrix_display, line_number, "duplicate-decision", decision)) + else: + decisions.add(decision) + for column, cell in zip(EXPECTED_COLUMNS, cells): + if not cell or PLACEHOLDER_PATTERN.search(cell): + findings.append(Finding(matrix_display, line_number, "incomplete-cell", column)) + if cells[7] not in {"consensus", "split"}: + findings.append(Finding(matrix_display, line_number, "invalid-agreement", cells[7])) + if not decisions: + findings.append(Finding(matrix_display, header_index + 1, "empty-matrix", + "at least one decision is required")) + return decisions, findings + + +def iter_product_files(runtimec_root: Path): + for directory_name in SCANNED_PRODUCT_DIRECTORIES: + directory = runtimec_root / directory_name + if directory.is_dir(): + for path in sorted(directory.rglob("*")): + if path.is_file() and path.suffix in {".c", ".h", ".def"}: + yield path + + +def audit_markers( + runtimec_root: Path, repository_root: Path, decisions: set[str], +) -> tuple[list[Finding], int]: + findings: list[Finding] = [] + scanned = 0 + for path in iter_product_files(runtimec_root): + scanned += 1 + lines = path.read_text(encoding="utf-8").splitlines() + path_display = display(path, repository_root) + for index, line in enumerate(lines): + for match in MARKER_PATTERN.finditer(line): + if match.group(1) not in decisions: + findings.append(Finding(path_display, index + 1, "unknown-decision", + match.group(1))) + if (path.suffix == ".def" and MANIFEST_ENTRY_PATTERN.search(line) + and "INVALID" not in line): + context = "\n".join(lines[max(0, index - 2):index + 1]) + if MARKER_PATTERN.search(context) is None: + findings.append(Finding( + path_display, index + 1, "manifest-before-matrix", + "add a behavior-decision marker before this entry", + )) + return findings, scanned + + +def main(command_arguments: list[str] | None = None) -> int: + arguments = parse_arguments(command_arguments or sys.argv[1:]) + repository_root, runtimec_root = locate_runtimec(arguments.source_root) + matrix_path = runtimec_root / "specification" / \ + "five-language-behavior-matrix.md" + decisions, findings = read_decisions(matrix_path, repository_root) + marker_findings, scanned = audit_markers(runtimec_root, repository_root, decisions) + findings.extend(marker_findings) + for finding in sorted(set(findings)): + print(finding.render()) + if findings: + print(f"check_behavior_matrix: FAIL ({len(set(findings))} finding(s))") + return 1 + if not arguments.quiet: + print(f"check_behavior_matrix: OK ({len(decisions)} decisions, {scanned} files scanned)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtimec/tools/architecture/check_exports.py b/runtimec/tools/architecture/check_exports.py new file mode 100644 index 00000000..897d1200 --- /dev/null +++ b/runtimec/tools/architecture/check_exports.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""Require the dynamic library export set to equal interface/exports.txt.""" + +from __future__ import annotations + +import argparse +import platform +import subprocess +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--library", type=Path, required=True) + parser.add_argument("--manifest", type=Path, required=True) + arguments = parser.parse_args() + + expected = { + line.strip() + for line in arguments.manifest.read_text(encoding="utf-8").splitlines() + if line.strip() and not line.lstrip().startswith("#") + } + system = platform.system() + command = (["nm", "-gU", str(arguments.library)] if system == "Darwin" + else ["nm", "-D", "--defined-only", str(arguments.library)]) + output = subprocess.run( + command, check=True, text=True, stdout=subprocess.PIPE, + ).stdout + actual = set() + for line in output.splitlines(): + if line.strip(): + symbol = line.split()[-1] + if system == "Darwin" and symbol.startswith("_"): + symbol = symbol[1:] + actual.add(symbol) + + missing = sorted(expected - actual) + extra = sorted(actual - expected) + for symbol in missing: + print(f"missing export: {symbol}") + for symbol in extra: + print(f"unexpected export: {symbol}") + if missing or extra: + return 1 + print(f"check_exports: OK ({len(actual)} symbol(s))") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtimec/tools/architecture/check_layers.py b/runtimec/tools/architecture/check_layers.py new file mode 100644 index 00000000..c491ed16 --- /dev/null +++ b/runtimec/tools/architecture/check_layers.py @@ -0,0 +1,582 @@ +#!/usr/bin/env python3 +"""Enforce the runtimec component graph and its single execution-policy merge.""" + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path, PurePosixPath + + +SOURCE_SUFFIXES = {".c", ".h"} +PRODUCT_DIRECTORIES = ( + "include", "interface", "contract", "host", "binrwir", "vmkv", + "binrwir_compiler", "vmbin", "engine", "generated", +) +KNOWN_LAYERS = { + "public", "interface", "contract", "host", "binrwir", "vmkv", + "binrwir_compiler", "vmbin", "engine", +} +ALLOWED_DEPENDENCIES = { + "public": {"public"}, + "contract": {"public", "contract"}, + "host": {"contract", "host"}, + "binrwir": {"contract", "binrwir"}, + "vmkv": {"contract", "binrwir", "vmkv"}, + "binrwir_compiler": {"contract", "binrwir", "binrwir_compiler"}, + "vmbin": {"contract", "binrwir", "vmbin"}, + "engine": { + "contract", "host", "binrwir", "vmkv", "binrwir_compiler", + "vmbin", "engine", + }, + "interface": {"public", "contract", "engine", "interface"}, +} +EXPECTED_COMPONENT_ORDER = ( + ("runtimec_contract", "contract"), + ("runtimec_host", "host"), + ("runtimec_binrwir", "binrwir"), + ("runtimec_vmkv", "vmkv"), + ("runtimec_binrwir_compiler", "binrwir_compiler"), + ("runtimec_vmbin", "vmbin"), + ("runtimec_engine", "engine"), +) +EXPECTED_TARGET_DEPENDENCIES = { + "runtimec_contract": set(), + "runtimec_host": {"runtimec_contract"}, + "runtimec_binrwir": {"runtimec_contract"}, + "runtimec_vmkv": {"runtimec_contract", "runtimec_binrwir"}, + "runtimec_binrwir_compiler": {"runtimec_contract", "runtimec_binrwir"}, + "runtimec_vmbin": {"runtimec_contract", "runtimec_binrwir"}, + "runtimec_engine": { + "runtimec_contract", "runtimec_host", "runtimec_binrwir", + "runtimec_vmkv", "runtimec_binrwir_compiler", "runtimec_vmbin", + }, + "runtimec_interface": {"runtimec_contract", "runtimec_engine"}, +} +INCLUDE_PATTERN = re.compile(r'^\s*#\s*include\s*([<"])([^>"]+)[>"]') +FORBIDDEN_TREE_PATTERN = re.compile(r"(?:^|/)(?:runtime|runtimec2)(?:/|$)") +COMPONENT_PATTERN = re.compile( + r"runtimec_add_component\(\s*(runtimec_[a-z0-9_]+)\s+([a-z0-9_]+)\s*\)" +) +LINK_PATTERN = re.compile( + r"target_link_libraries\(\s*(runtimec_[a-z0-9_]+)\s+(.*?)\)", + re.DOTALL, +) +INTERNAL_PREFIXES = { + "contract", "host", "binrwir", "vmkv", "binrwir_compiler", + "vmbin", "engine", "interface", +} +POLICY_LAYERS = {"binrwir_compiler", "vmbin"} +ROUTE_FILES = {"engine/route.c", "engine/route.h"} +BINRWIR_NEUTRAL_FILE_NAMES = { + "value.c", "value.h", "reference.c", "reference.h", "signature.c", + "signature.h", "instruction.c", "instruction.h", "program.c", + "program.h", "execution_plan.c", "execution_plan.h", +} +REMOVED_BINRWIR_MODEL_FILES = ( + "execution_binding.c", "execution_binding.h", + "reference.c", "reference.h", "value.c", "value.h", +) +REQUIRED_JIT_STATE_FILES = ( + "register_allocation.c", "register_allocation.h", + "safepoint.c", "safepoint.h", +) +REQUIRED_JIT_SAFEPOINT_TOKENS = ( + "BINRWIR_JIT_SAFEPOINT_BOUNDARY_INSTRUCTION_COMPLETION", + "BINRWIR_JIT_SAFEPOINT_ACTION_COMMIT_COMPLETED_STATE", + "BINRWIR_JIT_SAFEPOINT_ACTION_DISCARD_INCOMPLETE_STATE", + "BINRWIR_JIT_MATERIALIZATION_FLAG_COMPLETED_DIRTY", + "binrwir_jit_safepoint_commit_completed_state", + "binrwir_jit_safepoint_discard_incomplete_state", +) +FORBIDDEN_JIT_SAFEPOINT_TOKENS = ( + "observe_every_instruction", + "BINRWIR_JIT_MATERIALIZATION_FLAG_DIRTY", + "binrwir_jit_safepoint_materialize(", +) +C_NON_CODE_PATTERN = re.compile( + r'//[^\n]*|/\*.*?\*/|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'', + re.DOTALL, +) +JIT_EXCLUSIVE_STORAGE_PATTERN = re.compile( + r"\b(?:[A-Za-z_][A-Za-z0-9_]*register[A-Za-z0-9_]*|" + r"[A-Za-z_][A-Za-z0-9_]*spill[A-Za-z0-9_]*)\b", + re.IGNORECASE, +) +REMOVED_VALUE_CACHE_PATTERN = re.compile( + r"\b(?:binrwir_value_slot|binrwir_cell|value_slot|cell_cache|" + r"value_cache|register_file|virtual_register|value_base|value_count|" + r"payload_low|payload_high)\b", + re.IGNORECASE, +) +HARDCODED_XVALUE_BODY_OFFSET_PATTERN = re.compile( + r"\b(?:xvalue|resolved)[A-Za-z0-9_]*address\b\s*\+\s*" + r"(?:8U?\b|UINT64_C\s*\(\s*8\s*\))", + re.IGNORECASE, +) + + +@dataclass(frozen=True, order=True) +class Finding: + path: str + line: int + rule: str + detail: str + + def render(self) -> str: + return f"{self.path}:{self.line}: [{self.rule}] {self.detail}" + + +def parse_arguments(command_arguments: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--source-root", type=Path, + default=Path(__file__).resolve().parents[3], + help="repository source root, or the runtimec directory itself", + ) + parser.add_argument("--quiet", action="store_true", help="suppress success output") + return parser.parse_args(command_arguments) + + +def locate_runtimec(source_root: Path) -> tuple[Path, Path]: + source_root = source_root.resolve() + return ((source_root.parent, source_root) if source_root.name == "runtimec" + else (source_root, source_root / "runtimec")) + + +def relative_display(path: Path, repository_root: Path) -> str: + try: + return path.resolve().relative_to(repository_root).as_posix() + except ValueError: + return path.as_posix() + + +def layer_for_relative(relative: PurePosixPath) -> str | None: + if not relative.parts: + return None + if relative.parts[0] == "include": + return "public" + if relative.parts[0] == "generated": + return "contract" + return relative.parts[0] if relative.parts[0] in KNOWN_LAYERS else None + + +def inferred_include_layer(token: str) -> str | None: + parts = tuple(part for part in PurePosixPath(token).parts if part not in {".", ".."}) + if not parts: + return None + if parts[0] == "kvlang": + return "public" + if parts[0] == "runtimec" and len(parts) > 1: + parts = parts[1:] + if not parts: + return None + return parts[0] if parts[0] in INTERNAL_PREFIXES else None + + +def resolve_include( + token: str, source: Path, runtimec_root: Path, repository_root: Path, +) -> Path | None: + candidates = [ + source.parent / token, + runtimec_root / token, + runtimec_root / "include" / token, + *(runtimec_root / directory / token for directory in INTERNAL_PREFIXES), + repository_root / token, + repository_root / "include" / token, + ] + return next((candidate.resolve() for candidate in candidates if candidate.is_file()), None) + + +def iter_product_sources(runtimec_root: Path): + for directory_name in PRODUCT_DIRECTORIES: + directory = runtimec_root / directory_name + if directory.is_dir(): + for path in sorted(directory.rglob("*")): + if path.is_file() and path.suffix in SOURCE_SUFFIXES: + yield path + + +def cmake_target_dependencies(cmake_text: str) -> dict[str, set[str]]: + dependencies: dict[str, set[str]] = {} + for match in LINK_PATTERN.finditer(cmake_text): + target, body = match.groups() + tokens = set(re.findall(r"\bruntimec_[a-z0-9_]+\b", body)) + dependencies.setdefault(target, set()).update(tokens) + return dependencies + + +def c_code_without_comments_or_literals(text: str) -> str: + return C_NON_CODE_PATTERN.sub( + lambda match: re.sub(r"[^\n]", " ", match.group(0)), text, + ) + + +def struct_fields(code: str, tag: str, alias: str) -> tuple[str, ...] | None: + match = re.search( + rf"typedef\s+struct\s+{re.escape(tag)}\s*\{{(.*?)\}}\s*" + rf"{re.escape(alias)}\s*;", + code, + re.DOTALL, + ) + if match is None: + return None + return tuple( + " ".join(declaration.split()) + for declaration in match.group(1).split(";") + if declaration.strip() + ) + + +def audit_addressed_binrwir_model( + runtimec_root: Path, repository_root: Path, +) -> list[Finding]: + findings: list[Finding] = [] + binrwir_root = runtimec_root / "binrwir" + jit_root = runtimec_root / "binrwir_compiler" / "jit" + + for file_name in REMOVED_BINRWIR_MODEL_FILES: + path = binrwir_root / file_name + if path.exists(): + findings.append(Finding( + relative_display(path, repository_root), 1, + "removed-binrwir-storage-model", + "reference/value/execution binding files must not exist", + )) + for file_name in REQUIRED_JIT_STATE_FILES: + path = jit_root / file_name + if not path.is_file(): + findings.append(Finding( + relative_display(path, repository_root), 1, + "missing-jit-state-contract", + "JIT register allocation and safepoint files are required", + )) + + safepoint_header = jit_root / "safepoint.h" + safepoint_source = jit_root / "safepoint.c" + lowering_header = jit_root / "lowering_plan.h" + jit_contract_text = "\n".join( + path.read_text(encoding="utf-8") + for path in (safepoint_header, safepoint_source, lowering_header) + if path.is_file() + ) + for token in REQUIRED_JIT_SAFEPOINT_TOKENS: + if token not in jit_contract_text: + findings.append(Finding( + relative_display(safepoint_header, repository_root), 1, + "missing-jit-commit-discard-contract", token, + )) + for token in FORBIDDEN_JIT_SAFEPOINT_TOKENS: + if token in jit_contract_text: + findings.append(Finding( + relative_display(jit_root, repository_root), 1, + "optional-or-ambiguous-jit-boundary", token, + )) + + lowering_code = c_code_without_comments_or_literals( + lowering_header.read_text(encoding="utf-8") + if lowering_header.is_file() else "", + ) + lowering_fields = struct_fields( + lowering_code, + "binrwir_jit_lowering_plan_options_t", + "binrwir_jit_lowering_plan_options_t", + ) + expected_lowering_fields = ( + "size_t structure_size", + "uint32_t code_generation_revision", + ) + if lowering_fields != expected_lowering_fields: + findings.append(Finding( + relative_display(lowering_header, repository_root), 1, + "optional-instruction-boundary", + f"expected fields {expected_lowering_fields!r}, found {lowering_fields!r}", + )) + + expected_structs = { + binrwir_root / "argument.h": ( + "binrwir_argument", "binrwir_argument_t", + ("uint32_t argument_index", + "uint32_t reserved_32_bits", + "uint64_t body_address"), + "sizeof(binrwir_argument_t) == 16U", + ), + binrwir_root / "instruction.h": ( + "binrwir_instruction", "binrwir_instruction_t", + ("uint32_t operation_identifier",), + "sizeof(binrwir_instruction_t) == 4U", + ), + } + for path, (tag, alias, expected_fields, size_assertion) in expected_structs.items(): + display = relative_display(path, repository_root) + if not path.is_file(): + findings.append(Finding(display, 1, "missing-addressed-binrwir-file", + "fixed binary structure is required")) + continue + text = path.read_text(encoding="utf-8") + code = c_code_without_comments_or_literals(text) + actual_fields = struct_fields(code, tag, alias) + if actual_fields != expected_fields: + findings.append(Finding( + display, 1, "addressed-binrwir-layout", + f"expected fields {expected_fields!r}, found {actual_fields!r}", + )) + if size_assertion not in code: + findings.append(Finding(display, 1, "addressed-binrwir-size", + size_assertion)) + + required_layout_assertions = { + binrwir_root / "argument.h": ( + "offsetof(binrwir_argument_t, argument_index) == 0U", + "offsetof(binrwir_argument_t, body_address) == 8U", + ), + binrwir_root / "instruction.h": ( + "offsetof(binrwir_instruction_t, operation_identifier) == 0U", + ), + } + for path, assertions in required_layout_assertions.items(): + display = relative_display(path, repository_root) + code = c_code_without_comments_or_literals( + path.read_text(encoding="utf-8") if path.is_file() else "", + ) + for assertion in assertions: + if assertion not in code: + findings.append(Finding( + display, 1, "addressed-binrwir-offset", assertion, + )) + + argument_source = binrwir_root / "argument.c" + instruction_source = binrwir_root / "instruction.c" + required_source_tokens = { + argument_source: ( + "body_address == BINRWIR_BODY_ADDRESS_NONE", + "memset(output_argument, 0, sizeof(*output_argument))", + ), + instruction_source: ( + ".body_address ==", + "BINRWIR_BODY_ADDRESS_NONE", + ), + } + for path, tokens in required_source_tokens.items(): + display = relative_display(path, repository_root) + text = path.read_text(encoding="utf-8") if path.is_file() else "" + text = c_code_without_comments_or_literals(text) + for token in tokens: + if token not in text: + findings.append(Finding( + display, 1, "missing-zero-address-rejection", token, + )) + + host_services = runtimec_root / "contract" / "host_services.h" + host_text = host_services.read_text(encoding="utf-8") \ + if host_services.is_file() else "" + if not re.search( + r"#define\s+RUNTIMEC_HOST_SERVICES_BINARY_INTERFACE_VERSION\s+3U\b", + host_text, + ): + findings.append(Finding( + relative_display(host_services, repository_root), 1, + "host-services-version", "binary interface version 3 is required", + )) + for token in ( + "RUNTIMEC_XVALUE_BODY_OFFSET_BYTES KVSPACE_XVALUE_HEAD_LEN", + "RUNTIMEC_XVALUE_BODY_OFFSET_BYTES == 8U", + "resolve_static_xvalue_address", + ): + if token not in host_text: + findings.append(Finding( + relative_display(host_services, repository_root), 1, + "xvalue-body-address-contract", token, + )) + + vmkv_source = runtimec_root / "vmkv" / "vmkv.c" + vmkv_text = vmkv_source.read_text(encoding="utf-8") \ + if vmkv_source.is_file() else "" + for token in ( + "static bool vmkv_derive_value_body_address(", + "UINT64_MAX - RUNTIMEC_XVALUE_BODY_OFFSET_BYTES", + "xvalue_address + RUNTIMEC_XVALUE_BODY_OFFSET_BYTES", + ): + if token not in c_code_without_comments_or_literals(vmkv_text): + findings.append(Finding( + relative_display(vmkv_source, repository_root), 1, + "vmkv-owned-body-address-derivation", token, + )) + + offset_owners = {host_services, vmkv_source} + for path in iter_product_sources(runtimec_root): + code = c_code_without_comments_or_literals( + path.read_text(encoding="utf-8"), + ) + if (path not in offset_owners and + "RUNTIMEC_XVALUE_BODY_OFFSET_BYTES" in code): + findings.append(Finding( + relative_display(path, repository_root), 1, + "body-address-derivation-outside-vmkv", + "only the contract declaration and VMKV derivation may use " + "the XValue body offset", + )) + if path != vmkv_source: + for match in HARDCODED_XVALUE_BODY_OFFSET_PATTERN.finditer(code): + findings.append(Finding( + relative_display(path, repository_root), + code.count("\n", 0, match.start()) + 1, + "hardcoded-body-address-derivation-outside-vmkv", + match.group(0), + )) + + removed_resolver_name = "resolve_static_" + "value_body_address" + for path in iter_product_sources(runtimec_root): + text = path.read_text(encoding="utf-8") + if removed_resolver_name in text: + findings.append(Finding( + relative_display(path, repository_root), 1, + "removed-static-address-resolver", removed_resolver_name, + )) + + for path in iter_product_sources(runtimec_root): + relative = path.relative_to(runtimec_root) + if relative.parts[:2] == ("binrwir_compiler", "jit"): + continue + code = c_code_without_comments_or_literals( + path.read_text(encoding="utf-8"), + ) + for rule, pattern in ( + ("jit-storage-outside-jit", JIT_EXCLUSIVE_STORAGE_PATTERN), + ("removed-value-cache-model", REMOVED_VALUE_CACHE_PATTERN), + ): + for match in pattern.finditer(code): + findings.append(Finding( + relative_display(path, repository_root), + code.count("\n", 0, match.start()) + 1, + rule, match.group(0), + )) + return findings + + +def audit_build_graph( + runtimec_root: Path, repository_root: Path, +) -> list[Finding]: + cmake_path = runtimec_root / "CMakeLists.txt" + display = relative_display(cmake_path, repository_root) + if not cmake_path.is_file(): + return [Finding(display, 1, "missing-build-file", "CMakeLists.txt is required")] + text = cmake_path.read_text(encoding="utf-8") + findings: list[Finding] = [] + actual_order = tuple(COMPONENT_PATTERN.findall(text)) + if actual_order != EXPECTED_COMPONENT_ORDER: + findings.append(Finding( + display, 1, "component-order", + f"expected {EXPECTED_COMPONENT_ORDER!r}, found {actual_order!r}", + )) + if not re.search(r"add_library\(\s*runtimec_interface\s+STATIC\b", text): + findings.append(Finding( + display, 1, "missing-interface-component", + "runtimec_interface static component is required", + )) + actual_dependencies = cmake_target_dependencies(text) + for target, expected in EXPECTED_TARGET_DEPENDENCIES.items(): + actual = actual_dependencies.get(target, set()) + if actual != expected: + findings.append(Finding( + display, 1, "target-dependencies", + f"{target} expected {sorted(expected)!r}, found {sorted(actual)!r}", + )) + return findings + + +def audit(repository_root: Path, runtimec_root: Path) -> tuple[list[Finding], int]: + findings = audit_build_graph(runtimec_root, repository_root) + findings.extend(audit_addressed_binrwir_model(runtimec_root, repository_root)) + scanned = 0 + for source in iter_product_sources(runtimec_root): + scanned += 1 + relative = PurePosixPath(source.relative_to(runtimec_root).as_posix()) + source_layer = layer_for_relative(relative) + display = relative_display(source, repository_root) + if source_layer is None: + findings.append(Finding(display, 1, "unknown-source-layer", + "product source has no owner")) + continue + if (source_layer == "vmbin" and + relative.name in BINRWIR_NEUTRAL_FILE_NAMES): + findings.append(Finding( + display, 1, "binrwir-owned-by-vmbin", + "neutral BinRWIR file must live under binrwir", + )) + try: + lines = source.read_text(encoding="utf-8").splitlines() + except (OSError, UnicodeError) as error: + findings.append(Finding(display, 1, "read-error", str(error))) + continue + + included_layers: set[str] = set() + for line_number, line in enumerate(lines, 1): + match = INCLUDE_PATTERN.match(line) + if not match: + continue + delimiter, token = match.groups() + normalized = token.replace("\\", "/") + if FORBIDDEN_TREE_PATTERN.search(normalized): + findings.append(Finding( + display, line_number, "forbidden-tree-include", + f"{source_layer} includes deleted tree {token!r}", + )) + continue + resolved = resolve_include(token, source, runtimec_root, repository_root) + target_layer = None + if resolved is not None: + try: + target_relative = PurePosixPath( + resolved.relative_to(runtimec_root.resolve()).as_posix() + ) + except ValueError: + continue + target_layer = layer_for_relative(target_relative) + else: + target_layer = inferred_include_layer(normalized) + if delimiter == '"': + findings.append(Finding( + display, line_number, "unresolved-private-include", + f"cannot resolve private include {token!r}", + )) + if target_layer is None: + continue + included_layers.add(target_layer) + if target_layer not in ALLOWED_DEPENDENCIES[source_layer]: + findings.append(Finding( + display, line_number, "forbidden-layer-edge", + f"{source_layer} -> {target_layer} via {token!r}", + )) + + merged_policy_layers = included_layers & POLICY_LAYERS + if (source_layer == "engine" and len(merged_policy_layers) > 1 and + relative.as_posix() not in ROUTE_FILES): + findings.append(Finding( + display, 1, "route-bypass", + "only engine/route may merge binrwir_compiler and vmbin", + )) + return sorted(set(findings)), scanned + + +def main(command_arguments: list[str] | None = None) -> int: + arguments = parse_arguments(command_arguments or sys.argv[1:]) + repository_root, runtimec_root = locate_runtimec(arguments.source_root) + if not runtimec_root.is_dir(): + print(f"{runtimec_root}:1: [missing-runtimec] runtimec source directory does not exist") + return 2 + findings, scanned = audit(repository_root, runtimec_root) + for finding in findings: + print(finding.render()) + if findings: + print(f"check_layers: FAIL ({len(findings)} finding(s), {scanned} file(s) scanned)") + return 1 + if not arguments.quiet: + print(f"check_layers: OK ({scanned} file(s) scanned)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtimec/tools/architecture/check_names.py b/runtimec/tools/architecture/check_names.py new file mode 100644 index 00000000..b8787af1 --- /dev/null +++ b/runtimec/tools/architecture/check_names.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +"""Enforce the runtimec vocabulary and reject alternate architecture paths.""" + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path + + +PRODUCT_DIRECTORIES = ( + "include", "interface", "contract", "host", "binrwir", "vmkv", + "binrwir_compiler", "vmbin", "engine", "generated", + "specification", "tests", +) +TEXT_SUFFIXES = {".c", ".h", ".cmake", ".in", ".map", ".exports", ".def"} +ALLOWED_EXTERNAL_IDENTIFIERS = ("KVSPACE_XVALUE_HEAD_LEN",) +FORBIDDEN_PATH_PARTS = { + "abi": "execution_interface", "api": "interface", "compat": "delete", + "compatibility": "delete", "docs": "documentation", "legacy": "delete", + "spec": "specification", +} +FORBIDDEN_NAMES = ( + ("removed-jit-component-name", re.compile(r"vmjit", re.IGNORECASE)), + ("removed-compiler-target", re.compile(r"runtimec_compiler")), + ("standalone-jit-target", re.compile(r"runtimec_jit")), + ("removed-jit-target", re.compile(r"runtimec_vmjit")), + ("removed-jit-option", re.compile(r"RUNTIMEC_ENABLE_VMJIT")), + ("removed-jit-configuration", re.compile(r"enable_vmjit")), + ("old-binir-name", re.compile(r"\bbinir\b", re.IGNORECASE)), + ("old-binir-identifier", re.compile(r"\bbinir_[A-Za-z0-9_]*\b")), + ("old-runtimec2-name", re.compile(r"\bruntimec2\b")), + ("old-runtime-tree", re.compile(r"(?:^|[\"<])runtime/")), + ("old-success-name", re.compile(r"\bRUNTIMEC_STATUS_OK\b")), + ("removed-fallback-status", re.compile(r"\bRUNTIMEC_STATUS_FALLBACK\b")), + ("removed-jit-execution-entry", re.compile(r"\bbinrwir_jit_try_execute\b")), + ("removed-runtime-deoptimization", re.compile( + r"\bBINRWIR_EXECUTION_EXIT_DEOPTIMIZATION\b", + )), + ("removed-runtime-retry", re.compile( + r"\bBINRWIR_EXECUTION_EXIT_FLAG_RETRY_LOGICAL_PROGRAM_COUNTER\b", + )), + ("removed-static-body-address-resolver", re.compile( + r"\bresolve_static_value_body_address\b", + )), + ("removed-public-body-address-derivation", re.compile( + r"\bruntimec_xvalue_address_to_value_body_address\b", + )), + ("alternate-jit-compilation-cache", re.compile( + r"\b(?:binrwir_jit_code_cache|binrwir_jit_cache_key|" + r"binrwir_jit_code_cache_get_or_compile)\b", + )), + ("binrwir-owned-plan-builder", re.compile( + r"\bbinrwir_execution_plan_build\b", + )), + ("second-jit-plan", re.compile(r"\bbinrwir_jit_plan_[A-Za-z0-9_]*\b")), + ("unobservable-compilation-state", re.compile( + r"\bBINRWIR_COMPILATION_STATE_[A-Za-z0-9_]*\b", + )), + ("execution-time-fallback", re.compile(r"fallback", re.IGNORECASE)), + ("binrwir-under-vmbin", re.compile( + r"(?:^|/)vmbin/(?:value|reference|signature|instruction|program|execution_plan)" + r"\.(?:c|h)$", + re.IGNORECASE, + )), + ("compatibility-language", re.compile( + r"\b(?:backward|compat|compatibility|legacy|forwarding)[ _-]?(?:header|layer|path|support)?\b", + re.IGNORECASE, + )), + ("third-fast-kernel", re.compile(r"\bfast[ _-]?kernel\b", re.IGNORECASE)), + ("third-executor", re.compile( + r"\b(?:fast|quick|direct|native|optimized)[ _-]?(?:executor|interpreter|execute|runner)\b", + re.IGNORECASE, + )), + ("old-fast-operation", re.compile(r"\bfast[ _-]?op(?:eration)?\b", re.IGNORECASE)), + ("alternate-execution-window", re.compile(r"\bexecute[ _-]?window\b", re.IGNORECASE)), + ("old-vmkv-type", re.compile(r"\bVmkv\b")), + ("old-vmbin-type", re.compile(r"\bVmBin\b")), + ("ambiguous-segment-type", re.compile(r"\b(?:Segment|CodeSeg|CInsn)\b")), + ("old-native-context", re.compile(r"\b(?:NativeCtx|NativeFn)\b")), + ("old-frame-type", re.compile(r"\b(?:FrameCtx|RunResult)\b")), + ("removed-unobservable-memory-frame", re.compile( + r"\bmem[ _-]?frame\b", re.IGNORECASE, + )), + ("removed-segment-may-stop", re.compile( + r"\bsegment[ _-]?may[ _-]?stop\b", re.IGNORECASE, + )), + ("old-runtimec2-macro", re.compile(r"\b(?:RC2|KV2)_[A-Za-z0-9_]*\b")), + ("old-runtimec2-test-name", re.compile(r"\bc2_[A-Za-z0-9_]*\b")), + ("old-codeseg-name", re.compile(r"\bcodeseg\b", re.IGNORECASE)), +) +ABBREVIATIONS = { + "abi": "binary_interface or interface", "api": "interface", + "arg": "argument", "buf": "buffer", "cfg": "control_flow_graph", + "ctx": "context", "desc": "descriptor", "dst": "destination", + "fn": "function", "id": "identifier", "impl": "implementation", + "len": "length", "op": "operation", "pc": "program_counter", + "ptr": "pointer", "ref": "reference", "req": "request", + "src": "source", "tmp": "temporary", +} +ABBREVIATION_PATTERNS = tuple( + (abbreviation, replacement, re.compile( + rf"(? str: + return f"{self.path}:{self.line}: [{self.rule}] forbidden name {self.token!r}" + + +def parse_arguments(command_arguments: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--source-root", type=Path, + default=Path(__file__).resolve().parents[3], + help="repository source root, or the runtimec directory itself", + ) + parser.add_argument("--quiet", action="store_true", help="suppress success output") + return parser.parse_args(command_arguments) + + +def locate_runtimec(source_root: Path) -> tuple[Path, Path]: + source_root = source_root.resolve() + return ((source_root.parent, source_root) if source_root.name == "runtimec" + else (source_root, source_root / "runtimec")) + + +def relative_display(path: Path, repository_root: Path) -> str: + try: + return path.resolve().relative_to(repository_root).as_posix() + except ValueError: + return path.as_posix() + + +def iter_product_files(runtimec_root: Path): + cmake_lists = runtimec_root / "CMakeLists.txt" + if cmake_lists.is_file(): + yield cmake_lists + for directory_name in PRODUCT_DIRECTORIES: + directory = runtimec_root / directory_name + if directory.is_dir(): + for path in sorted(directory.rglob("*")): + if path.is_file() and ( + path.suffix in TEXT_SUFFIXES or path.name == "CMakeLists.txt" + ): + yield path + + +def audit_public_headers(runtimec_root: Path, repository_root: Path) -> list[Finding]: + include_root = runtimec_root / "include" + allowed_header = include_root / "kvlang" / "runtimec.h" + return [ + Finding(relative_display(header, repository_root), 1, + "noncanonical-public-header", + header.relative_to(include_root).as_posix()) + for header in sorted(include_root.rglob("*.h")) + if header != allowed_header + ] if include_root.is_dir() else [] + + +def c_code_without_comments_or_literals(text: str) -> str: + return C_NON_CODE_PATTERN.sub( + lambda match: re.sub(r"[^\n]", " ", match.group(0)), text, + ) + + +def audit(repository_root: Path, runtimec_root: Path) -> tuple[list[Finding], int]: + findings = audit_public_headers(runtimec_root, repository_root) + scanned = 0 + forbidden_directories = { + "api": "interface", + "compat": "delete", + "compiler": "binrwir_compiler", + "docs": "documentation", + "jit": "binrwir_compiler/jit", + "spec": "specification", + "vmjit": "binrwir_compiler/jit", + } + for forbidden_directory, replacement in forbidden_directories.items(): + path = runtimec_root / forbidden_directory + if path.exists(): + findings.append(Finding(relative_display(path, repository_root), 1, + "forbidden-directory", + f"{forbidden_directory} -> {replacement}")) + + for path in iter_product_files(runtimec_root): + scanned += 1 + display = relative_display(path, repository_root) + relative_path = path.relative_to(runtimec_root) + for part in relative_path.parts: + replacement = FORBIDDEN_PATH_PARTS.get(part.lower()) + if replacement is not None: + findings.append(Finding(display, 1, "forbidden-path-name", + f"{part} -> {replacement}")) + for rule, pattern in FORBIDDEN_NAMES: + match = pattern.search(relative_path.as_posix()) + if match: + findings.append(Finding(display, 1, f"{rule}-in-path", match.group(0))) + try: + text = path.read_text(encoding="utf-8") + lines = text.splitlines() + except (OSError, UnicodeError) as error: + findings.append(Finding(display, 1, "read-error", str(error))) + continue + if path.suffix in {".c", ".h"}: + code = c_code_without_comments_or_literals(text) + for match in VOID_LIFECYCLE_PATTERN.finditer(code): + findings.append(Finding( + display, + code.count("\n", 0, match.start()) + 1, + "void-lifecycle-function", + match.group(1), + )) + for line_number, line in enumerate(lines, 1): + for rule, pattern in FORBIDDEN_NAMES: + for match in pattern.finditer(line): + findings.append(Finding(display, line_number, rule, match.group(0))) + if path.name == "CMakeLists.txt": + continue + abbreviation_line = line + for identifier in ALLOWED_EXTERNAL_IDENTIFIERS: + abbreviation_line = abbreviation_line.replace( + identifier, " " * len(identifier), + ) + for abbreviation, replacement, pattern in ABBREVIATION_PATTERNS: + for match in pattern.finditer(abbreviation_line): + findings.append(Finding( + display, line_number, + f"abbreviation-{abbreviation}-use-{replacement}", match.group(0), + )) + return sorted(set(findings)), scanned + + +def main(command_arguments: list[str] | None = None) -> int: + arguments = parse_arguments(command_arguments or sys.argv[1:]) + repository_root, runtimec_root = locate_runtimec(arguments.source_root) + if not runtimec_root.is_dir(): + print(f"{runtimec_root}:1: [missing-runtimec] runtimec source directory does not exist") + return 2 + findings, scanned = audit(repository_root, runtimec_root) + for finding in findings: + print(finding.render()) + if findings: + print(f"check_names: FAIL ({len(findings)} finding(s), {scanned} file(s) scanned)") + return 1 + if not arguments.quiet: + print(f"check_names: OK ({scanned} file(s) scanned)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtimec/vmbin/builtin/registry.c b/runtimec/vmbin/builtin/registry.c new file mode 100644 index 00000000..81aceee2 --- /dev/null +++ b/runtimec/vmbin/builtin/registry.c @@ -0,0 +1,54 @@ +/* + * 组件:vmbin/builtin/registry 的实现 + * 作用:初始化只读条目表;查找业务保持失败关闭。 + */ + +#include "registry.h" + +#include + +runtimec_status_t +vmbin_builtin_registry_initialize(vmbin_builtin_registry_t *registry, + const vmbin_builtin_entry_t *entries, + size_t entry_count) { + if (registry == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(registry, 0, sizeof(*registry)); + if (entry_count != 0U && entries == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + registry->entries = entries; + registry->entry_count = entry_count; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t vmbin_builtin_registry_reset( + vmbin_builtin_registry_t *registry) { + if (registry == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(registry, 0, sizeof(*registry)); + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t vmbin_builtin_registry_destroy( + vmbin_builtin_registry_t *registry) { + return vmbin_builtin_registry_reset(registry); +} + +runtimec_status_t +vmbin_builtin_registry_find(const vmbin_builtin_registry_t *registry, + uint32_t builtin_identifier, + const vmbin_builtin_entry_t **output_entry) { + (void)registry; + (void)builtin_identifier; + if (output_entry != NULL) { + *output_entry = NULL; + } + if (registry == NULL || builtin_identifier == BINRWIR_IDENTIFIER_NONE || + output_entry == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/vmbin/builtin/registry.h b/runtimec/vmbin/builtin/registry.h new file mode 100644 index 00000000..d9934fca --- /dev/null +++ b/runtimec/vmbin/builtin/registry.h @@ -0,0 +1,65 @@ +#ifndef VMBIN_BUILTIN_REGISTRY_H +#define VMBIN_BUILTIN_REGISTRY_H + +/* + * 组件:vmbin/builtin/registry + * 作用:将 builtin 标识符映射到唯一处理函数。 + * 联系:interpreter 调用;处理函数从 instruction 的 argaddr 直接访问 + * kvspace V.body。 + * 约束:Registry 不拥有条目或处理函数,生命周期属于 session;处理函数 + * 不得缓存 V.body 内容或建立寄存器文件。 + */ + +#include +#include + +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" +#include "binrwir/instruction.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef runtimec_status_t +(*vmbin_builtin_handler_function_t)( + binrwir_execution_frame_t *frame, + const binrwir_instruction_view_t *instruction, + binrwir_execution_exit_t *output_exit); + +typedef enum vmbin_builtin_flags { + VMBIN_BUILTIN_FLAG_NONE = 0, + VMBIN_BUILTIN_FLAG_EFFECT = 1U << 0, + VMBIN_BUILTIN_FLAG_MAY_YIELD = 1U << 1 +} vmbin_builtin_flags_t; + +typedef struct vmbin_builtin_entry { + uint32_t builtin_identifier; + uint32_t flags; + vmbin_builtin_handler_function_t handler; +} vmbin_builtin_entry_t; + +typedef struct vmbin_builtin_registry { + const vmbin_builtin_entry_t *entries; + size_t entry_count; +} vmbin_builtin_registry_t; + +runtimec_status_t +vmbin_builtin_registry_initialize(vmbin_builtin_registry_t *registry, + const vmbin_builtin_entry_t *entries, + size_t entry_count); +runtimec_status_t vmbin_builtin_registry_reset( + vmbin_builtin_registry_t *registry); +runtimec_status_t vmbin_builtin_registry_destroy( + vmbin_builtin_registry_t *registry); +runtimec_status_t +vmbin_builtin_registry_find(const vmbin_builtin_registry_t *registry, + uint32_t builtin_identifier, + const vmbin_builtin_entry_t **output_entry); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmbin/call.c b/runtimec/vmbin/call.c new file mode 100644 index 00000000..df778173 --- /dev/null +++ b/runtimec/vmbin/call.c @@ -0,0 +1,79 @@ +/* + * 组件:vmbin/call 的实现 + * 作用:只含控制元数据的调用关系与进出 Frame 失败桩。 + * 约束:未实现必须同时返回结构化错误和未实现状态。 + */ + +#include "vmbin/call.h" + +static void vmbin_call_initialize_error_exit( + binrwir_execution_exit_t *output_exit, runtimec_status_t status) { + *output_exit = (binrwir_execution_exit_t){0}; + output_exit->kind = BINRWIR_EXECUTION_EXIT_ERROR; + output_exit->status = status; + output_exit->logical_program_counter = + BINRWIR_EXECUTION_PROGRAM_COUNTER_NONE; + output_exit->resume_logical_program_counter = + BINRWIR_EXECUTION_PROGRAM_COUNTER_NONE; +} + +runtimec_status_t vmbin_call_control_create( + const binrwir_execution_frame_t *caller, + const binrwir_program_t *callee, + const vmbin_call_request_t *request, + const runtimec_allocator_t *allocator, + vmbin_call_control_t **output_control) { + (void)caller; + (void)callee; + (void)request; + (void)allocator; + if (output_control != NULL) { + *output_control = NULL; + } + if (caller == NULL || callee == NULL || request == NULL || + request->call_instruction_identifier == BINRWIR_IDENTIFIER_NONE || + allocator == NULL || output_control == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} +runtimec_status_t vmbin_call_control_destroy(vmbin_call_control_t *control) { + if (control == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} +runtimec_status_t vmbin_call_enter( + binrwir_execution_frame_t *caller, + binrwir_execution_frame_t *callee, + const vmbin_call_control_t *control, + binrwir_execution_exit_t *output_exit) { + if (output_exit != NULL) { + vmbin_call_initialize_error_exit(output_exit, + RUNTIMEC_STATUS_INVALID); + } + if (caller == NULL || callee == NULL || control == NULL || + output_exit == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + vmbin_call_initialize_error_exit(output_exit, + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} +runtimec_status_t vmbin_call_return( + binrwir_execution_frame_t *caller, + const binrwir_execution_frame_t *callee, + const vmbin_call_control_t *control, + binrwir_execution_exit_t *output_exit) { + if (output_exit != NULL) { + vmbin_call_initialize_error_exit(output_exit, + RUNTIMEC_STATUS_INVALID); + } + if (caller == NULL || callee == NULL || control == NULL || + output_exit == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + vmbin_call_initialize_error_exit(output_exit, + RUNTIMEC_STATUS_NOT_IMPLEMENTED); + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/vmbin/call.h b/runtimec/vmbin/call.h new file mode 100644 index 00000000..4e4647d5 --- /dev/null +++ b/runtimec/vmbin/call.h @@ -0,0 +1,52 @@ +#ifndef VMBIN_CALL_H +#define VMBIN_CALL_H + +/* + * 组件:vmbin/call + * 作用:按 canonical BinRWIR Program 的签名建立控制流调用关系。 + * 联系:engine route 驱动调用;指令中的 argaddr 是参数 V.body 的唯一位置。 + * 约束:调用对象只能保存控制转移元数据,不得保存 V.body 内容、Cell、value + * slot 或第二份地址缓存;调用不得重复已经提交的副作用。 + */ + +#include + +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" +#include "binrwir/program.h" +#include "contract/allocator.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct vmbin_call_control vmbin_call_control_t; +typedef struct vmbin_call_request { + uint32_t call_instruction_identifier; + uint32_t return_logical_program_counter; +} vmbin_call_request_t; + +runtimec_status_t vmbin_call_control_create( + const binrwir_execution_frame_t *caller, + const binrwir_program_t *callee, + const vmbin_call_request_t *request, + const runtimec_allocator_t *allocator, + vmbin_call_control_t **output_control); +runtimec_status_t vmbin_call_control_destroy(vmbin_call_control_t *control); +runtimec_status_t vmbin_call_enter( + binrwir_execution_frame_t *caller, + binrwir_execution_frame_t *callee, + const vmbin_call_control_t *control, + binrwir_execution_exit_t *output_exit); +runtimec_status_t vmbin_call_return( + binrwir_execution_frame_t *caller, + const binrwir_execution_frame_t *callee, + const vmbin_call_control_t *control, + binrwir_execution_exit_t *output_exit); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmbin/interpreter.c b/runtimec/vmbin/interpreter.c new file mode 100644 index 00000000..ac23a8c6 --- /dev/null +++ b/runtimec/vmbin/interpreter.c @@ -0,0 +1,37 @@ +/* + * 组件:vmbin/interpreter 的实现 + * 作用:唯一 vmbin 执行入口的失败桩。 + * 约束:所有解释策略只能位于本执行器内部,并通过 argaddr 直接访问 + * kvspace V.body;不得建立值缓存或第三执行器。 + */ + +#include "interpreter.h" + +#include + +runtimec_status_t +vmbin_execute(const binrwir_program_t *program, + const binrwir_execution_plan_t *execution_plan, + binrwir_execution_frame_t *frame, + const vmbin_builtin_registry_t *builtin_registry, + vmbin_profile_t *profile, + binrwir_execution_exit_t *output_exit) { + (void)execution_plan; + (void)profile; + + if (output_exit != NULL) { + memset(output_exit, 0, sizeof(*output_exit)); + output_exit->kind = BINRWIR_EXECUTION_EXIT_ERROR; + output_exit->status = RUNTIMEC_STATUS_INVALID; + output_exit->logical_program_counter = + BINRWIR_EXECUTION_PROGRAM_COUNTER_NONE; + output_exit->resume_logical_program_counter = + BINRWIR_EXECUTION_PROGRAM_COUNTER_NONE; + } + if (program == NULL || frame == NULL || builtin_registry == NULL || + output_exit == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + output_exit->status = RUNTIMEC_STATUS_NOT_IMPLEMENTED; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} diff --git a/runtimec/vmbin/interpreter.h b/runtimec/vmbin/interpreter.h new file mode 100644 index 00000000..b2e74189 --- /dev/null +++ b/runtimec/vmbin/interpreter.h @@ -0,0 +1,39 @@ +#ifndef VMBIN_INTERPRETER_H +#define VMBIN_INTERPRETER_H + +/* + * 组件:vmbin/interpreter + * 作用:定义唯一完整的 BinRWIR 正确性执行入口。 + * 联系:engine route 是唯一调用者;BinRWIR Compiler 可提供只读执行计划。 + * 约束:执行计划可为空;此时仍须执行全部合法 canonical BinRWIR。每个参数 + * 只通过 instruction 的 64 位 argaddr 直接访问 kvspace V.body。VMBIN + * 不得缓存 V.body,不得拥有 Cell、value slot、register file、spill 或 + * dirty value cache;写结果落入 V.body 后才能推进逻辑程序计数器。 + */ + +#include "binrwir/execution_exit.h" +#include "binrwir/execution_frame.h" +#include "binrwir/execution_plan.h" +#include "binrwir/program.h" +#include "builtin/registry.h" +#include "contract/status.h" +#include "profile.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 从 Frame 的当前逻辑程序计数器开始执行,直到产生结构化退出。 */ +runtimec_status_t +vmbin_execute(const binrwir_program_t *program, + const binrwir_execution_plan_t *execution_plan, + binrwir_execution_frame_t *frame, + const vmbin_builtin_registry_t *builtin_registry, + vmbin_profile_t *profile, + binrwir_execution_exit_t *output_exit); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmbin/profile.c b/runtimec/vmbin/profile.c new file mode 100644 index 00000000..01cc64e7 --- /dev/null +++ b/runtimec/vmbin/profile.c @@ -0,0 +1,50 @@ +/* + * 组件:vmbin/profile 的实现 + * 作用:初始化、清零和读取调用方拥有的统计存储。 + */ + +#include "profile.h" + +#include + +runtimec_status_t +vmbin_profile_initialize(vmbin_profile_t *profile, + vmbin_profile_counter_t *counter_storage, + size_t counter_count) { + if (profile == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(profile, 0, sizeof(*profile)); + if (counter_count != 0U && counter_storage == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + if (counter_storage != NULL) { + memset(counter_storage, 0, counter_count * sizeof(*counter_storage)); + } + profile->counters = counter_storage; + profile->counter_count = counter_count; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t vmbin_profile_reset(vmbin_profile_t *profile) { + if (profile == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + if (profile->counters != NULL) { + memset(profile->counters, 0, + profile->counter_count * sizeof(*profile->counters)); + } + profile->frame_count = 0U; + profile->instruction_count = 0U; + profile->flags = 0U; + profile->reserved_32_bits = 0U; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t vmbin_profile_destroy(vmbin_profile_t *profile) { + if (profile == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + memset(profile, 0, sizeof(*profile)); + return RUNTIMEC_STATUS_SUCCESS; +} diff --git a/runtimec/vmbin/profile.h b/runtimec/vmbin/profile.h new file mode 100644 index 00000000..bd489749 --- /dev/null +++ b/runtimec/vmbin/profile.h @@ -0,0 +1,46 @@ +#ifndef VMBIN_PROFILE_H +#define VMBIN_PROFILE_H + +/* + * 组件:vmbin/profile + * 作用:保存 session 拥有的可丢弃执行统计。 + * 联系:interpreter 更新;engine session 持有。 + * 约束:统计不是正确性依赖,也不得进入不可变 Program;只记录计数,绝不 + * 保存 argaddr 指向的 V.body 内容。 + */ + +#include +#include + +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct vmbin_profile_counter { + uint64_t instruction_count; + uint64_t dispatch_ticks; +} vmbin_profile_counter_t; + +typedef struct vmbin_profile { + vmbin_profile_counter_t *counters; + size_t counter_count; + uint64_t frame_count; + uint64_t instruction_count; + uint32_t flags; + uint32_t reserved_32_bits; +} vmbin_profile_t; + +runtimec_status_t +vmbin_profile_initialize(vmbin_profile_t *profile, + vmbin_profile_counter_t *counter_storage, + size_t counter_count); +runtimec_status_t vmbin_profile_reset(vmbin_profile_t *profile); +runtimec_status_t vmbin_profile_destroy(vmbin_profile_t *profile); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmkv/encoder.h b/runtimec/vmkv/encoder.h new file mode 100644 index 00000000..339b6ca4 --- /dev/null +++ b/runtimec/vmkv/encoder.h @@ -0,0 +1,76 @@ +#ifndef RUNTIMEC_VMKV_ENCODER_H +#define RUNTIMEC_VMKV_ENCODER_H + +/* + * 组件:vmkv/encoder + * 作用:读取并验证明文 RWIR,将 operation name 和 typed named parameters + * 映射为 opid/argidx,再把静态参数 key 解引用为非零 64 位 argaddr, + * 最终等价编码为 canonical、不可变的 binrwir_program_t。 + * 联系:engine route 在 compilation repository 未命中时调用;源只经 + * vmkv/source 与主机服务读取;结果交给 BinRWIR Compiler。 + * 约束:vmkv 只有 RWIR 到 canonical BinRWIR 这一条正常方向。每个静态 key + * 必须通过 host service resolve_static_xvalue_address 取得 XValue 基址, + * 再由合同唯一函数推导 V.body 地址;动态 key、零地址、地址溢出及无法保证 + * snapshot 生命周期的地址返回 UNSUPPORTED 或错误,且不产生 Program。 + * 编码不得优化、融合、生成执行计划或机器码,也不得执行。 + */ + +#include "binrwir/program.h" +#include "contract/allocator.h" +#include "contract/snapshot.h" +#include "contract/status.h" +#include "vmkv/rwir.h" +#include "vmkv/source.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct vmkv_encoder_t vmkv_encoder_t; + +/* 一次等价编码请求。RWIR 验证是强制步骤,不提供绕过开关。 */ +typedef struct vmkv_encode_request_t { + const runtimec_host_services_t *host_services; + runtimec_string_view_t function_path; + runtimec_snapshot_t expected_snapshot; +} vmkv_encode_request_t; + +runtimec_status_t vmkv_encoder_create( + runtimec_allocator_t allocator, + vmkv_encoder_t **output_encoder, + runtimec_error_t *error); + +runtimec_status_t vmkv_encoder_destroy(vmkv_encoder_t *encoder); + +runtimec_status_t vmkv_encoder_get_canonical_encoding_revision( + const vmkv_encoder_t *encoder, + uint32_t *output_canonical_encoding_revision); + +/* + * VMKV 地址化的唯一窄入口。它先验证明文 parameter type/name/key,再拒绝 + * dynamic key;静态 key 只能由 host service 的非零 XValue 基址安全推导 V.body + * 地址后生成 argument。该函数不读取 V.body,也不缓存 key、XValue 或 body。 + */ +runtimec_status_t vmkv_encode_static_argument( + const vmkv_encode_request_t *request, + const vmkv_rwir_argument_view_t *source_argument, + uint32_t argument_index, + binrwir_argument_t *output_argument, + runtimec_error_t *error); + +/* + * 成功时只返回与已验证 RWIR 等价的 canonical BinRWIR Program。任一 instruction + * 的 operation name、parameter type/name 或 argidx 映射不匹配时失败;遇到动态 + * key 必须返回 UNSUPPORTED,并保持 output_program 为 NULL。 + */ +runtimec_status_t vmkv_encode( + vmkv_encoder_t *encoder, + const vmkv_encode_request_t *request, + binrwir_program_t **output_program, + runtimec_error_t *error); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmkv/rwir.h b/runtimec/vmkv/rwir.h new file mode 100644 index 00000000..8662a897 --- /dev/null +++ b/runtimec/vmkv/rwir.h @@ -0,0 +1,81 @@ +#ifndef RUNTIMEC_VMKV_RWIR_H +#define RUNTIMEC_VMKV_RWIR_H + +/* + * 组件:vmkv/rwir + * 作用:明文 RWIR instruction 的只读视图与顺序读取器。operation name、 + * parameter type、parameter name 和 key expression 都保留到 VMKV 完成 + * manifest 校验和地址解析;Read/Write 方向由两个参数分区显式表达。 + * 联系:vmkv/source 提供函数源;vmkv/encoder 将已验证名称映射为 opid/argidx, + * 并只把静态 key 解引用为 argaddr。vmbin 与 JIT 不直接消费本结构。 + * 约束:所有视图都只借用内存。读取器不执行、不优化、不生成机器码、不修复 + * 输入。动态 key 只能被识别并拒绝,不能进入 canonical BinRWIR。 + */ + +#include +#include + +#include "contract/view.h" +#include "contract/status.h" +#include "vmkv/source.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum vmkv_key_addressability_t { + VMKV_KEY_ADDRESSABILITY_INVALID = 0, + VMKV_KEY_ADDRESSABILITY_STATIC = 1, + VMKV_KEY_ADDRESSABILITY_DYNAMIC = 2 +} vmkv_key_addressability_t; + +/* + * 单个明文参数。parameter_type_name 与 parameter_name 必须和 operation + * manifest 完全匹配;key 是静态 key 或尚待求值的动态 key expression。 + */ +typedef struct vmkv_rwir_argument_view_t { + runtimec_string_view_t parameter_type_name; + runtimec_string_view_t parameter_name; + runtimec_key_view_t key; + vmkv_key_addressability_t key_addressability; +} vmkv_rwir_argument_view_t; + +/* 一条 RWIR 指令及其两个方向分区。 */ +typedef struct vmkv_rwir_instruction_view_t { + runtimec_string_view_t operation_name; + uint32_t logical_program_counter; + const vmkv_rwir_argument_view_t *read_arguments; + size_t read_argument_count; + const vmkv_rwir_argument_view_t *write_arguments; + size_t write_argument_count; +} vmkv_rwir_instruction_view_t; + +/* 组件状态只表示调用成败;读取位置由本枚举表达。 */ +typedef enum vmkv_rwir_read_result_t { + VMKV_RWIR_READ_RESULT_INVALID = 0, + VMKV_RWIR_READ_RESULT_INSTRUCTION = 1, + VMKV_RWIR_READ_RESULT_END = 2 +} vmkv_rwir_read_result_t; + +typedef struct vmkv_rwir_reader_t vmkv_rwir_reader_t; + +/* 在函数源上创建顺序读取器;不拥有 function。 */ +runtimec_status_t vmkv_rwir_reader_create( + vmkv_function_source_t *function, + vmkv_rwir_reader_t **output_reader, + runtimec_error_t *error); + +/* 成功读取或到达结尾都返回 RUNTIMEC_STATUS_SUCCESS,读取结果单独表达。 */ +runtimec_status_t vmkv_rwir_reader_next( + vmkv_rwir_reader_t *reader, + vmkv_rwir_read_result_t *output_result, + vmkv_rwir_instruction_view_t *output_instruction, + runtimec_error_t *error); + +runtimec_status_t vmkv_rwir_reader_destroy(vmkv_rwir_reader_t *reader); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmkv/source.h b/runtimec/vmkv/source.h new file mode 100644 index 00000000..91a68466 --- /dev/null +++ b/runtimec/vmkv/source.h @@ -0,0 +1,42 @@ +#ifndef RUNTIMEC_VMKV_SOURCE_H +#define RUNTIMEC_VMKV_SOURCE_H + +/* + * 组件:vmkv/source + * 作用:通过主机服务打开一个函数的明文 RWIR 源,并返回源快照。源保留 + * operation name、parameter type、parameter name 与 parameter key。 + * 它是 vmkv 与 kvspace 之间唯一的读取入口,vmkv 不直接包含 + * kvspace 结构。 + * 联系:vmkv/encoder 使用本接口读取 RWIR;读取结果只能进入顶层 + * binrwir 的不可变 Program 构建器。 + * 约束:打开源不执行 RWIR、不生成机器码、不修改执行事实;失败 + * 不得留下函数源。 + */ + +#include "contract/host_services.h" +#include "contract/snapshot.h" +#include "contract/status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 一次函数读取的句柄;生命周期内保证视图可借用。 */ +typedef struct vmkv_function_source_t vmkv_function_source_t; + +/* 打开函数源;失败时输出保持明确无效。 */ +runtimec_status_t vmkv_source_open_function( + const runtimec_host_services_t *host_services, + runtimec_string_view_t function_path, + vmkv_function_source_t **output_function, + runtimec_snapshot_t *output_snapshot, + runtimec_error_t *error); + +/* 关闭函数源;不提交、不修改 kvspace 事实。 */ +runtimec_status_t vmkv_source_close_function(vmkv_function_source_t *function); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/runtimec/vmkv/vmkv.c b/runtimec/vmkv/vmkv.c new file mode 100644 index 00000000..c4d0c8c3 --- /dev/null +++ b/runtimec/vmkv/vmkv.c @@ -0,0 +1,207 @@ +/* + * 组件:vmkv 未实现入口 + * 作用:先把输出置为明确无效,再验证接口形状,最后返回 + * RUNTIMEC_STATUS_NOT_IMPLEMENTED。 + * 约束:不产生空 Program 伪装成功,不执行或优化 RWIR,不生成机器码。 + * 当前只实现静态 argument 地址化合同,不实现完整 RWIR 编码。 + */ + +#include "vmkv/encoder.h" +#include "vmkv/rwir.h" +#include "vmkv/source.h" + +#include + +static bool vmkv_named_argument_is_valid( + const vmkv_rwir_argument_view_t *argument) { + return argument != NULL && + runtimec_string_view_is_valid(argument->parameter_type_name) && + argument->parameter_type_name.size > 0U && + runtimec_string_view_is_valid(argument->parameter_name) && + argument->parameter_name.size > 0U && + runtimec_key_view_is_valid(argument->key) && + argument->key.size > 0U && + (argument->key_addressability == VMKV_KEY_ADDRESSABILITY_STATIC || + argument->key_addressability == VMKV_KEY_ADDRESSABILITY_DYNAMIC); +} + +static bool vmkv_derive_value_body_address( + runtimec_xvalue_address_t xvalue_address, + runtimec_value_body_address_t *output_body_address) { + if (output_body_address == NULL) { + return false; + } + *output_body_address = RUNTIMEC_VALUE_BODY_ADDRESS_NONE; + if (xvalue_address == RUNTIMEC_XVALUE_ADDRESS_NONE || + xvalue_address > + UINT64_MAX - RUNTIMEC_XVALUE_BODY_OFFSET_BYTES) { + return false; + } + *output_body_address = + xvalue_address + RUNTIMEC_XVALUE_BODY_OFFSET_BYTES; + return true; +} + +runtimec_status_t vmkv_source_open_function( + const runtimec_host_services_t *host_services, + runtimec_string_view_t function_path, + vmkv_function_source_t **output_function, + runtimec_snapshot_t *output_snapshot, + runtimec_error_t *error) { + if (output_function != NULL) { + *output_function = NULL; + } + if (output_snapshot != NULL) { + runtimec_snapshot_reset(output_snapshot); + } + if (!runtimec_host_services_is_valid(host_services) || + !runtimec_string_view_is_valid(function_path) || + output_function == NULL || output_snapshot == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_source_close_function(vmkv_function_source_t *function) { + if (function == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_rwir_reader_create( + vmkv_function_source_t *function, + vmkv_rwir_reader_t **output_reader, + runtimec_error_t *error) { + if (output_reader != NULL) { + *output_reader = NULL; + } + if (function == NULL || output_reader == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_rwir_reader_next( + vmkv_rwir_reader_t *reader, + vmkv_rwir_read_result_t *output_result, + vmkv_rwir_instruction_view_t *output_instruction, + runtimec_error_t *error) { + if (output_result != NULL) { + *output_result = VMKV_RWIR_READ_RESULT_INVALID; + } + if (output_instruction != NULL) { + *output_instruction = (vmkv_rwir_instruction_view_t){0}; + } + if (reader == NULL || output_result == NULL || output_instruction == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_rwir_reader_destroy(vmkv_rwir_reader_t *reader) { + if (reader == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_encoder_create( + runtimec_allocator_t allocator, + vmkv_encoder_t **output_encoder, + runtimec_error_t *error) { + if (output_encoder != NULL) { + *output_encoder = NULL; + } + if (!runtimec_allocator_is_valid(&allocator) || output_encoder == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_encoder_destroy(vmkv_encoder_t *encoder) { + if (encoder == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_encoder_get_canonical_encoding_revision( + const vmkv_encoder_t *encoder, + uint32_t *output_canonical_encoding_revision) { + if (output_canonical_encoding_revision != NULL) { + *output_canonical_encoding_revision = UINT32_MAX; + } + if (encoder == NULL || output_canonical_encoding_revision == NULL) { + return RUNTIMEC_STATUS_INVALID; + } + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +} + +runtimec_status_t vmkv_encode_static_argument( + const vmkv_encode_request_t *request, + const vmkv_rwir_argument_view_t *source_argument, + uint32_t argument_index, + binrwir_argument_t *output_argument, + runtimec_error_t *error) { + if (output_argument != NULL) { + memset(output_argument, 0, sizeof(*output_argument)); + } + if (request == NULL || output_argument == NULL || + argument_index == BINRWIR_ARGUMENT_INDEX_NONE || + !runtimec_host_services_is_valid(request->host_services) || + !runtimec_string_view_is_valid(request->function_path) || + !runtimec_snapshot_is_valid(&request->expected_snapshot) || + !vmkv_named_argument_is_valid(source_argument)) { + return RUNTIMEC_STATUS_INVALID; + } + if (source_argument->key_addressability == + VMKV_KEY_ADDRESSABILITY_DYNAMIC) { + return RUNTIMEC_STATUS_UNSUPPORTED; + } + if (request->host_services->resolve_static_xvalue_address == NULL) { + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; + } + + runtimec_xvalue_address_t xvalue_address = RUNTIMEC_XVALUE_ADDRESS_NONE; + runtimec_status_t status = + request->host_services->resolve_static_xvalue_address( + request->host_services->context, &request->expected_snapshot, + source_argument->key, &xvalue_address, error); + if (status != RUNTIMEC_STATUS_SUCCESS) { + return status; + } + + runtimec_value_body_address_t body_address = + RUNTIMEC_VALUE_BODY_ADDRESS_NONE; + if (!vmkv_derive_value_body_address(xvalue_address, &body_address)) { + return RUNTIMEC_STATUS_ERROR; + } + + output_argument->body_address = body_address; + output_argument->argument_index = argument_index; + output_argument->reserved_32_bits = 0U; + return RUNTIMEC_STATUS_SUCCESS; +} + +runtimec_status_t vmkv_encode( + vmkv_encoder_t *encoder, + const vmkv_encode_request_t *request, + binrwir_program_t **output_program, + runtimec_error_t *error) { + if (output_program != NULL) { + *output_program = NULL; + } + if (encoder == NULL || request == NULL || output_program == NULL || + !runtimec_host_services_is_valid(request->host_services) || + !runtimec_string_view_is_valid(request->function_path) || + !runtimec_snapshot_is_valid(&request->expected_snapshot)) { + return RUNTIMEC_STATUS_INVALID; + } + (void)error; + return RUNTIMEC_STATUS_NOT_IMPLEMENTED; +}