Files
ViperC/App/lib/Stub/StubMerger/StubMergerTopo.vp
T
2026-08-27 09:48:20 +08:00

187 lines
8.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import w32.win32file as win32file
import w32.win32base as win32base
import w32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# ============================================================
# StubMergerTopo - StubMerger 拆分(阶段 E):StubMergerTopo
#
# 直接移动模式:函数实现迁至此模块,原 StubMerger.vp 仅保留
# 常量/全局变量;调用方经本模块前缀访问。全局变量跨模块经
# StubMerger._xxx 访问(main.vp 先例)。
# ============================================================
# ============================================================
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
#
# 背景:跨模块 vtable 生成要求被依赖模块先完整翻译(如 astaux.vp 继承
# base.vp 的 AST,vtable 槽位需 AST.accept 的 TypeStr 已注册到全局表)。
# Phase B+ 原先按 store 扫描顺序编译,依赖顺序不保证 → fail-fast 暴露。
#
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中)。
# indegree[m] = m 在可达集合内的直接依赖数;
# 入度为 0(不依赖任何可达模块)者先输出,输出后对依赖它的模块
# 减入度,入度归 0 者入队。结果即"被依赖者先编译"。
# reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
#
# Args:
# store_count / store_sha1_arr / store_mod_arr: store 元数据
# reachable_set / reachable_count: 可达集合(_BuildReachableSha1Set 输出)
# sorted_idx: 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
#
# Returns:
# 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
# ============================================================
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
store_mod_arr: bytes, reachable_set: bytes,
reachable_count: int,
sorted_idx: t.CSizeT | t.CPtr) -> int:
"""对可达 includes 逆拓扑排序(被依赖者先),store 索引写入 sorted_idx"""
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None:
return 0
indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
if indegree is None or queue is None or rel_idx is None:
if indegree is not None:
stdlib.free(indegree)
if queue is not None:
stdlib.free(queue)
if rel_idx is not None:
stdlib.free(rel_idx)
return 0
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
zi: int = 0
for zi in range(store_count + 1):
indegree[zi] = 0
queue[zi] = 0
rel_idx[zi] = 0
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
rel_count: int = 0
i_t: int
if reachable_set is None or reachable_count <= 0:
for i_t in range(store_count):
rel_idx[rel_count] = i_t
rel_count += 1
else:
for i_t in range(store_count):
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + t.CSizeT(i_t) * 17, reachable_set, reachable_count) != 0:
rel_idx[rel_count] = i_t
rel_count += 1
# 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行)
fb_rel: str = VLogger.fmt_buf()
if fb_rel is not None:
viperlib.snprintf(fb_rel, 1024, "诊断 topo rel_count=%d", rel_count)
VLogger.debug(fb_rel, "StubMergerTopo")
# 计算入度:m 在可达集合内的直接依赖数
i_r: int
for i_r in range(rel_count):
si: int = rel_idx[i_r]
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si) * 17)
if deps_si is None:
continue
dl_si: t.CSizeT = string.strlen(deps_si)
dpos: t.CSizeT = 0
while dpos < dl_si:
dstart: t.CSizeT = dpos
while dpos < dl_si and deps_si[dpos] != ' ':
dpos += 1
dlen: t.CSizeT = dpos - dstart
if dpos < dl_si:
dpos += 1
if dlen == 0:
continue
dname_buf: bytes = stdlib.malloc(dlen + 1)
if dname_buf is None:
continue
string.strncpy(dname_buf, deps_si + dstart, dlen)
dname_buf[dlen] = '\0'
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
stdlib.free(dname_buf)
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
# 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug
indg: int = indegree[si]
indg = indg + 1
indegree[si] = indg
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
q_tail: int = 0
for i_r in range(rel_count):
si2: int = rel_idx[i_r]
if indegree[si2] == 0:
queue[q_tail] = si2
q_tail += 1
out_count: int = 0
# Kahn 主循环必须用 while(不能用 for range):循环体内会动态入队
# (queue[q_tail]=si3; q_tail+=1),for range 上界只求值一次,
# 动态入队的模块永远不会被处理且兜底(indegree>0)也捞不回 → 拓扑漏项
q_head: int = 0
while q_head < q_tail:
m: int = queue[q_head]
sorted_idx[out_count] = m
out_count += 1
# 对依赖 m 的可达模块减入度
mod_m: str = store_mod_arr + t.CSizeT(m) * 64
mod_m_len: t.CSizeT = string.strlen(mod_m)
for i_r in range(rel_count):
si3: int = rel_idx[i_r]
if indegree[si3] == 0:
continue
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + t.CSizeT(si3) * 17)
if deps_si3 is None:
continue
dl_si3: t.CSizeT = string.strlen(deps_si3)
dpos3: t.CSizeT = 0
found_dep: int = 0
while dpos3 < dl_si3 and found_dep == 0:
dstart3: t.CSizeT = dpos3
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
dpos3 += 1
dlen3: t.CSizeT = dpos3 - dstart3
if dpos3 < dl_si3:
dpos3 += 1
if dlen3 == 0:
continue
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0:
found_dep = 1
if found_dep == 1:
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
indg3: int = indegree[si3]
indg3 = indg3 - 1
indegree[si3] = indg3
if indegree[si3] == 0:
queue[q_tail] = si3
q_tail += 1
q_head += 1
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
for i_r in range(rel_count):
si4: int = rel_idx[i_r]
if indegree[si4] > 0:
sorted_idx[out_count] = si4
out_count += 1
stdlib.free(indegree)
stdlib.free(queue)
stdlib.free(rel_idx)
return out_count