观察
#116 把控制流摊平(if/while/for/break/continue → goto/br 到 int64 指令号,runtime 不再建 scope 帧、无 _then_/_else_/_while_ 目录)之后,kvlang 的运行时地址退回成三个数字轴:
PC = /vthread/<vtid>/[d]/[i,j]
└┬┘ └┬┘
函数轴 d │ └ 指令参数轴 j(读参负 / 写参正 / opcode=0)
(调用栈深度,栈式) └ 指令轴 i(函数内平坦指令号,1..N)
这个形态和经典字节码 VM(CPython/Lua/JVM)结构上非常接近。本 issue 用 CPython 3.12 的真实字节码逐条对比,厘清:哪些是已经收敛的经典模型、哪些是 kvlang 承重的本质差异、哪些可以反过来向成熟字节码借鉴 。不改代码,先定认知与方向。
一、同一函数,两侧真实产物
源(两侧同构):
absval(x): if x < 0 { r = 0 - x } else { r = x }; return r
CPython 3.12 dis.dis (PC = co_code 里的整数字节偏移):
2 LOAD_FAST 0(x); LOAD_CONST 1(0); COMPARE_OP <(2); POP_JUMP_IF_FALSE →26
3 LOAD_CONST 1(0); LOAD_FAST 0(x); BINARY_OP -(10); STORE_FAST 1(r)
6 LOAD_FAST 1(r); RETURN_VALUE
5 > LOAD_FAST 0(x); STORE_FAST 1(r)
6 LOAD_FAST 1(r); RETURN_VALUE
co_varnames=('x','r') co_consts=(None,0) # 局部=整数下标, LOAD_FAST 走 co_varnames[i]
kvlang kvlang dump (PC = KV 路径,指令按 [i,j] 逐槽铺开):
[1,0] goto [1,-1]=2 # 跳到指令 2
[2,0] int64·lt [2,-1]=x [2,-2]=0 [2,1]=_1
[3,0] br [3,-1]=_1 [3,-2]=4 [3,-3]=6 # 真→4 假→6
[4,0] int64·sub [4,-1]=0 [4,-2]=x [4,1]=r
[5,0] goto [5,-1]=8
[6,0] = [6,-1]=x [6,1]=r
[7,0] goto [7,-1]=8
[8,0] return
# x →[0,-1](Ptr) r →[0,1](Ptr) # 名字→slot 的命名 Ptr
二、已经收敛的部分(和经典字节码一样)
维度
CPython
kvlang(#116 后)
收敛?
每函数一段平坦线性指令 ,源码块结构被摊掉
co_code 字节序列
/lib/<func>/[i,*],i=1..N
✅ 相同
分支 = 跳到一个指令号
POP_JUMP_IF_FALSE/JUMP_BACKWARD 到字节偏移
goto/br 到 int64 irseq(kvcpu.c:32 jump_irseq)
✅ 相同模型
代码模板共享 + 每次调用一帧
一个 code object,每调用一个 frame
/lib/<func> + 每帧一次 extindex(kvspace篇-03)
✅ 相同
局部在"每帧命名空间",键由编译/布局期定死
co_varnames 整数下标
名字→slot 的 Ptr,layout 期解析
✅ 逻辑相同
调用栈
frame 对象链
帧按深度编号 [d](keytree.c:53)
✅ 逻辑相同(栈式)
用户的观察成立 :摊平之后,kvlang 的地址骨架就是"经典字节码 + 帧栈"。
三、承重的本质差异(不是退化,是 kvlang 立身之本)
指令参数轴 j = 三地址,不是栈式。 CPython 是栈机 :指令不显式寻址操作数,操作数经隐式求值栈流动(LOAD_FAST 压栈、BINARY_OP 弹二压一,co_stacksize 记录栈深)。kvlang 每条指令显式命名 读操作数([i,-1],[i,-2]…)与写操作数([i,1]…),更接近 LLVM IR / Lua 寄存器 / 三地址码 。j 轴 = 三地址指令的操作数平面,是"无隐藏栈、每个操作数都是可寻址 KV 路径"的直接后果,也是扩展 rwir 能有任意 nr/nw 的前提。承重,不可去。
基底是可观测、可崩溃恢复的 KV 树。 CPython 的 PC 是内存整数偏移、co_code 是不透明字节、frame 是堆上 C 结构——进程死即全失。kvlang 的 PC 是 KV 路径字符串 ,指令是可 kvspace tree/get 的 KV 条目,帧是 KV 子树——外部化、可观测、可重启续跑。逻辑骨架相同,基底完全不同,这正是 kvlang 的核心命题。
layout 不是 codegen。 CPython compile() 产出不透明字节数组(codegen)。kvlang 把同一份三地址 IR 布局 成 KV 坐标,不序列化成字节,IR 本身即运行时表示(呼应 kvlang/layout 定位澄清:它是语法检查器和布局工具,不是编译器 #202 :layout 只检查+布局,不是编译器)。
优化/编译在扩展,核心刻意不优化字节码。 CPython 有窥孔优化 + 3.11 起的自适应特化解释器,优化的是字节码本身。kvlang 核心保持朴素解释,把融合/codegen 推给扩展编译器(算子融合,op-gpu)。所以"看着像字节码"的核心是故意不优化 的。
四、由收敛引出的可借鉴点(本 issue 要讨论的方向)
既然骨架已和成熟字节码收敛,成熟设计里有一处直接可借:
局部变量寻址:CPython 的整数槽 vs kvlang 的 3 跳 Ptr 链。
讨论点(供定案,本 issue 不改码):
是否把 name→整数 slot 下标 在 layout 期固化,让运行时局部寻址从 3 跳降到 1 跳(对齐 CPython LOAD_FAST/JVM iload/Lua 寄存器),消掉命名 Ptr 中间跳。与 j 轴的负/正坐标如何统一编号。
三地址(j 轴显式操作数)vs 栈式的定案确认:kvlang 明确站三地址阵营(理由:每操作数须为可寻址 KV 路径),把这条写进设计文档,避免反复。
CPython frame 的紧凑布局是否对 2-runtime/PC 与帧寻址:调用深度导致 key 长度 O(D)、活跃 key 总量 O(D²) —— 评估 [ctlflow,irseq,params] 三维地址 #116 的 O(D²) 活跃键问题有借鉴(如把一帧的定长 slot 段合并寻址)。
复现
# CPython 侧
python3 -c "import dis; ..." # 见上,Python 3.12.7
# kvlang 侧
kvlang dump cmp_demo.kv # absval/sum_to(while)/fact(递归)/gcd,dump 见上
关联
#116 (三维定稿与摊平实测)、#194 (每操作一次 KV 往返的正交成本)、#202 (layout ≠ 编译器)、kvspace篇-03(布局格式与 3 跳 Ptr 链)。
观察
#116 把控制流摊平(if/while/for/break/continue → goto/br 到 int64 指令号,runtime 不再建 scope 帧、无
_then_/_else_/_while_目录)之后,kvlang 的运行时地址退回成三个数字轴:这个形态和经典字节码 VM(CPython/Lua/JVM)结构上非常接近。本 issue 用 CPython 3.12 的真实字节码逐条对比,厘清:哪些是已经收敛的经典模型、哪些是 kvlang 承重的本质差异、哪些可以反过来向成熟字节码借鉴。不改代码,先定认知与方向。
一、同一函数,两侧真实产物
源(两侧同构):
CPython 3.12
dis.dis(PC = co_code 里的整数字节偏移):kvlang
kvlang dump(PC = KV 路径,指令按[i,j]逐槽铺开):二、已经收敛的部分(和经典字节码一样)
/lib/<func>/[i,*],i=1..NPOP_JUMP_IF_FALSE/JUMP_BACKWARD到字节偏移goto/br到 int64 irseq(kvcpu.c:32jump_irseq)/lib/<func>+ 每帧一次 extindex(kvspace篇-03)co_varnames整数下标[d](keytree.c:53)用户的观察成立:摊平之后,kvlang 的地址骨架就是"经典字节码 + 帧栈"。
三、承重的本质差异(不是退化,是 kvlang 立身之本)
指令参数轴 j = 三地址,不是栈式。 CPython 是栈机:指令不显式寻址操作数,操作数经隐式求值栈流动(
LOAD_FAST压栈、BINARY_OP弹二压一,co_stacksize 记录栈深)。kvlang 每条指令显式命名读操作数([i,-1],[i,-2]…)与写操作数([i,1]…),更接近 LLVM IR / Lua 寄存器 / 三地址码。j 轴 = 三地址指令的操作数平面,是"无隐藏栈、每个操作数都是可寻址 KV 路径"的直接后果,也是扩展 rwir 能有任意 nr/nw 的前提。承重,不可去。基底是可观测、可崩溃恢复的 KV 树。 CPython 的 PC 是内存整数偏移、co_code 是不透明字节、frame 是堆上 C 结构——进程死即全失。kvlang 的 PC 是 KV 路径字符串,指令是可
kvspace tree/get的 KV 条目,帧是 KV 子树——外部化、可观测、可重启续跑。逻辑骨架相同,基底完全不同,这正是 kvlang 的核心命题。layout 不是 codegen。 CPython
compile()产出不透明字节数组(codegen)。kvlang 把同一份三地址 IR 布局成 KV 坐标,不序列化成字节,IR 本身即运行时表示(呼应 kvlang/layout 定位澄清:它是语法检查器和布局工具,不是编译器 #202:layout 只检查+布局,不是编译器)。优化/编译在扩展,核心刻意不优化字节码。 CPython 有窥孔优化 + 3.11 起的自适应特化解释器,优化的是字节码本身。kvlang 核心保持朴素解释,把融合/codegen 推给扩展编译器(算子融合,op-gpu)。所以"看着像字节码"的核心是故意不优化的。
四、由收敛引出的可借鉴点(本 issue 要讨论的方向)
既然骨架已和成熟字节码收敛,成熟设计里有一处直接可借:
局部变量寻址:CPython 的整数槽 vs kvlang 的 3 跳 Ptr 链。
LOAD_FAST i一步命中frame.f_localsplus[i],i 是编译期整数下标。resolve_read_path)。2-runtime/PC 与帧寻址:调用深度导致 key 长度 O(D)、活跃 key 总量 O(D²) —— 评估 [ctlflow,irseq,params] 三维地址 #116 的三条热路径分析里,这类 O(D) 字符串拼接 + KV 往返是主要单次开销;kvspace篇-03 自己也注了「可优化为 name→slotIdx 内存 map」。讨论点(供定案,本 issue 不改码):
LOAD_FAST/JVMiload/Lua 寄存器),消掉命名 Ptr 中间跳。与 j 轴的负/正坐标如何统一编号。复现
关联
#116(三维定稿与摊平实测)、#194(每操作一次 KV 往返的正交成本)、#202(layout ≠ 编译器)、kvspace篇-03(布局格式与 3 跳 Ptr 链)。