feat(tpc): includes 只认项目配置;ASan 改为显式链接 clang64 运行库
includes 目录不再回退仓库硬编码 TransPyCRoot/includes:Phase2Translator 新增 PROJECT_INCLUDE_DIRS(INCLUDE_DIRS 置空以兼容旧引用),Config.py 从 project.vpj 的 includes 同步;HandlesImports 的模块搜索、TypeAnnotationResolver._builtins_include_dirs 一律只读项目目录。 ASan:mingw64 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,新增 _FindAsanRuntimeDir 定位 clang64 运行库并按 -l: 显式链接(asan_dynamic-x86_64.dll.a + runtime_thunk),定位失败即报错退出。HandlesExprFormat:i32 不再写 'l' 长度修饰符(LP64 下 printf 会按 64 位读实参)。
This commit is contained in:
1 parent
ab3873b038
commit
cc2f5d34eb
122 files changed
+89
-31
No files matched your search
+12
-10
@@ -147,16 +147,16 @@ def main() -> None:
|
||||
parser.add_argument('--run', action='store_true', help='编译成功后立即执行生成的可执行文件')
|
||||
parser.add_argument('--rebuild-includes', action='store_true', help='删除 includes.binary 预编译缓存并重新编译所有 includes')
|
||||
parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)')
|
||||
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩并链接 -fsanitize=address(需 clang64 环境)')
|
||||
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
|
||||
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
|
||||
|
||||
args: argparse.Namespace = parser.parse_args()
|
||||
|
||||
# ASan 模式(--asan):直接经 args 传给 Phase2Translator(compile_ll_to_obj
|
||||
# 先 opt -passes=asan 插桩,链接追加 -fsanitize=address)。不再用 TPC_ASAN
|
||||
# 环境变量透传——环境变量在 worker 子进程/外部调用链下不可靠。
|
||||
# 先 opt -passes=asan 插桩,链接显式 -l: clang64 的 libclang_rt.asan_dynamic*)。
|
||||
# 不再用 TPC_ASAN 环境变量透传——环境变量在 worker 子进程/外部调用链下不可靠。
|
||||
if args.asan:
|
||||
_vlog().info('ASan 模式已启用(opt -passes=asan + -fsanitize=address)', 'asan')
|
||||
_vlog().info('ASan 模式已启用(opt -passes=asan + clang64 libclang_rt.asan_dynamic*)', 'asan')
|
||||
|
||||
project_file: str | None = args.project
|
||||
if not project_file:
|
||||
@@ -184,6 +184,13 @@ def main() -> None:
|
||||
cc_flags: list[str] = resolved.get('compiler', {}).get('flags', ['-filetype=obj'])
|
||||
slice_level: int = resolved.get('options', {}).get('slice_level', 3)
|
||||
project_include_dirs: list[str] = resolved.get('includes', [])
|
||||
# includes 只认项目配置:同步到 Phase2Translator.PROJECT_INCLUDE_DIRS,
|
||||
# 供 builtins.vp 扫描等静态路径在 phase1/2 全程使用(禁止仓库硬编码回退)
|
||||
try:
|
||||
from lib.Projectrans.Phase2Translator import Phase2Translator
|
||||
Phase2Translator.PROJECT_INCLUDE_DIRS = [os.path.abspath(d) for d in project_include_dirs if os.path.isdir(os.path.join(project_dir, d)) or os.path.isdir(d)]
|
||||
except Exception:
|
||||
pass
|
||||
target_triple: str | None = resolved.get('target', {}).get('triple')
|
||||
target_datalayout: str | None = resolved.get('target', {}).get('datalayout')
|
||||
|
||||
@@ -250,11 +257,6 @@ def main() -> None:
|
||||
_vlog().error("阶段一需要指定源目录,不支持文件列表")
|
||||
sys.exit(1)
|
||||
all_include_dirs: list[str] = list(project_include_dirs) if project_include_dirs else []
|
||||
for d in Phase2Translator.INCLUDE_DIRS:
|
||||
if os.path.isdir(d):
|
||||
d = os.path.abspath(d)
|
||||
if d not in [os.path.abspath(x) for x in all_include_dirs]:
|
||||
all_include_dirs.append(d)
|
||||
gen: Phase1Generator = Phase1Generator(src, temp_dir, include_dirs=all_include_dirs, target_triple=target_triple, target_datalayout=target_datalayout)
|
||||
gen.run()
|
||||
save_sha1_map(temp_dir, gen.sha1_map)
|
||||
@@ -286,7 +288,7 @@ def main() -> None:
|
||||
force_recompile_includes: bool = bool(resolved.get('options', {}).get('force_recompile_includes', False))
|
||||
if args.rebuild_includes:
|
||||
force_recompile_includes = True
|
||||
all_inc_dirs: list[str] = list(project_include_dirs) + list(Phase2Translator.INCLUDE_DIRS)
|
||||
all_inc_dirs: list[str] = list(project_include_dirs)
|
||||
seen_dirs: set[str] = set()
|
||||
for inc_dir in all_inc_dirs:
|
||||
abs_inc: str = os.path.abspath(inc_dir)
|
||||
|
||||
@@ -37,6 +37,40 @@ def _CachedIsFile(path: str) -> bool:
|
||||
return r
|
||||
|
||||
|
||||
def _FindAsanRuntimeDir() -> str | None:
|
||||
"""定位 clang64 的 ASan 运行库目录(按 LLVM 大版本取最新的那个)
|
||||
|
||||
mingw64 的 clang 只为 x86_64-w64-windows-gnu 目标提供 builtins / fuzzer /
|
||||
profile / orc 运行库,**没有 ASan runtime**(<resource-dir>/lib/
|
||||
x86_64-w64-windows-gnu/ 是空目录),所以不能靠 `-fsanitize=address` 让驱动
|
||||
自动去找 —— 那只会得到
|
||||
"could not open .../libclang_rt.asan_dynamic.dll.a: No such file or directory"。
|
||||
clang64(MSVC ABI) 那边才有 libclang_rt.asan_dynamic-x86_64.dll.a 与
|
||||
libclang_rt.asan_dynamic_runtime_thunk-x86_64.a,只能借用这两份。
|
||||
|
||||
Returns:
|
||||
形如 D:\\msys64\\clang64\\lib\\clang\\22\\lib\\windows 的目录;找不到返回 None
|
||||
"""
|
||||
root = r'D:\msys64\clang64\lib\clang'
|
||||
if not os.path.isdir(root):
|
||||
return None
|
||||
found: list[str] = []
|
||||
for name in os.listdir(root):
|
||||
cand = os.path.join(root, name, 'lib', 'windows')
|
||||
if os.path.isfile(os.path.join(cand, 'libclang_rt.asan_dynamic-x86_64.dll.a')):
|
||||
found.append(cand)
|
||||
if not found:
|
||||
return None
|
||||
|
||||
def _Ver(path: str) -> int:
|
||||
# 目录名是 LLVM 大版本号(如 "22")——按数值比,否则 "9" 会排在 "22" 之后
|
||||
v = path.split(os.sep)[-3]
|
||||
return int(v) if v.isdigit() else 0
|
||||
|
||||
found.sort(key=_Ver)
|
||||
return found[-1]
|
||||
|
||||
|
||||
def _PickWorkerCount(n_files: int, per_worker_mb: int = 350) -> int:
|
||||
"""按可用物理内存动态限制并行 worker 数,避免多进程编译内存峰值 OOM。
|
||||
|
||||
@@ -565,9 +599,10 @@ class Phase2Translator:
|
||||
|
||||
INCLUDE_LIB_EXTENSIONS: tuple[str, ...] = ('.dll', '.ll', '.so', '.o', '.a', '.lib')
|
||||
INCLUDE_SRC_EXTENSIONS: tuple[str, ...] = ('.c', '.cpp', '.cc', '.cxx', '.m', '.mm')
|
||||
INCLUDE_DIRS: list[str] = [
|
||||
os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'includes'),
|
||||
]
|
||||
# includes 只认项目配置(project.vpj includes),禁止仓库硬编码回退。
|
||||
# INCLUDE_DIRS 保留为空以兼容旧引用;实际目录由 __init__ 写入 PROJECT_INCLUDE_DIRS。
|
||||
INCLUDE_DIRS: list[str] = []
|
||||
PROJECT_INCLUDE_DIRS: list[str] = []
|
||||
# include 源文件 sha1/存在性缓存(_build_current_ModuleSha1Map):
|
||||
# 编译期间 include .vp/.py 源文件不变,避免每文件重复 isfile + open +
|
||||
# compute_sha1(PROF 显示 9306 isfile + 4653 open + TextIOWrapper.read
|
||||
@@ -595,13 +630,16 @@ class Phase2Translator:
|
||||
# ASan 模式:由 Config 的 --asan 参数直传(不再用 TPC_ASAN 环境变量,
|
||||
# 环境变量在 worker 子进程/外部调用链下不可靠)。
|
||||
# 检测 stdlib 堆/栈/全局越界与垃圾指针(MemBuddy pool 内部由 canary 覆盖)。
|
||||
# 注意:mingw64 的 clang 不提供 ASan runtime(libclang_rt.asan 缺失),
|
||||
# 必须使用 clang64 环境(自带 asan_dynamic runtime),PATH 前置 clang64/bin。
|
||||
# 注意:mingw64 的 clang 不提供 x86_64-w64-windows-gnu 目标的 ASan runtime,
|
||||
# 只能借用 clang64(MSVC ABI) 的那两份:PATH 前置 clang64/bin(运行期 DLL),
|
||||
# 链接时显式 -l: 那两份 .a/.dll.a(见 _FindAsanRuntimeDir 的说明)。
|
||||
self.asan_mode = asan_mode
|
||||
self.asan_rt_dir: str | None = None
|
||||
if self.asan_mode:
|
||||
_clang64_bin = r'D:\msys64\clang64\bin'
|
||||
if os.path.isdir(_clang64_bin):
|
||||
os.environ['PATH'] = _clang64_bin + os.pathsep + os.environ.get('PATH', '')
|
||||
self.asan_rt_dir = _FindAsanRuntimeDir()
|
||||
self.slice_level = 3
|
||||
self.sha1_map: dict[str, str] = {}
|
||||
self.sig_files: dict[str, str] = {}
|
||||
@@ -652,7 +690,7 @@ class Phase2Translator:
|
||||
self._shared_cross_module_vtable: set[str] = set()
|
||||
self._shared_class_methods: dict[str, list[str]] = {}
|
||||
self._shared_cross_module_novtable: set[str] = set()
|
||||
default_dirs = [d for d in self.INCLUDE_DIRS if os.path.isdir(d)]
|
||||
default_dirs: list[str] = []
|
||||
if include_dirs:
|
||||
seen = set()
|
||||
self.include_dirs = []
|
||||
@@ -663,6 +701,8 @@ class Phase2Translator:
|
||||
seen.add(d)
|
||||
else:
|
||||
self.include_dirs = default_dirs
|
||||
# 严格:只认项目 includes;供 builtins.vp 扫描等静态路径使用
|
||||
Phase2Translator.PROJECT_INCLUDE_DIRS = list(self.include_dirs)
|
||||
self._LoadSha1Map()
|
||||
|
||||
def _LoadSha1Map(self) -> None:
|
||||
@@ -1486,6 +1526,10 @@ class Phase2Translator:
|
||||
trans.translator._global_function_default_args = self.function_default_args
|
||||
trans.translator._global_function_param_names = self.function_param_names
|
||||
trans.translator._temp_dir = self.temp_dir
|
||||
# includes 只认 project.vpj 配置的 include 目录(不再回退仓库 TransPyCRoot/includes):
|
||||
# 目录重组/缺失时避免声明解析从仓库或 sig 回退路径产生错误时序(memhub 未注册→i32)
|
||||
_proj_inc = [os.path.abspath(d) for d in self.include_dirs if os.path.isdir(d)]
|
||||
trans.translator.LibraryPaths = _proj_inc if _proj_inc else ['.']
|
||||
trans.translator._shared_doc_meta = self._shared_doc_meta
|
||||
# 传递共享 vtable 状态
|
||||
trans.translator._shared_class_vtable = self._shared_class_vtable
|
||||
@@ -3655,9 +3699,23 @@ SECTIONS {
|
||||
# 其他所有标志(包括 -Wl, 选项)放在目标文件之前
|
||||
non_lib_flags.append(flag)
|
||||
i += 1
|
||||
# ASan 模式:链接 libasan(配合各 .ll 的 opt -passes=asan 插桩)
|
||||
# ASan 模式:链接 ASan 运行库(配合各 .ll 的 opt -passes=asan 插桩)
|
||||
# 不能传 -fsanitize=address:那会让 clang++ 驱动去找
|
||||
# <resource-dir>/lib/x86_64-w64-windows-gnu/libclang_rt.asan_dynamic.dll.a,
|
||||
# 而该目录是空的(上游 LLVM 不为这个 triple 构建 ASan runtime),
|
||||
# 直接报 "could not open ... No such file or directory"。
|
||||
# 改为显式链接 clang64 的那两份(与 TPV / README §8.1 同口径)。
|
||||
# 它们是库,必须排在目标文件之后,所以追加到 lib_flags 而不是 non_lib_flags。
|
||||
if self.asan_mode:
|
||||
non_lib_flags.append('-fsanitize=address')
|
||||
if self.asan_rt_dir is None:
|
||||
_vlog().error(
|
||||
"ASan 模式找不到 clang64 的 ASan 运行库目录 "
|
||||
r"(D:\msys64\clang64\lib\clang\<ver>\lib\windows)\n"
|
||||
" 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装。")
|
||||
sys.exit(1)
|
||||
lib_flags.append('-L' + self.asan_rt_dir)
|
||||
lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a')
|
||||
lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a')
|
||||
cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags
|
||||
_vlog().info(f" 执行: {' '.join(cmd)}")
|
||||
result = subprocess.run(
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -175,10 +175,10 @@ class ExprFormatHandle(BaseHandle):
|
||||
|
||||
length_mod: str = ''
|
||||
if isinstance(val.type, ir.IntType):
|
||||
# 32 位**不写**长度修饰符(N14):与 TPV 侧 ExprGenFstr 保持同步。
|
||||
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
|
||||
if val.type.width == 64:
|
||||
length_mod = 'll'
|
||||
elif val.type.width == 32:
|
||||
length_mod = 'l'
|
||||
elif val.type.width == 16:
|
||||
length_mod = 'h'
|
||||
elif val.type.width == 8:
|
||||
|
||||
@@ -581,8 +581,10 @@ class ImportHandle(BaseHandle):
|
||||
HandlesFile = os.path.abspath(inspect.getfile(self.__class__))
|
||||
TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile))))
|
||||
|
||||
FullModulePath = None
|
||||
SearchDirs = [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths)
|
||||
FullModulePath = None
|
||||
# includes 只认项目(project.vpj includes → LibraryPaths)目录,禁止回退仓库硬编码
|
||||
# TransPyCRoot/includes:仓库副本缺失时回退会造成声明解析时序错乱(memhub 未注册→i32)。
|
||||
SearchDirs = list(self.Trans.LibraryPaths)
|
||||
|
||||
for LibPath in SearchDirs:
|
||||
SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath
|
||||
@@ -613,7 +615,7 @@ class ImportHandle(BaseHandle):
|
||||
if not FullModulePath:
|
||||
ModulePathPyBase = module_name.replace('.', os.sep)
|
||||
PackageInitPathPyBase = module_name.replace('.', os.sep) + os.sep + '__init__'
|
||||
SearchDirsForPy = [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths)
|
||||
SearchDirsForPy = list(self.Trans.LibraryPaths)
|
||||
for LibPath in SearchDirsForPy:
|
||||
SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath
|
||||
# 优先尝试 .vp,回退 .py
|
||||
@@ -649,7 +651,7 @@ class ImportHandle(BaseHandle):
|
||||
SearchFromDirs = [CurrentFileDir, os.getcwd()]
|
||||
else:
|
||||
SearchFromDirs = [os.getcwd()]
|
||||
SearchDirsForSubmodule = SearchFromDirs + [os.path.join(TransPyCRoot, 'includes'), os.path.join(TransPyCRoot, 'lib', 'includes')] + list(self.Trans.LibraryPaths)
|
||||
SearchDirsForSubmodule = SearchFromDirs + list(self.Trans.LibraryPaths)
|
||||
for SearchPath in SearchDirsForSubmodule:
|
||||
if not os.path.isdir(SearchPath):
|
||||
continue
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -105,18 +105,14 @@ class TypeAnnotationResolver:
|
||||
|
||||
@staticmethod
|
||||
def _builtins_include_dirs() -> list[str]:
|
||||
"""includes 目录列表(builtins.vp 定位用)"""
|
||||
"""includes 目录列表(builtins.vp 定位用)——只认项目配置 includes,
|
||||
禁止仓库硬编码回退(重组/缺失时会产生错误类型解析,如 uint32→struct 退化)"""
|
||||
dirs: list[str] = []
|
||||
try:
|
||||
from lib.Projectrans.Phase2Translator import Phase2Translator
|
||||
dirs = list(Phase2Translator.INCLUDE_DIRS)
|
||||
dirs = list(Phase2Translator.PROJECT_INCLUDE_DIRS)
|
||||
except Exception:
|
||||
pass
|
||||
derived: str = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
||||
'includes')
|
||||
if derived not in dirs:
|
||||
dirs.append(derived)
|
||||
return dirs
|
||||
|
||||
@staticmethod
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user