feat(vm): 提升器改用"执行流栈快照(DFS)"渲染, 消除跨跳转失步

做了什么:
- _lift_func 重写为两遍:
  第一遍: 沿执行流 DFS 传播"求值栈快照"(每条 pc 记录进入时的栈)
  第二遍: 按地址序渲染, 块首用快照还原栈
- 新增 _next_pc(): 用规范操作数长度计算后继(含 CREATE_CLOSURE 动态长度)

为什么:
- 原来按"地址序 + 块首清空栈"提升, 表达式跨分支传递时会整体丢失
- 栈机必须在"执行流"上跟踪栈, 而不是地址序

怎么验证:
- XTEA 轮仍可读: r2=(r2+2654435769); r?+=(((r6<<4)^(r6>>>5))+r6)^(r2+r7[((r2>>>11)&3)])
- 闭包图正确: // closure func_9109(p0->r3,p1->r4) caps=[r7<-r8] target=@9109
- 已知残留: 汇合点(多前驱)取首路径栈, 仍会产出少量 r<?>  (下一步: phi 合并/对齐运行时轨迹)
This commit is contained in:
yml2213
2026-10-11 22:17:55 +08:00
parent eed7004754
commit 942421d811
2 changed files with 3204 additions and 22 deletions
File diff suppressed because it is too large Load Diff
+55 -22
View File
@@ -87,39 +87,72 @@ class Lifter:
return self.funcs[target]["name"]
def _lift_func(self, entry: int, name: str):
all_ins = disassemble_cfg(self.z, entry)
insts = {i["pc"]: i for i in all_ins if i["pc"] >= entry}
# 只在"跳转目标/入口/洞"处打标签, 避免每行一个标签
insts = {i["pc"]: i for i in disassemble_cfg(self.z, entry) if i["pc"] >= entry}
# ---------- 第一遍: 沿执行流传播"求值栈快照"(DFS, 每 pc 记录首次到达时的栈) ----------
state: Dict[int, List[Expr]] = {entry: []}
seen: Dict[int, bool] = {}
work = [entry]
while work:
pc = work.pop()
if pc in seen:
continue
st = list(state.get(pc, []))
while pc in insts and pc not in seen:
seen[pc] = True
state.setdefault(pc, list(st)) # 每条 pc 都记录"进入时的栈"
ins = insts[pc]
self._emit(ins["op"], ins, pc, st, []) # 只改栈, 丢弃语句
op = ins["op"]
ops = ins["operands"] or []
nxt = self._next_pc(ins)
if op == OP_JUMP:
succ = [ops[0]]
elif op == OP_JUMP_IF_TRUE:
succ = [ops[0], nxt]
elif op in (19, 49): # RET_TRUE / THROW
succ = []
else:
succ = [nxt]
for t in succ:
if t in insts and t not in state:
state[t] = list(st)
if t in insts and t not in seen:
work.append(t)
if succ != [nxt]:
break
pc = nxt
# ---------- 第二遍: 按地址序渲染 (块首用快照还原栈) ----------
targets = {entry}
for i in all_ins:
for o in i.get("operands") or []:
for i in insts.values():
for o in (i.get("operands") or []):
if isinstance(o, int) and o in insts:
targets.add(o)
# 只保留可达块; 逐块输出
self.out.append("")
self.out.append(f"function {name}() {{")
stack: List[Expr] = []
pending: List[str] = []
last_pc = None
def flush():
for s in pending:
self.out.append(" " + s)
pending.clear()
pcs = sorted(insts)
for pc in pcs:
for pc in sorted(insts):
ins = insts[pc]
op = ins["op"]
if op is None:
if ins["op"] is None:
continue
st = list(state.get(pc, []))
if pc in targets:
flush()
self.out.append(f" @{pc:05d}:") # 目标标签
self._emit(op, ins, pc, stack, pending)
flush()
for x in pending:
self.out.append(" " + x)
pending.clear()
self.out.append(f" @{pc:05d}:")
self._emit(ins["op"], ins, pc, st, pending)
for x in pending:
self.out.append(" " + x)
self.out.append("}")
def _next_pc(self, ins) -> int:
from tdc_disasm import ARITY, DYNAMIC_ARITY
op = ins["op"]; ops = ins["operands"] or []
if op == OP_CREATE_CLOSURE:
capn = self.z[ins["pc"] + 2]; parn = self.z[ins["pc"] + 3]
return ins["pc"] + 1 + 3 + 2 * capn + parn
return ins["pc"] + 1 + ARITY.get(op, 0)
def _fallthrough(self, ins) -> int:
op = ins["op"]
if op == OP_JUMP: