Compare commits
300
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8461a3da47 | ||
|
|
08fe5a7b6b | ||
|
|
37c15c9aa0 | ||
|
|
850f61dc22 | ||
|
|
82a735c367 | ||
|
|
42b094a575 | ||
|
|
3242560d02 | ||
|
|
6e17716a69 | ||
|
|
eff88dd591 | ||
|
|
bf0bbcc89b | ||
|
|
d46fdf152a | ||
|
|
3e1e1fc1b3 | ||
|
|
15952d3852 | ||
|
|
201c881b43 | ||
|
|
19feb40e8c | ||
|
|
2f4031b3d6 | ||
|
|
d8648176e9 | ||
|
|
e796314a24 | ||
|
|
7e9ed22dff | ||
|
|
c57c931099 | ||
|
|
832b0a5706 | ||
|
|
6196bdfe56 | ||
|
|
d06e665ebd | ||
|
|
5460d78ff1 | ||
|
|
9d0a43d5e6 | ||
|
|
15a210bede | ||
|
|
59c032f135 | ||
|
|
23236c88ea | ||
|
|
d1dd6de070 | ||
|
|
cbadc356f9 | ||
|
|
5dc483e256 | ||
|
|
6b86f846ca | ||
|
|
217f5f021d | ||
|
|
5ea0ce094e | ||
|
|
3e52baa209 | ||
|
|
508428bbc9 | ||
|
|
3a687db0bb | ||
|
|
38a277a914 | ||
|
|
dc4468593c | ||
|
|
a143c2de0c | ||
|
|
7da9d7cb0d | ||
|
|
d09b79995d | ||
|
|
61f23d99a1 | ||
|
|
813b59e25f | ||
|
|
ee3337f3c4 | ||
|
|
a060e1d76e | ||
|
|
3c6041c97c | ||
|
|
ca81c1185c | ||
|
|
40ea5d6513 | ||
|
|
4f45b391ea | ||
|
|
d6b87da063 | ||
|
|
0f2675e794 | ||
|
|
0678cc8c48 | ||
|
|
70c271d2a4 | ||
|
|
9d02a3ac0b | ||
|
|
6b180c8f59 | ||
|
|
a23c1bda3e | ||
|
|
dc24970af2 | ||
|
|
ea54d5685d | ||
|
|
99a021ad67 | ||
|
|
12da1cbfc1 | ||
|
|
bc1eeb6e38 | ||
|
|
c1e5a11962 | ||
|
|
d2138678ef | ||
|
|
1c3cdf3493 | ||
|
|
377c0db5d3 | ||
|
|
6cb1f525d9 | ||
|
|
a01e9280cc | ||
|
|
14d8c05a2a | ||
|
|
97c0a1c6fd | ||
|
|
d7c5af9b80 | ||
|
|
cb2dc4cb3a | ||
|
|
444e87dd7d | ||
|
|
d3997e0746 | ||
|
|
32599a08c1 | ||
|
|
37c4b0da3b | ||
|
|
5d15d01e21 | ||
|
|
3ca2165fd9 | ||
|
|
b859f8a136 | ||
|
|
22475e0fda | ||
|
|
c0ff5c9d96 | ||
|
|
c9562a6e04 | ||
|
|
d206abf3e1 | ||
|
|
ea2af739dd | ||
|
|
00bf7ebf38 | ||
|
|
e3743b185f | ||
|
|
54b239747e | ||
|
|
ae12ae8581 | ||
|
|
001f8afc37 | ||
|
|
e7b05c6a3d | ||
|
|
3d638927d5 | ||
|
|
4a66aa7fd2 | ||
|
|
9d02cc4684 | ||
|
|
65c72fd55c | ||
|
|
8453698f66 | ||
|
|
74fafb3b56 | ||
|
|
f044d94e90 | ||
|
|
87914b0a51 | ||
|
|
d7a5f7e68b | ||
|
|
7c11c589f7 | ||
|
|
3384941cd1 | ||
|
|
d0c7b9c77c | ||
|
|
9b93586eec | ||
|
|
023a22af08 | ||
|
|
4e46a25ab1 | ||
|
|
be4db7b5d1 | ||
|
|
985983391b | ||
|
|
3b9deb08d8 | ||
|
|
7468b007f7 | ||
|
|
3c0d3d13b5 | ||
|
|
966fc3b8f5 | ||
|
|
77f4a25055 | ||
|
|
82ad5da7c7 | ||
|
|
d023e9932d | ||
|
|
41772b4a0c | ||
|
|
58951a29a8 | ||
|
|
5bb6538703 | ||
|
|
9db3748f68 | ||
|
|
87e0bce5ee | ||
|
|
034293df59 | ||
|
|
eea8111e61 | ||
|
|
f2cbb658e1 | ||
|
|
c1d3f62bf0 | ||
|
|
5ec75b2d38 | ||
|
|
1784eaf906 | ||
|
|
948e1855a6 | ||
|
|
a3a627f513 | ||
|
|
ea57986312 | ||
|
|
5d1e604aac | ||
|
|
41674d125c | ||
|
|
79d1c86982 | ||
|
|
6da8e2e095 | ||
|
|
4907e64714 | ||
|
|
953c829e9f | ||
|
|
357a8bba05 | ||
|
|
bf5ecd7668 | ||
|
|
6000734704 | ||
|
|
71730d920c | ||
|
|
0f504a9010 | ||
|
|
960ecdfd74 | ||
|
|
5a10a2ea10 | ||
|
|
e82e2ac768 | ||
|
|
337be883d3 | ||
|
|
d0ffb094c2 | ||
|
|
79299ec47d | ||
|
|
2bd37e80e3 | ||
|
|
fc7c5d5560 | ||
|
|
8881dea2e2 | ||
|
|
2353dca8ec | ||
|
|
c98730f717 | ||
|
|
007943142a | ||
|
|
6769cf0335 | ||
|
|
c791bd5e46 | ||
|
|
fc797da542 | ||
|
|
14a8dcb98b | ||
|
|
ab30b264fc | ||
|
|
d926987d44 | ||
|
|
eb0c3dfd31 | ||
|
|
1a3ef521b3 | ||
|
|
aeb396831a | ||
|
|
6758ecbf69 | ||
|
|
5f92abb7bc | ||
|
|
dd24dc670f | ||
|
|
95b076581a | ||
|
|
054dcf0a0b | ||
|
|
07ff906338 | ||
|
|
a2e1c651fc | ||
|
|
9b97470c09 | ||
|
|
20e3f3694f | ||
|
|
400cc6e17b | ||
|
|
be2521fe46 | ||
|
|
8b63ae4ca2 | ||
|
|
b71ed43f10 | ||
|
|
efb4a54428 | ||
|
|
3209a7a633 | ||
|
|
301a9d3328 | ||
|
|
2b5ff215c7 | ||
|
|
8b2008031d | ||
|
|
8fb0e38f4c | ||
|
|
c02aa3a2eb | ||
|
|
b60ce06335 | ||
|
|
d921dcd9e7 | ||
|
|
97313e9e55 | ||
|
|
9ff5a3a03e | ||
|
|
b6a9b50f93 | ||
|
|
70390858d8 | ||
|
|
2178970ff7 | ||
|
|
78b2052445 | ||
|
|
934708b206 | ||
|
|
1dd53ebff5 | ||
|
|
09b5bf5ac3 | ||
|
|
9e55e909a8 | ||
|
|
8a98051759 | ||
|
|
c77d38e923 | ||
|
|
14920c0e1e | ||
|
|
64502471e1 | ||
|
|
cd68bb62ac | ||
|
|
9f98e683a9 | ||
|
|
b03e55bb7e | ||
|
|
1fb0f4a4ea | ||
|
|
52fd89decf | ||
|
|
4ce577d8fb | ||
|
|
a6e1957768 | ||
|
|
ec47401151 | ||
|
|
7070bc1608 | ||
|
|
2ab6342d3c | ||
|
|
c20bd4734b | ||
|
|
5c1b882169 | ||
|
|
2fb648d68a | ||
|
|
ce50daa87a | ||
|
|
3b4bd1c6dc | ||
|
|
edca6c3760 | ||
|
|
f3628a797a | ||
|
|
12b15958c6 | ||
|
|
5993affd9c | ||
|
|
fb3d3787e6 | ||
|
|
36d23d2f42 | ||
|
|
848dc0ab38 | ||
|
|
7a9bc5ae75 | ||
|
|
0cf80632c1 | ||
|
|
374921623b | ||
|
|
df2de5ecfa | ||
|
|
cd55a7e214 | ||
|
|
aeb4e8e54c | ||
|
|
fb53f88893 | ||
|
|
a7b8118ce8 | ||
|
|
87b24c2445 | ||
|
|
0f4aeeceff | ||
|
|
a2874a14e6 | ||
|
|
e79ea010b0 | ||
|
|
f2ef1bb240 | ||
|
|
4a685d478f | ||
|
|
f923dc4d8d | ||
|
|
30a3da4cd4 | ||
|
|
185a2196ca | ||
|
|
9717ce0107 | ||
|
|
4236127956 | ||
|
|
68cd5f2f90 | ||
|
|
58cfe2346e | ||
|
|
6a6cae2a44 | ||
|
|
b7e4861e6c | ||
|
|
94b174c8a0 | ||
|
|
3426139d03 | ||
|
|
d0434bf114 | ||
|
|
cbf76cd468 | ||
|
|
7b3e814524 | ||
|
|
1dcd29db9c | ||
|
|
cda4cca805 | ||
|
|
2ac412c11a | ||
|
|
f9ba391236 | ||
|
|
21436b2690 | ||
|
|
ddb1e6cc2a | ||
|
|
3a4323e288 | ||
|
|
da18dd39a4 | ||
|
|
96dad309ac | ||
|
|
28b7437e21 | ||
|
|
9a80f1df6c | ||
|
|
e196d6a28a | ||
|
|
c035a82394 | ||
|
|
bf2f38acd2 | ||
|
|
09ea7283f9 | ||
|
|
8af967db7f | ||
|
|
98ee53340f | ||
|
|
feb29fa7a3 | ||
|
|
df1ffa4c2a | ||
|
|
bcbc7142be | ||
|
|
ab0ee723b3 | ||
|
|
2868798f58 | ||
|
|
63acb2bf4d | ||
|
|
f2b65f57ec | ||
|
|
3b465121cd | ||
|
|
692998820c | ||
|
|
1fbebb2a62 | ||
|
|
18f4c2326f | ||
|
|
105456e56e | ||
|
|
8da4550d79 | ||
|
|
19d7a90c28 | ||
|
|
67667b10db | ||
|
|
4c114d8875 | ||
|
|
53ba4e48bd | ||
|
|
9bd3ac3dfb | ||
|
|
df9a5a9beb | ||
|
|
cc8ea01476 | ||
|
|
ec9e950185 | ||
|
|
324a22b6d1 | ||
|
|
3b0ed04e70 | ||
|
|
611f49cfb6 | ||
|
|
35f2070d3b | ||
|
|
78d3941f7d | ||
|
|
94d2bf5047 | ||
|
|
0ebe2e0752 | ||
|
|
4eec3f0908 | ||
|
|
a3bb62f2c3 | ||
|
|
fabd92adc7 | ||
|
|
38860a6313 | ||
|
|
d633a5786f | ||
|
|
fb4175736f | ||
|
|
43d25e58b9 | ||
|
|
0b7f7a50c7 | ||
|
|
8d01c01afd |
No files matched your search
+1
-1
@@ -28,4 +28,4 @@ __pycache__/
|
|||||||
# 编译
|
# 编译
|
||||||
**/output/
|
**/output/
|
||||||
**/temp/
|
**/temp/
|
||||||
**/.tpv_build/
|
**/__vpcbuild__/
|
||||||
+2617
-2293
File diff suppressed because it is too large.
Load diff
+408
-551
File diff suppressed because it is too large.
Load diff
+169
-260
@@ -1,109 +1,63 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import memhub
|
import memhub
|
||||||
import viperlib
|
import win32.fileio as fileio
|
||||||
import sys
|
|
||||||
import w32.fileio as fileio
|
|
||||||
import w32.win32file
|
|
||||||
import w32.win32base
|
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.VLogger as VLogger
|
||||||
import lib.core.Gen.Translator as HandlesTranslator
|
|
||||||
import lib.core.IncludesScanner as IncludesScanner
|
import lib.core.IncludesScanner as IncludesScanner
|
||||||
import lib.Stub.StubMerger as StubMerger
|
|
||||||
import lib.core.Gen.Generator as GenGenerator
|
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
import lib.core.CompilerFactory as CompilerFactory
|
||||||
import lib.Phase.BuildPipeline as BuildPipeline
|
|
||||||
import lib.Projectrans.Config as Config
|
|
||||||
import lib.Stub.StubGen.Converter as StubConverter
|
|
||||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
|
||||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
|
||||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
|
||||||
|
|
||||||
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
|
||||||
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
|
||||||
import lib.Phase.Phase1 as Phase1
|
import lib.Phase.Phase1 as Phase1
|
||||||
|
|
||||||
import lib.core.Gen.GenApi as GenApi
|
import lib.core.Gen.GenApi as GenApi
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Phase1Scan - scan class inheritance, generic-class markers and container usage.
|
||||||
# Phase1Scan - 扫描类继承关系、泛型类标记与容器使用
|
# Functions live in this module; globals are accessed cross-module via Phase1._xxx.
|
||||||
#
|
|
||||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
|
# Topological sort stores class info/deps in flat byte arrays (names in
|
||||||
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
|
# class_names_buf/parent_names_buf, SHA1s in def_sha1s_buf) to avoid NULL str fields.
|
||||||
# 类名/父类名存储在 class_names_buf/parent_names_buf(每个 Phase1.CLASS_NAME_LEN 字节),
|
|
||||||
# 定义 SHA1 存储在 def_sha1s_buf(每个 Phase1.SHA1_LEN 字节)。
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
|
||||||
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
|
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
|
||||||
#
|
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: rawptr, generic_file_count_box: rawptr) -> int:
|
||||||
# 遍历所有可达 includes 文件解析 AST,收集 ClassDef 的类名与父类名存入扁平字节数组,返回收集到的类数量。
|
"""Pre-scan includes files and collect class inheritance info."""
|
||||||
#
|
if result is None: return -1
|
||||||
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否使用可达集合过滤;
|
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
|
||||||
# sha1_set SHA1 集合(use_reachable=0 时使用);set_count SHA1 集合数量;class_names_buf 类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节);
|
# strcpy write the zero page, so fail-fast abort instead of returning -1 silently.
|
||||||
# parent_names_buf 父类名扁平数组(每个 Phase1.CLASS_NAME_LEN 字节,无父类填 '\0');def_sha1s_buf 定义 SHA1 扁平数组(每个 Phase1.SHA1_LEN 字节)
|
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed (class_names_buf/parent_names_buf/def_sha1s_buf is NULL)", "Phase1")
|
||||||
# Returns: 收集到的类数量,-1 失败
|
|
||||||
# ============================================================
|
|
||||||
def _ScanClassInheritance(mb: memhub.MemBuddy*,
|
|
||||||
result: rawptr[IncludesScanner.ScanResult],
|
|
||||||
reachable_set: str, reachable_count: int,
|
|
||||||
use_reachable: int,
|
|
||||||
sha1_set: str, set_count: int,
|
|
||||||
class_names_buf: bytes,
|
|
||||||
parent_names_buf: bytes,
|
|
||||||
def_sha1s_buf: bytes,
|
|
||||||
generic_file_indices: t.CPtr,
|
|
||||||
generic_file_count_box: t.CPtr) -> int:
|
|
||||||
"""预扫描 includes 文件,收集类继承关系"""
|
|
||||||
if result is None:
|
|
||||||
return -1
|
|
||||||
# class_names_buf/parent_names_buf/def_sha1s_buf 由调用方 mb.alloc 分配(Phase1Main 预扫描前),本函数把其地址直接作为 strcpy 目的缓冲;
|
|
||||||
# 分配失败返回 NULL 时目的指针为近零地址 → strcpy 写零页(ASan access-violation),故按 fail-fast 显式报错终止(VLogger.error 内部 sys.exit(1)),不静默返回 -1。
|
|
||||||
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
|
|
||||||
VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
|
|
||||||
|
|
||||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||||
class_count: int = 0
|
class_count: int = 0
|
||||||
|
|
||||||
for i in range(result.Count):
|
for i in range(int(result.Count)):
|
||||||
# 文件级 mark:收集完类继承信息后立即回收本文件 AST(可达 includes 全量 parse 的 AST 若驻留到 mark1 会耗尽 1GB 主池 OOM);
|
# Per-file mark: reclaim this file's AST right after collecting inheritance
|
||||||
# class_names_buf/parent_names_buf/def_sha1s_buf 与 generic_file_indices 在调用前分配、位于本 mark 之前,reset 不影响。
|
# info (keeping all ASTs alive until mark1 would OOM the 1GB main pool).
|
||||||
scan_mark: sizet = mb.mark()
|
scan_mark: sizet = mb.mark()
|
||||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||||
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
|
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||||
if entry is None or entry.Sha1 is None:
|
if entry is None or entry.Sha1 is None: continue
|
||||||
continue
|
|
||||||
|
|
||||||
sha1_e: str = entry.Sha1
|
sha1_e: str = entry.Sha1
|
||||||
file_has_generic: int = 0
|
file_has_generic: int = 0
|
||||||
|
|
||||||
# 按需翻译过滤
|
# on-demand translation filter
|
||||||
in_set: int = 0
|
in_set: int = 0
|
||||||
if use_reachable != 0:
|
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||||
else:
|
if in_set == 0: continue
|
||||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
|
||||||
if in_set == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 读取文件
|
# read the file
|
||||||
file_path: str = entry.Path
|
file_path: str = entry.Path
|
||||||
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
|
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||||
if f.closed:
|
if f.closed: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||||
if f.size() >= Phase1.SRC_BUF_SIZE:
|
if f.size() >= Phase1.SRC_BUF_SIZE:
|
||||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
|
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path}", "Phase1")
|
||||||
f.close()
|
f.close()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -117,10 +71,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
|
|||||||
if bytes_read <= 0:
|
if bytes_read <= 0:
|
||||||
stdlib.free(src_buf)
|
stdlib.free(src_buf)
|
||||||
continue
|
continue
|
||||||
if bytes_read < Phase1.SRC_BUF_SIZE:
|
if bytes_read < Phase1.SRC_BUF_SIZE: src_buf[bytes_read] = 0
|
||||||
src_buf[bytes_read] = 0
|
|
||||||
|
|
||||||
# 解析 AST
|
# parse the AST
|
||||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||||
if lx is None:
|
if lx is None:
|
||||||
stdlib.free(src_buf)
|
stdlib.free(src_buf)
|
||||||
@@ -132,150 +85,141 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*,
|
|||||||
stdlib.free(src_buf)
|
stdlib.free(src_buf)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 遍历 AST 顶层节点,收集 ClassDef
|
# walk top-level AST nodes, collect ClassDef
|
||||||
ch: list[ast.AST*]* = tree.children
|
ch: list[ast.AST*]* = tree.children
|
||||||
if ch is not None:
|
if ch is not None:
|
||||||
cn: sizet = len(ch)
|
cn: sizet = len(ch)
|
||||||
for ci in range(cn):
|
for ci in range(int(cn)):
|
||||||
child: ast.AST* = ch.get(ci)
|
child: ast.AST* = ch.get(ci)
|
||||||
if child is None:
|
if child is None: continue
|
||||||
continue
|
|
||||||
kd: int = child.kind()
|
kd: int = child.kind()
|
||||||
if kd != ast.ASTKind.ClassDef:
|
if kd != ast.ASTKind.ClassDef: continue
|
||||||
continue
|
|
||||||
|
|
||||||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||||||
if cd is None or cd.name is None:
|
if cd is None or cd.name is None: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# 检测泛型类(有 type_params),记录文件索引以供 Phase 1a 预注册
|
# detect generic classes (type_params); record the file index for Phase 1a
|
||||||
if file_has_generic == 0:
|
if file_has_generic == 0:
|
||||||
tp_scan: list[str]* = cd.type_params
|
tp_scan: list[str]* = cd.type_params
|
||||||
if tp_scan is not None:
|
if tp_scan is not None:
|
||||||
file_has_generic = 1
|
file_has_generic = 1
|
||||||
if generic_file_count_box is not None:
|
if generic_file_count_box is not None:
|
||||||
gfc_ptr_scan: t.CPtr = (int | t.CPtr)(generic_file_count_box)
|
gfc_ptr_scan: int* = (int*)(generic_file_count_box)
|
||||||
if gfc_ptr_scan is not None:
|
if gfc_ptr_scan is not None:
|
||||||
gfc_scan: int = gfc_ptr_scan[0]
|
gfc_scan: int = gfc_ptr_scan[0]
|
||||||
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
|
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
|
||||||
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
|
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
|
||||||
gi_ptr_scan: t.CPtr = (int | t.CPtr)(gi_addr_scan)
|
gi_ptr_scan: int* = (int*)(gi_addr_scan)
|
||||||
if gi_ptr_scan is not None:
|
if gi_ptr_scan is not None: gi_ptr_scan[0] = i
|
||||||
gi_ptr_scan[0] = i
|
|
||||||
gfc_ptr_scan[0] = gfc_scan + 1
|
gfc_ptr_scan[0] = gfc_scan + 1
|
||||||
|
|
||||||
if class_count >= Phase1.MAX_CLASSES:
|
if class_count >= Phase1.MAX_CLASSES: break
|
||||||
break
|
|
||||||
|
|
||||||
# 填充扁平数组:类名(偏移 = class_count * Phase1.CLASS_NAME_LEN)
|
# fill flat array: class name (offset = class_count * Phase1.CLASS_NAME_LEN)
|
||||||
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
name_dst: str = class_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||||
nm_len: sizet = string.strlen(cd.name)
|
nm_len: sizet = string.strlen(cd.name)
|
||||||
if nm_len >= Phase1.CLASS_NAME_LEN:
|
if nm_len >= Phase1.CLASS_NAME_LEN:
|
||||||
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
|
string.strncpy(name_dst, cd.name, Phase1.CLASS_NAME_LEN - 1)
|
||||||
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
name_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||||
else:
|
else: string.strcpy(name_dst, cd.name)
|
||||||
string.strcpy(name_dst, cd.name)
|
|
||||||
|
|
||||||
# 填充扁平数组:父类名(无父类填 '\0')
|
# fill flat array: parent name (no parent -> '\0')
|
||||||
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
parent_dst: str = parent_names_buf + class_count * Phase1.CLASS_NAME_LEN
|
||||||
parent_dst[0] = '\0'
|
parent_dst[0] = '\0'
|
||||||
parent_nm: str = GenApi._get_parent_class(cd, None)
|
parent_nm: str = GenApi._get_parent_class(cd, None)
|
||||||
# parent_nm 是 AST 节点内部指针,不需要释放
|
# parent_nm is an internal AST pointer; no need to free
|
||||||
if parent_nm is not None:
|
if parent_nm is not None:
|
||||||
pnm_len: sizet = string.strlen(parent_nm)
|
pnm_len: sizet = string.strlen(parent_nm)
|
||||||
if pnm_len >= Phase1.CLASS_NAME_LEN:
|
if pnm_len >= Phase1.CLASS_NAME_LEN:
|
||||||
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
|
string.strncpy(parent_dst, parent_nm, Phase1.CLASS_NAME_LEN - 1)
|
||||||
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
parent_dst[Phase1.CLASS_NAME_LEN - 1] = '\0'
|
||||||
else:
|
else: string.strcpy(parent_dst, parent_nm)
|
||||||
string.strcpy(parent_dst, parent_nm)
|
|
||||||
|
|
||||||
# 填充扁平数组:定义 SHA1(偏移 = class_count * Phase1.SHA1_LEN)
|
# fill flat array: defining SHA1 (offset = class_count * Phase1.SHA1_LEN)
|
||||||
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
|
sha1_dst: str = def_sha1s_buf + class_count * Phase1.SHA1_LEN
|
||||||
string.strcpy(sha1_dst, sha1_e)
|
string.strcpy(sha1_dst, sha1_e)
|
||||||
|
|
||||||
class_count += 1
|
class_count += 1
|
||||||
|
|
||||||
stdlib.free(src_buf)
|
stdlib.free(src_buf)
|
||||||
# 回收本文件 AST/lexer/token(类名/父类/SHA1 已拷入扁平数组)
|
# reclaim this file's AST/lexer/token (names/SHA1 already copied into flat arrays)
|
||||||
mb.reset_to(scan_mark)
|
mb.reset_to(scan_mark)
|
||||||
|
|
||||||
return class_count
|
return class_count
|
||||||
# ============================================================
|
# _BufHasGenericClassMarker - text scan for a generic-class marker (PEP 695 "class
|
||||||
# _BufHasGenericClassMarker - 文本扫描检测文件是否含泛型类标记
|
# Name["). Used to pre-filter files before registering generic templates.
|
||||||
#
|
|
||||||
# 泛型类(PEP 695)语法: class Name[T]:(类名后紧跟 '[')。逐字节扫描找词边界 'class' 关键字,其后标识符后非空白是 '[' → 命中。
|
|
||||||
# 用于 _PreRegisterAllGenericTemplates 预注册前筛选,避免 parse 全部 98 个 includes 致 AST 驻留 mark1 前把 pool 推到 OOM。
|
|
||||||
# 误报(字符串/注释里的 "class X[")无害(多 parse 一个文件);漏报有害(泛型模板未预注册 → 悬空复发),故保守只匹配单行形式。
|
|
||||||
# ============================================================
|
|
||||||
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
|
def _BufHasGenericClassMarker(buf: bytes, blen: sizet) -> int:
|
||||||
"""文本扫描:buf 中是否有 'class 标识符[' 泛型类标记,返回 1/0"""
|
"""Return 1 if buf contains a 'class identifier[' generic-class marker, else 0."""
|
||||||
if buf is None or blen <= 6:
|
if buf is None or blen <= 6: return 0
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
pos: sizet = 0
|
||||||
while pos + 5 < blen:
|
while pos + 5 < blen:
|
||||||
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' \
|
if buf[pos] == 'c' and buf[pos + 1] == 'l' and buf[pos + 2] == 'a' and buf[pos + 3] == 's' and buf[pos + 4] == 's':
|
||||||
and buf[pos + 3] == 's' and buf[pos + 4] == 's':
|
# 'class' preceded by a word boundary (line start or non-identifier)
|
||||||
# class 前是词边界(行首或非标识符字符)
|
|
||||||
prev_ok: int = 1
|
prev_ok: int = 1
|
||||||
if pos > 0:
|
if pos > 0:
|
||||||
pc: char = buf[pos - 1]
|
pc: char = buf[pos - 1]
|
||||||
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') \
|
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||||
or(pc >= '0' and pc <= '9') or pc == '_':
|
|
||||||
prev_ok = 0
|
|
||||||
if prev_ok != 0:
|
if prev_ok != 0:
|
||||||
p: sizet = pos + 5
|
p: sizet = pos + 5
|
||||||
# 跳过空白
|
# skip whitespace
|
||||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
|
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||||
p += 1
|
# read the class-name identifier
|
||||||
# 读类名标识符
|
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||||
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
|
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||||
or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
|
||||||
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z')
|
|
||||||
or(buf[p] >= 'A' and buf[p] <= 'Z')
|
|
||||||
or(buf[p] >= '0' and buf[p] <= '9')
|
|
||||||
or buf[p] == '_'):
|
|
||||||
p += 1
|
p += 1
|
||||||
# 类名后非空白是 '[' → 泛型类
|
# non-space after the name is '[' -> generic class
|
||||||
while p < blen and(buf[p] == ' ' or buf[p] == '\t'):
|
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||||
p += 1
|
if p < blen and buf[p] == '[': return 1
|
||||||
if p < blen and buf[p] == '[':
|
|
||||||
return 1
|
|
||||||
pos += 1
|
|
||||||
else:
|
|
||||||
pos += 1
|
pos += 1
|
||||||
|
else: pos += 1
|
||||||
return 0
|
return 0
|
||||||
# ============================================================
|
# _BufHasGenericFuncMarker - text scan for a generic-function marker (PEP 695
|
||||||
# _PreRegisterAllGenericTemplates - 预注册 includes 泛型类模板(mark1 前)
|
# "def name["); cheap pre-filter before parsing for generic function templates.
|
||||||
#
|
def _BufHasGenericFuncMarker(buf: bytes, blen: sizet) -> int:
|
||||||
# 泛型模板表(GenericClassNames/GenericClassNodes/GenericClassSha1s/GenericClassFiles + 模板节点)是懒分配的跨文件持久表,
|
"""Return 1 if buf contains a 'def identifier[' generic-function marker, else 0."""
|
||||||
# 首次注册必须落在 mark1 之前才能跨 Phase1 1b(reset_to(mark1))与 Phase2(reset_to(mark2))存活;否则表与节点被 reset 回收,
|
if buf is None or blen <= 4: return 0
|
||||||
# _find_generic_template 遍历悬空内存 strcmp 崩溃。本函数先文本扫描(_BufHasGenericClassMarker,不 parse)筛选出含泛型类的文件,
|
pos: sizet = 0
|
||||||
# 仅对命中文件用**永久池(perm_pool)**解析(模板节点与表直接进 perm_pool,跨 mark 永久存活、不占主 pool);重复注册由 _register_generic_template 内部查重跳过。
|
while pos + 3 < blen:
|
||||||
# ============================================================
|
if buf[pos] == 'd' and buf[pos + 1] == 'e' and buf[pos + 2] == 'f':
|
||||||
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
|
# 'def' preceded by a word boundary (line start or non-identifier)
|
||||||
result: rawptr[IncludesScanner.ScanResult]) -> int:
|
prev_ok: int = 1
|
||||||
"""预注册全部 includes 文件的泛型类模板,返回处理到的泛型类数量"""
|
if pos > 0:
|
||||||
if result is None or mb is None:
|
pc: char = buf[pos - 1]
|
||||||
return 0
|
if(pc >= 'a' and pc <= 'z') or(pc >= 'A' and pc <= 'Z') or(pc >= '0' and pc <= '9') or pc == '_': prev_ok = 0
|
||||||
|
if prev_ok != 0:
|
||||||
|
p: sizet = pos + 3
|
||||||
|
# skip whitespace
|
||||||
|
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||||
|
# read the function-name identifier
|
||||||
|
if p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or buf[p] == '_'):
|
||||||
|
while p < blen and((buf[p] >= 'a' and buf[p] <= 'z') or(buf[p] >= 'A' and buf[p] <= 'Z') or(buf[p] >= '0' and buf[p] <= '9') or buf[p] == '_'):
|
||||||
|
p += 1
|
||||||
|
# non-space after the name is '[' -> generic function
|
||||||
|
while p < blen and(buf[p] == ' ' or buf[p] == '\t'): p += 1
|
||||||
|
if p < blen and buf[p] == '[': return 1
|
||||||
|
pos += 1
|
||||||
|
else: pos += 1
|
||||||
|
return 0
|
||||||
|
# _PreRegisterAllGenericTemplates - pre-register includes generic templates before
|
||||||
|
# mark1 so the lazy tables survive later resets; parses only marker-matched files.
|
||||||
|
def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*) -> int:
|
||||||
|
"""Pre-register generic templates from all includes files and return the count."""
|
||||||
|
if result is None or mb is None: return 0
|
||||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||||
if perm is None:
|
if perm is None: return 0
|
||||||
return 0
|
|
||||||
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||||
handled_count: int = 0
|
handled_count: int = 0
|
||||||
for i in range(result.Count):
|
for i in range(int(result.Count)):
|
||||||
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
|
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
|
||||||
entry_pg: rawptr[IncludesScanner.FileEntry] = (
|
entry_pg: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(entry_addr_pg)
|
||||||
rawptr[IncludesScanner.FileEntry])(entry_addr_pg)
|
if entry_pg is None or entry_pg.Sha1 is None: continue
|
||||||
if entry_pg is None or entry_pg.Sha1 is None:
|
# read the file
|
||||||
continue
|
|
||||||
# 读取文件
|
|
||||||
file_path_pg: str = entry_pg.Path
|
file_path_pg: str = entry_pg.Path
|
||||||
f_pg: rawptr[fileio.File] = fileio.File(file_path_pg, fileio.MODE.R)
|
f_pg: fileio.File* = fileio.File(file_path_pg, fileio.MODE.R)
|
||||||
if f_pg.closed:
|
if f_pg.closed: continue
|
||||||
continue
|
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
|
||||||
# read_all 的容量参数含终止符(最多读 cap-1 字节), 文件更长会被静默截断
|
|
||||||
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
|
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
|
||||||
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
|
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path_pg}", "Phase1")
|
||||||
f_pg.close()
|
f_pg.close()
|
||||||
continue
|
continue
|
||||||
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
|
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
|
||||||
@@ -287,13 +231,13 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
|
|||||||
if bytes_read_pg <= 0:
|
if bytes_read_pg <= 0:
|
||||||
stdlib.free(src_buf_pg)
|
stdlib.free(src_buf_pg)
|
||||||
continue
|
continue
|
||||||
if bytes_read_pg < Phase1.SRC_BUF_SIZE:
|
if bytes_read_pg < Phase1.SRC_BUF_SIZE: src_buf_pg[bytes_read_pg] = 0
|
||||||
src_buf_pg[bytes_read_pg] = 0
|
# Text-scan filter: skip non-generic files (no parse, no residency, avoids
|
||||||
# 文本扫描筛选:非泛型文件跳过(不 parse、不驻留,防 OOM)
|
# OOM). A class or function marker hit selects the file for parsing.
|
||||||
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0:
|
if _BufHasGenericClassMarker(src_buf_pg, bytes_read_pg) == 0 and _BufHasGenericFuncMarker(src_buf_pg, bytes_read_pg) == 0:
|
||||||
stdlib.free(src_buf_pg)
|
stdlib.free(src_buf_pg)
|
||||||
continue
|
continue
|
||||||
# 泛型文件:永久池解析 → 注册模板(节点/表进 perm_pool,跨 reset 存活)
|
# generic file: parse in perm_pool and register templates (survives reset)
|
||||||
lx_pg: ast.Lexer* = ast.new_lexer(perm)
|
lx_pg: ast.Lexer* = ast.new_lexer(perm)
|
||||||
if lx_pg is None:
|
if lx_pg is None:
|
||||||
stdlib.free(src_buf_pg)
|
stdlib.free(src_buf_pg)
|
||||||
@@ -304,96 +248,75 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*,
|
|||||||
if tree_pg is None:
|
if tree_pg is None:
|
||||||
stdlib.free(src_buf_pg)
|
stdlib.free(src_buf_pg)
|
||||||
continue
|
continue
|
||||||
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
|
# walk top-level ClassDef/FunctionDef, register generic templates (type_params != None)
|
||||||
ch_pg: list[ast.AST*]* = tree_pg.children
|
ch_pg: list[ast.AST*]* = tree_pg.children
|
||||||
if ch_pg is not None:
|
if ch_pg is not None:
|
||||||
cn_pg: sizet = len(ch_pg)
|
cn_pg: sizet = len(ch_pg)
|
||||||
for ci_pg in range(cn_pg):
|
for ci_pg in range(int(cn_pg)):
|
||||||
child_pg: ast.AST* = ch_pg.get(ci_pg)
|
child_pg: ast.AST* = ch_pg.get(ci_pg)
|
||||||
if child_pg is None or child_pg.kind() != ast.ASTKind.ClassDef:
|
if child_pg is None: continue
|
||||||
continue
|
ck_pg: int = child_pg.kind()
|
||||||
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
|
if ck_pg == ast.ASTKind.ClassDef:
|
||||||
if cd_pg is None or cd_pg.name is None:
|
cd_pg: ast.ClassDef* = ast.ClassDef*(child_pg)
|
||||||
continue
|
if cd_pg is None or cd_pg.name is None: continue
|
||||||
tp_pg: list[str]* = cd_pg.type_params
|
tp_pg: list[str]* = cd_pg.type_params
|
||||||
if tp_pg is None:
|
if tp_pg is None: continue
|
||||||
continue
|
GenApi.set_current_file_type(file_path_pg)
|
||||||
GenApi.set_current_file_type(file_path_pg)
|
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
|
||||||
GenApi._register_generic_template(perm, cd_pg, entry_pg.Sha1)
|
handled_count += 1
|
||||||
handled_count += 1
|
elif ck_pg == ast.ASTKind.FunctionDef:
|
||||||
|
fd_pg: ast.FunctionDef* = ast.FunctionDef*(child_pg)
|
||||||
|
if fd_pg is None or fd_pg.name is None: continue
|
||||||
|
tf_pg: list[str]* = fd_pg.type_params
|
||||||
|
if tf_pg is None: continue
|
||||||
|
GenApi.set_current_file_type(file_path_pg)
|
||||||
|
GenApi._register_generic_func_template(perm, fd_pg, entry_pg.Sha1)
|
||||||
|
handled_count += 1
|
||||||
stdlib.free(src_buf_pg)
|
stdlib.free(src_buf_pg)
|
||||||
if handled_count > 0:
|
if handled_count > 0: VLogger.info(f"pre-registered {handled_count} includes generic templates (classes and functions) in the permanent pool before mark1", "Phase1")
|
||||||
VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型类模板", "Phase1")
|
else: VLogger.info(f"pre-registered 0 generic templates in the permanent pool before mark1 (scanned {result.Count} files; the marker may not have matched or the full scan failed)", "Phase1")
|
||||||
else:
|
|
||||||
VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
|
|
||||||
return handled_count
|
return handled_count
|
||||||
# ============================================================
|
# _FindClassDefSha1 - look up a class name's defining file SHA1 in the flat arrays;
|
||||||
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
|
# returns a pointer into def_sha1s_buf, or None if not found.
|
||||||
#
|
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, class_name: str) -> str:
|
||||||
# Args: class_names_buf 类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;class_count 类数量;class_name 要查找的类名
|
"""Find a class name's defining file SHA1 in the flat arrays."""
|
||||||
# Returns: SHA1 字符串指针(指向 def_sha1s_buf 内部偏移),None 未找到
|
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
|
||||||
# ============================================================
|
for i in range(int(class_count)):
|
||||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
|
|
||||||
class_count: int, class_name: str) -> str:
|
|
||||||
"""查找类名对应的定义文件 SHA1"""
|
|
||||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
|
|
||||||
return None
|
|
||||||
for i in range(class_count):
|
|
||||||
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
|
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
|
||||||
if string.strcmp(name_ptr, class_name) == 0:
|
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
|
||||||
return def_sha1s_buf + i * Phase1.SHA1_LEN
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _ScanTreeForContainers - recursively scan the AST for list/dict container usage:
|
||||||
# _ScanTreeForContainers - 递归扫描 AST 检测 list/dict 容器使用
|
# recurse expression/annotation struct fields first, then statement-block children.
|
||||||
#
|
|
||||||
# TPV 的 AST 中语句块挂在 children,而表达式/注解树全部以结构体字段挂载(value/slice/func/args/annotation/targets/returns/bases/left/right 等);
|
|
||||||
# 只递归 children 会漏掉 list[...] | t.CPtr、list[...](...)、字段注解等非语句块位置,故每个节点先递归表达式承载字段、再递归 children。
|
|
||||||
# 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def _AnnIsContainer(ann: ast.AST*) -> int:
|
def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||||
"""注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...])
|
"""Return 1 if the annotation is a list/dict container, else 0."""
|
||||||
|
|
||||||
数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _ScanTreeForContainers
|
|
||||||
把数组字面量误判为 list 容器使用(FIX 2026-08-30,联动 Translator.vp 同修)。
|
|
||||||
"""
|
|
||||||
while ann is not None:
|
while ann is not None:
|
||||||
ak_c: int = ann.kind()
|
ak_c: int = ann.kind()
|
||||||
if ak_c == ast.ASTKind.Name:
|
if ak_c == ast.ASTKind.Name:
|
||||||
nm_c: ast.Name* = ast.Name*(ann)
|
nm_c: ast.Name* = ast.Name*(ann)
|
||||||
if nm_c.id is not None:
|
if nm_c.id is not None:
|
||||||
if string.strcmp(nm_c.id, "list") == 0:
|
if string.strcmp(nm_c.id, "list") == 0: return 1
|
||||||
return 1
|
if string.strcmp(nm_c.id, "dict") == 0: return 1
|
||||||
if string.strcmp(nm_c.id, "dict") == 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
elif ak_c == ast.ASTKind.Subscript:
|
elif ak_c == ast.ASTKind.Subscript:
|
||||||
sub_c: ast.Subscript* = ast.Subscript*(ann)
|
sub_c: ast.Subscript* = ast.Subscript*(ann)
|
||||||
ann = sub_c.value
|
ann = sub_c.value
|
||||||
else:
|
else: return 0
|
||||||
return 0
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def _ScanTreeForContainers(node: ast.AST*,
|
def _ScanTreeForContainers(node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr, depth: int = 0):
|
||||||
need_list_box: t.CPtr,
|
"""Recursively scan the AST for list/dict container usage."""
|
||||||
need_dict_box: t.CPtr,
|
# Recursion depth guard: a self-referential AST cycle would recurse until the
|
||||||
depth: int = 0):
|
# stack is exhausted; fail-fast abort.
|
||||||
"""递归扫描 AST(字段 + 语句块),检测 list/dict 容器使用"""
|
if depth > 128: VLogger.error("container scan recursion too deep (AST cycle or nesting over 128 levels)", "Phase1")
|
||||||
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
|
if node is None: return
|
||||||
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
|
|
||||||
if depth > 128:
|
|
||||||
VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
|
|
||||||
if node is None:
|
|
||||||
return
|
|
||||||
k_s: int = node.kind()
|
k_s: int = node.kind()
|
||||||
if k_s == ast.ASTKind.Name:
|
if k_s == ast.ASTKind.Name:
|
||||||
nm_s: ast.Name* = ast.Name*(node)
|
nm_s: ast.Name* = ast.Name*(node)
|
||||||
if nm_s.id is not None:
|
if nm_s.id is not None:
|
||||||
if string.strcmp(nm_s.id, "list") == 0:
|
if string.strcmp(nm_s.id, "list") == 0: need_list_box[0] = 1
|
||||||
need_list_box[0] = 1
|
elif string.strcmp(nm_s.id, "dict") == 0: need_dict_box[0] = 1
|
||||||
elif string.strcmp(nm_s.id, "dict") == 0:
|
|
||||||
need_dict_box[0] = 1
|
|
||||||
elif k_s == ast.ASTKind.Subscript:
|
elif k_s == ast.ASTKind.Subscript:
|
||||||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||||||
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
@@ -409,16 +332,14 @@ def _ScanTreeForContainers(node: ast.AST*,
|
|||||||
if cl_args_s is not None:
|
if cl_args_s is not None:
|
||||||
na_s: sizet = len(cl_args_s)
|
na_s: sizet = len(cl_args_s)
|
||||||
i_a2: sizet
|
i_a2: sizet
|
||||||
for i_a2 in range(na_s):
|
for i_a2 in range(int(na_s)): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
|
||||||
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
|
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
|
||||||
if cl_kws_s is not None:
|
if cl_kws_s is not None:
|
||||||
nk_s: sizet = len(cl_kws_s)
|
nk_s: sizet = len(cl_kws_s)
|
||||||
i_k2: sizet
|
i_k2: sizet
|
||||||
for i_k2 in range(nk_s):
|
for i_k2 in range(int(nk_s)):
|
||||||
kw_s: rawptr[ast.Keyword] = rawptr[ast.Keyword](cl_kws_s.get(i_k2))
|
kw_s: ast.Keyword* = ast.Keyword*(cl_kws_s.get(i_k2))
|
||||||
if kw_s is not None:
|
if kw_s is not None: _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.Assign:
|
elif k_s == ast.ASTKind.Assign:
|
||||||
asn_s: ast.Assign* = ast.Assign*(node)
|
asn_s: ast.Assign* = ast.Assign*(node)
|
||||||
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
@@ -426,8 +347,7 @@ def _ScanTreeForContainers(node: ast.AST*,
|
|||||||
if tgts_s is not None:
|
if tgts_s is not None:
|
||||||
nt_s: sizet = len(tgts_s)
|
nt_s: sizet = len(tgts_s)
|
||||||
i_t2: sizet
|
i_t2: sizet
|
||||||
for i_t2 in range(nt_s):
|
for i_t2 in range(int(nt_s)): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.AnnAssign:
|
elif k_s == ast.ASTKind.AnnAssign:
|
||||||
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
|
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
|
||||||
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
|
||||||
@@ -441,18 +361,16 @@ def _ScanTreeForContainers(node: ast.AST*,
|
|||||||
if fa_s is not None:
|
if fa_s is not None:
|
||||||
nf_s: sizet = len(fa_s)
|
nf_s: sizet = len(fa_s)
|
||||||
i_f2: sizet
|
i_f2: sizet
|
||||||
for i_f2 in range(nf_s):
|
for i_f2 in range(int(nf_s)):
|
||||||
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
|
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
|
||||||
if arg_s is not None:
|
if arg_s is not None: _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.ClassDef:
|
elif k_s == ast.ASTKind.ClassDef:
|
||||||
cd_s: ast.ClassDef* = ast.ClassDef*(node)
|
cd_s: ast.ClassDef* = ast.ClassDef*(node)
|
||||||
bases_s: list[ast.AST*]* = cd_s.bases
|
bases_s: list[ast.AST*]* = cd_s.bases
|
||||||
if bases_s is not None:
|
if bases_s is not None:
|
||||||
nb_s: sizet = len(bases_s)
|
nb_s: sizet = len(bases_s)
|
||||||
i_b2: sizet
|
i_b2: sizet
|
||||||
for i_b2 in range(nb_s):
|
for i_b2 in range(int(nb_s)): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.Attribute:
|
elif k_s == ast.ASTKind.Attribute:
|
||||||
at_s: ast.Attribute* = ast.Attribute*(node)
|
at_s: ast.Attribute* = ast.Attribute*(node)
|
||||||
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
@@ -462,44 +380,35 @@ def _ScanTreeForContainers(node: ast.AST*,
|
|||||||
if elts_s is not None:
|
if elts_s is not None:
|
||||||
ne_s: sizet = len(elts_s)
|
ne_s: sizet = len(elts_s)
|
||||||
i_e2: sizet
|
i_e2: sizet
|
||||||
for i_e2 in range(ne_s):
|
for i_e2 in range(int(ne_s)): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.UnaryOp:
|
elif k_s == ast.ASTKind.UnaryOp:
|
||||||
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
|
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||||
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
|
||||||
elif k_s == ast.ASTKind.Expr:
|
elif k_s == ast.ASTKind.Expr:
|
||||||
ex_s: rawptr[ast.Expr] = rawptr[ast.Expr](node)
|
ex_s: ast.Expr* = ast.Expr*(node)
|
||||||
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
elif k_s == ast.ASTKind.Return:
|
elif k_s == ast.ASTKind.Return:
|
||||||
rt_s: rawptr[ast.Return] = rawptr[ast.Return](node)
|
rt_s: ast.Return* = ast.Return*(node)
|
||||||
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
|
||||||
elif k_s == ast.ASTKind.Compare:
|
elif k_s == ast.ASTKind.Compare:
|
||||||
cp_s: rawptr[ast.Compare] = rawptr[ast.Compare](node)
|
cp_s: ast.Compare* = ast.Compare*(node)
|
||||||
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
|
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
|
||||||
cps_s: list[ast.AST*]* = cp_s.comparators
|
cps_s: list[ast.AST*]* = cp_s.comparators
|
||||||
if cps_s is not None:
|
if cps_s is not None:
|
||||||
nc_s: sizet = len(cps_s)
|
nc_s: sizet = len(cps_s)
|
||||||
i_c2: sizet
|
i_c2: sizet
|
||||||
for i_c2 in range(nc_s):
|
for i_c2 in range(int(nc_s)): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
|
||||||
elif k_s == ast.ASTKind.BoolOp:
|
elif k_s == ast.ASTKind.BoolOp:
|
||||||
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
|
bo_s: ast.BoolOp* = ast.BoolOp*(node)
|
||||||
bvs_s: list[ast.AST*]* = bo_s.values
|
bvs_s: list[ast.AST*]* = bo_s.values
|
||||||
if bvs_s is not None:
|
if bvs_s is not None:
|
||||||
nv_s: sizet = len(bvs_s)
|
nv_s: sizet = len(bvs_s)
|
||||||
i_v2: sizet
|
i_v2: sizet
|
||||||
for i_v2 in range(nv_s):
|
for i_v2 in range(int(nv_s)): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
# `[...]`/`{}` literals do not trigger _list/_dict imports (compile-time constant lists, array init);
|
||||||
# `[...]`/`{}` 字面量不触发 _list/_dict 导入:字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数)与数组数据初始化,并非容器使用;
|
# recurse into statement-block children.
|
||||||
# 容器的真实使用由类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。
|
|
||||||
# elif k_s == ast.ASTKind.List:
|
|
||||||
# need_list_box[0] = 1
|
|
||||||
# elif k_s == ast.ASTKind.Dict:
|
|
||||||
# need_dict_box[0] = 1
|
|
||||||
# 递归语句块子节点
|
|
||||||
if node.children is not None:
|
if node.children is not None:
|
||||||
ch_s: list[ast.AST*]* = node.children
|
ch_s: list[ast.AST*]* = node.children
|
||||||
n_s: sizet = len(ch_s)
|
n_s: sizet = len(ch_s)
|
||||||
ci_s: sizet
|
ci_s: sizet
|
||||||
for ci_s in range(n_s):
|
for ci_s in range(int(n_s)): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||||
_ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
|
||||||
+132
-214
@@ -1,160 +1,109 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import memhub
|
import memhub
|
||||||
import viperlib
|
|
||||||
import w32.fileio as fileio
|
|
||||||
import w32.win32file
|
|
||||||
import w32.win32base
|
|
||||||
import ast
|
|
||||||
import llvmlite
|
|
||||||
import lib.core.VLogger as VLogger
|
|
||||||
import lib.core.Gen.Translator as HandlesTranslator
|
|
||||||
import lib.core.IncludesScanner as IncludesScanner
|
import lib.core.IncludesScanner as IncludesScanner
|
||||||
import lib.Stub.StubMerger as StubMerger
|
import lib.Stub.StubMerger as StubMerger
|
||||||
import lib.core.Gen.Generator as GenGenerator
|
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
|
||||||
import lib.Phase.BuildPipeline as BuildPipeline
|
|
||||||
import lib.Projectrans.Config as Config
|
|
||||||
import lib.Stub.StubGen.Converter as StubConverter
|
|
||||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
|
||||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||||
|
|
||||||
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
|
||||||
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
|
||||||
import lib.Phase.Phase1 as Phase1
|
import lib.Phase.Phase1 as Phase1
|
||||||
|
|
||||||
# ============================================================
|
# Phase1Topo - topologically sort source files by class inheritance and import dependencies.
|
||||||
# Phase1Topo - 按类继承与 import 依赖对源文件做拓扑排序
|
# Functions live in this module (accessed via its prefix); globals go through Phase1._xxx.
|
||||||
#
|
|
||||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局符号经 Phase1._xxx 跨模块访问。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# _TopoSortFiles - topologically sort files by class inheritance (Kahn).
|
||||||
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序(Kahn)
|
# Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1.
|
||||||
#
|
def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: rawptr, out_count: rawptr, temp_dir: str, td_len_ts: sizet) -> int:
|
||||||
# 步骤:1) 从扁平类信息数组构建「文件 SHA1 → 依赖文件 SHA1 集合」映射;2) 计算每个文件入度(DepCount,初始化 InDegree = DepCount);
|
"""Topologically sort files by class inheritance and import dependencies."""
|
||||||
# 3) 入度为 0 的先处理,处理后减少依赖它的文件入度;4) 重复直到全部处理完成。
|
if result is None or out_order is None or out_count is None: return -1
|
||||||
# 依赖方向是「父类所在文件 → 子类所在文件」,即父类文件必须先处理。
|
|
||||||
#
|
|
||||||
# Args: mb 内存池;result IncludesScanner 扫描结果;reachable_set 可达 SHA1 集合;reachable_count 可达 SHA1 数量;use_reachable 是否用可达集合过滤;
|
|
||||||
# sha1_set SHA1 集合;set_count SHA1 集合数量;class_names_buf 类名扁平数组;parent_names_buf 父类名扁平数组;def_sha1s_buf 定义 SHA1 扁平数组;
|
|
||||||
# class_count 类数量;out_order 输出拓扑顺序的文件索引数组(调用者分配,int 数组);out_count 输出文件数量
|
|
||||||
# Returns: 0 成功,-1 失败
|
|
||||||
# ============================================================
|
|
||||||
def _TopoSortFiles(mb: memhub.MemBuddy*,
|
|
||||||
result: rawptr[IncludesScanner.ScanResult],
|
|
||||||
reachable_set: str, reachable_count: int,
|
|
||||||
use_reachable: int,
|
|
||||||
sha1_set: str, set_count: int,
|
|
||||||
class_names_buf: bytes, parent_names_buf: bytes,
|
|
||||||
def_sha1s_buf: bytes, class_count: int,
|
|
||||||
out_order: t.CPtr, out_count: t.CPtr,
|
|
||||||
temp_dir: str, td_len_ts: sizet) -> int:
|
|
||||||
"""根据类继承关系对文件进行拓扑排序"""
|
|
||||||
if result is None or out_order is None or out_count is None:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||||
|
|
||||||
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
|
# 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags.
|
||||||
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
|
# Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing.
|
||||||
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||||
file_deps_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
# 取消整数提升后,模块属性 CDefine(Phase1.MAX_*) 按最小位宽物化为 i16/i8;三段乘积 256*16*17=69632 会在 i16 回绕,
|
||||||
file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
# 首乘数显式 sizet() 加宽到 64 位。上一行 256*17=4352 未超 i16,故无需改。
|
||||||
file_indegrees: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
file_deps_buf: bytes = mb.alloc(sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||||
file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
file_dep_counts: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||||
|
file_indegrees: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||||
|
file_processed: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||||
|
|
||||||
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
|
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
|
||||||
if file_sha1s_buf is not None:
|
if file_sha1s_buf is not None: mb.free(file_sha1s_buf)
|
||||||
mb.free(file_sha1s_buf)
|
if file_deps_buf is not None: mb.free(file_deps_buf)
|
||||||
if file_deps_buf is not None:
|
if file_dep_counts is not None: mb.free(file_dep_counts)
|
||||||
mb.free(file_deps_buf)
|
if file_indegrees is not None: mb.free(file_indegrees)
|
||||||
if file_dep_counts is not None:
|
if file_processed is not None: mb.free(file_processed)
|
||||||
mb.free(file_dep_counts)
|
|
||||||
if file_indegrees is not None:
|
|
||||||
mb.free(file_indegrees)
|
|
||||||
if file_processed is not None:
|
|
||||||
mb.free(file_processed)
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||||
string.memset(file_deps_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
string.memset(file_deps_buf, 0, sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||||
string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4)
|
string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||||
string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4)
|
string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||||
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
|
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||||
|
|
||||||
file_count: int = 0
|
file_count: int = 0
|
||||||
# sha1→file_index 映射(简单线性查找,文件数不多)
|
# sha1 -> file_index map (simple linear search; file count is small)
|
||||||
for i in range(result.Count):
|
for i in range(int(result.Count)):
|
||||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||||
entry: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](entry_addr)
|
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||||
if entry is None or entry.Sha1 is None:
|
if entry is None or entry.Sha1 is None: continue
|
||||||
continue
|
|
||||||
|
|
||||||
sha1_e: str = entry.Sha1
|
sha1_e: str = entry.Sha1
|
||||||
in_set: int = 0
|
in_set: int = 0
|
||||||
if use_reachable != 0:
|
if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
||||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
|
else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
||||||
else:
|
if in_set == 0: continue
|
||||||
in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count)
|
|
||||||
if in_set == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if file_count >= Phase1.MAX_FILE_DEPS:
|
if file_count >= Phase1.MAX_FILE_DEPS: break
|
||||||
break
|
|
||||||
|
|
||||||
# 初始化扁平数组:文件 SHA1(偏移 = file_count * Phase1.SHA1_LEN)
|
# Initialize the flat array: file SHA1 (offset = file_count * Phase1.SHA1_LEN).
|
||||||
# DepCount/InDegree/Processed 已由 memset 清零
|
# DepCount/InDegree/Processed are already zeroed by memset.
|
||||||
sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN
|
sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN
|
||||||
string.strcpy(sha1_dst, sha1_e)
|
string.strcpy(sha1_dst, sha1_e)
|
||||||
|
|
||||||
file_count += 1
|
file_count += 1
|
||||||
|
|
||||||
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
|
# 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge
|
||||||
for ci in range(class_count):
|
for ci in range(int(class_count)):
|
||||||
# 读取父类名(偏移 = ci * Phase1.CLASS_NAME_LEN)
|
# Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN)
|
||||||
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
|
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
|
||||||
if parent_ptr[0] == '\0':
|
if parent_ptr[0] == '\0': continue
|
||||||
continue
|
|
||||||
|
|
||||||
# 读取子类定义 SHA1(偏移 = ci * Phase1.SHA1_LEN)
|
# Read child class definition SHA1 (offset = ci * Phase1.SHA1_LEN)
|
||||||
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
|
child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN
|
||||||
|
|
||||||
# 查找父类定义所在文件的 SHA1
|
# Find the SHA1 of the file defining the parent class
|
||||||
parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
|
parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
|
||||||
if parent_sha1 is None:
|
if parent_sha1 is None:
|
||||||
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
|
continue # parent not in includes (e.g. Object, CEnum marker base classes)
|
||||||
|
|
||||||
# 父类文件 SHA1 不能等于子类文件 SHA1(同文件内继承不需要依赖)
|
# The parent file SHA1 must differ from the child's (same-file inheritance needs no dependency)
|
||||||
if string.strcmp(parent_sha1, child_def_sha1) == 0:
|
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
|
# Find the child file's index and add the parent file SHA1 to its dependency list
|
||||||
for fi in range(file_count):
|
for fi in range(int(file_count)):
|
||||||
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||||
if string.strcmp(fsha1, child_def_sha1) != 0:
|
if string.strcmp(fsha1, child_def_sha1) != 0: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# 读取当前依赖数量
|
# Read the current dependency count
|
||||||
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
|
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
|
||||||
cnt_ptr: t.CPtr = (int | t.CPtr)(cnt_addr)
|
cnt_ptr: int* = (int*)(cnt_addr)
|
||||||
if cnt_ptr is None:
|
if cnt_ptr is None: break
|
||||||
break
|
|
||||||
cur_cnt: int = cnt_ptr[0]
|
cur_cnt: int = cnt_ptr[0]
|
||||||
|
|
||||||
# 检查依赖是否已添加(去重)
|
# Check whether the dependency is already present (dedupe)
|
||||||
already: int = 0
|
already: int = 0
|
||||||
for di in range(cur_cnt):
|
for di in range(int(cur_cnt)):
|
||||||
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||||
if string.strcmp(dep_slot, parent_sha1) == 0:
|
if string.strcmp(dep_slot, parent_sha1) == 0:
|
||||||
already = 1
|
already = 1
|
||||||
break
|
break
|
||||||
if already != 0:
|
if already != 0: break
|
||||||
break
|
|
||||||
|
|
||||||
if cur_cnt < Phase1.MAX_DEP_EDGES:
|
if cur_cnt < Phase1.MAX_DEP_EDGES:
|
||||||
dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN
|
dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN
|
||||||
@@ -162,59 +111,44 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
|
|||||||
cnt_ptr[0] = cur_cnt + 1
|
cnt_ptr[0] = cur_cnt + 1
|
||||||
break
|
break
|
||||||
|
|
||||||
# 2.5 补充 import 依赖边:被 import 的模块先完整翻译。
|
# 2.5 Add import dependency edges: imported modules must be fully translated first.
|
||||||
#
|
# Class inheritance alone is insufficient (cross-module globals/vtable TypeStr/generics need deps); deps come from Phase 1a-pre, mapped via _BuildIncludesSha1Map.
|
||||||
# 仅按类继承建边不够:跨模块全局变量 stub、跨模块 vtable TypeStr、泛型模板都要求依赖模块先翻译,而它们之间可能无类继承关系。
|
|
||||||
# deps(_includes_deps_*)由 Phase 1a-pre 填充(含容器按需注入的 _list/_dict);用 _BuildIncludesSha1Map 复制局部 sha1→模块名 数组
|
|
||||||
# (与 _BuildReachableSha1Set 的 BFS 一致),避免直接跨函数传全局 store 返回值导致地址异常。
|
|
||||||
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||||
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||||
map_count_ts: int = 0
|
map_count_ts: int = 0
|
||||||
if sha1_arr_ts is not None and mod_arr_ts is not None:
|
if sha1_arr_ts is not None and mod_arr_ts is not None:
|
||||||
map_count_ts = StubMergerIR._BuildIncludesSha1Map(
|
map_count_ts = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
|
||||||
temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
|
for fi_imp in range(int(file_count)):
|
||||||
for fi_imp in range(file_count):
|
if map_count_ts <= 0: break
|
||||||
if map_count_ts <= 0:
|
|
||||||
break
|
|
||||||
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
|
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
|
||||||
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
|
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
|
||||||
if deps_imp is None:
|
if deps_imp is None: continue
|
||||||
continue
|
|
||||||
dl_imp: sizet = string.strlen(deps_imp)
|
dl_imp: sizet = string.strlen(deps_imp)
|
||||||
dpos_imp: sizet = 0
|
dpos_imp: sizet = 0
|
||||||
while dpos_imp < dl_imp:
|
while dpos_imp < dl_imp:
|
||||||
dstart_imp: sizet = dpos_imp
|
dstart_imp: sizet = dpos_imp
|
||||||
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ':
|
while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ': dpos_imp += 1
|
||||||
dpos_imp += 1
|
|
||||||
dlen_imp: sizet = dpos_imp - dstart_imp
|
dlen_imp: sizet = dpos_imp - dstart_imp
|
||||||
if dpos_imp < dl_imp:
|
if dpos_imp < dl_imp: dpos_imp += 1
|
||||||
dpos_imp += 1
|
if dlen_imp == 0: continue
|
||||||
if dlen_imp == 0:
|
|
||||||
continue
|
|
||||||
dname_imp: bytes = stdlib.malloc(dlen_imp + 1)
|
dname_imp: bytes = stdlib.malloc(dlen_imp + 1)
|
||||||
if dname_imp is None:
|
if dname_imp is None: continue
|
||||||
continue
|
|
||||||
string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp)
|
string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp)
|
||||||
dname_imp[dlen_imp] = '\0'
|
dname_imp[dlen_imp] = '\0'
|
||||||
dsha1_imp: str = StubMergerIR._FindSha1ByModName(
|
dsha1_imp: str = StubMergerIR._FindSha1ByModName(sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
|
||||||
sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp)
|
|
||||||
stdlib.free(dname_imp)
|
stdlib.free(dname_imp)
|
||||||
if dsha1_imp is None:
|
if dsha1_imp is None: continue
|
||||||
continue
|
# Find the dependency file's index in the list (skip self)
|
||||||
# 找依赖文件在列表中的索引(跳过自身)
|
for fj_imp in range(int(file_count)):
|
||||||
for fj_imp in range(file_count):
|
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
|
||||||
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0:
|
if fj_imp == fi_imp: break
|
||||||
continue
|
# Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++)
|
||||||
if fj_imp == fi_imp:
|
|
||||||
break
|
|
||||||
# 去重 + 加边:fi_imp 依赖 fj_imp(InDegree[fi_imp]++)
|
|
||||||
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
|
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
|
||||||
cnt_ptr_imp: t.CPtr = (int | t.CPtr)(cnt_addr_imp)
|
cnt_ptr_imp: int* = (int*)(cnt_addr_imp)
|
||||||
if cnt_ptr_imp is None:
|
if cnt_ptr_imp is None: break
|
||||||
break
|
|
||||||
cur_cnt_imp: int = cnt_ptr_imp[0]
|
cur_cnt_imp: int = cnt_ptr_imp[0]
|
||||||
already_imp: int = 0
|
already_imp: int = 0
|
||||||
for di_imp in range(cur_cnt_imp):
|
for di_imp in range(int(cur_cnt_imp)):
|
||||||
dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN
|
dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN
|
||||||
if string.strcmp(dep_slot_imp, dsha1_imp) == 0:
|
if string.strcmp(dep_slot_imp, dsha1_imp) == 0:
|
||||||
already_imp = 1
|
already_imp = 1
|
||||||
@@ -225,117 +159,104 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
|
|||||||
cnt_ptr_imp[0] = cur_cnt_imp + 1
|
cnt_ptr_imp[0] = cur_cnt_imp + 1
|
||||||
break
|
break
|
||||||
|
|
||||||
# 释放 import 边用的局部映射数组
|
# Free the local mapping arrays used for import edges
|
||||||
if sha1_arr_ts is not None:
|
if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts)
|
||||||
stdlib.free(sha1_arr_ts)
|
if mod_arr_ts is not None: stdlib.free(mod_arr_ts)
|
||||||
if mod_arr_ts is not None:
|
|
||||||
stdlib.free(mod_arr_ts)
|
|
||||||
|
|
||||||
# 3. Kahn 算法:InDegree = DepCount(依赖多少个父文件)
|
# 3. Kahn: InDegree = DepCount (number of parent files depended on).
|
||||||
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
|
# Process zero-dependency files first, then decrement the in-degree of dependents.
|
||||||
for fi in range(file_count):
|
for fi in range(int(file_count)):
|
||||||
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
|
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
|
||||||
cnt_ptr3: t.CPtr = (int | t.CPtr)(cnt_addr3)
|
cnt_ptr3: int* = (int*)(cnt_addr3)
|
||||||
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
|
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
|
||||||
ind_ptr3: t.CPtr = (int | t.CPtr)(ind_addr3)
|
ind_ptr3: int* = (int*)(ind_addr3)
|
||||||
if cnt_ptr3 is not None and ind_ptr3 is not None:
|
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
|
||||||
ind_ptr3[0] = cnt_ptr3[0]
|
|
||||||
|
|
||||||
# 4. 拓扑排序输出
|
# 4. Topological sort output
|
||||||
out_idx: int = 0
|
out_idx: int = 0
|
||||||
changed: int = 1
|
changed: int = 1
|
||||||
while changed != 0:
|
while changed != 0:
|
||||||
changed = 0
|
changed = 0
|
||||||
for fi in range(file_count):
|
for fi in range(int(file_count)):
|
||||||
# 检查处理状态
|
# Check processed flag
|
||||||
proc_addr4: uint64 = uint64(file_processed) + fi * 4
|
proc_addr4: uint64 = uint64(file_processed) + fi * 4
|
||||||
proc_ptr4: t.CPtr = (int | t.CPtr)(proc_addr4)
|
proc_ptr4: int* = (int*)(proc_addr4)
|
||||||
if proc_ptr4 is None or proc_ptr4[0] != 0:
|
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
|
||||||
continue
|
# Check in-degree
|
||||||
# 检查入度
|
|
||||||
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
|
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
|
||||||
ind_ptr4: t.CPtr = (int | t.CPtr)(ind_addr4)
|
ind_ptr4: int* = (int*)(ind_addr4)
|
||||||
if ind_ptr4 is None or ind_ptr4[0] > 0:
|
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
|
||||||
continue
|
|
||||||
|
|
||||||
# 入度为 0,可以处理
|
# In-degree 0: can be processed
|
||||||
# 找到对应的 result.Entries 索引
|
# Find the matching result.Entries index
|
||||||
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||||
for ri in range(result.Count):
|
for ri in range(int(result.Count)):
|
||||||
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
|
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
|
||||||
ent_r: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r)
|
ent_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r)
|
||||||
if ent_r is not None and ent_r.Sha1 is not None:
|
if ent_r is not None and ent_r.Sha1 is not None:
|
||||||
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
||||||
# 写入输出索引数组
|
# Write into the output index array
|
||||||
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
|
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
|
||||||
out_int_ptr: t.CPtr = (int | t.CPtr)(out_idx_addr)
|
out_int_ptr: int* = (int*)(out_idx_addr)
|
||||||
if out_int_ptr is not None:
|
if out_int_ptr is not None: out_int_ptr[0] = ri
|
||||||
out_int_ptr[0] = ri
|
|
||||||
out_idx += 1
|
out_idx += 1
|
||||||
break
|
break
|
||||||
|
|
||||||
# 标记已处理
|
# Mark as processed
|
||||||
proc_ptr4[0] = 1
|
proc_ptr4[0] = 1
|
||||||
changed = 1
|
changed = 1
|
||||||
|
|
||||||
# 减少依赖该文件的其他文件的入度
|
# Decrement the in-degree of files that depend on this one
|
||||||
for fj in range(file_count):
|
for fj in range(int(file_count)):
|
||||||
proc_addr5: uint64 = uint64(file_processed) + fj * 4
|
proc_addr5: uint64 = uint64(file_processed) + fj * 4
|
||||||
proc_ptr5: t.CPtr = (int | t.CPtr)(proc_addr5)
|
proc_ptr5: int* = (int*)(proc_addr5)
|
||||||
if proc_ptr5 is None or proc_ptr5[0] != 0:
|
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
|
||||||
continue
|
# Read fj's dependency count
|
||||||
# 读取 fj 的依赖数量
|
|
||||||
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
|
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
|
||||||
cnt_ptr5: t.CPtr = (int | t.CPtr)(cnt_addr5)
|
cnt_ptr5: int* = (int*)(cnt_addr5)
|
||||||
if cnt_ptr5 is None:
|
if cnt_ptr5 is None: continue
|
||||||
continue
|
|
||||||
fj_dep_cnt: int = cnt_ptr5[0]
|
fj_dep_cnt: int = cnt_ptr5[0]
|
||||||
for di in range(fj_dep_cnt):
|
for di in range(int(fj_dep_cnt)):
|
||||||
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||||
if string.strcmp(dep_slot5, target_sha1) == 0:
|
if string.strcmp(dep_slot5, target_sha1) == 0:
|
||||||
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
|
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
|
||||||
ind_ptr5: t.CPtr = (int | t.CPtr)(ind_addr5)
|
ind_ptr5: int* = (int*)(ind_addr5)
|
||||||
if ind_ptr5 is not None:
|
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
|
||||||
ind_ptr5[0] = ind_ptr5[0] - 1
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# 处理循环依赖:环内优先输出被依赖者(被其余剩余文件依赖数量多者先)。
|
# Handle cycles: within a cycle emit the more-depended-upon files first.
|
||||||
#
|
# On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr.
|
||||||
# 容器按需注入(string 用 list 泛型 → 依赖 _list)与真实 import 会形成伪环,Kahn 无法完全归零;若按扫描顺序追加,
|
|
||||||
# 依赖者会先于被依赖者翻译 → 跨模块 vtable TypeStr 未注册 → 误杀。
|
|
||||||
if out_idx < file_count:
|
if out_idx < file_count:
|
||||||
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
|
rem_idx: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||||
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
|
rem_depcnt: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||||
rem_count: int = 0
|
rem_count: int = 0
|
||||||
if rem_idx is not None and rem_depcnt is not None:
|
if rem_idx is not None and rem_depcnt is not None:
|
||||||
for fi in range(file_count):
|
for fi in range(int(file_count)):
|
||||||
proc_addr7: uint64 = uint64(file_processed) + fi * 4
|
proc_addr7: uint64 = uint64(file_processed) + fi * 4
|
||||||
proc_ptr7: t.CPtr = (int | t.CPtr)(proc_addr7)
|
proc_ptr7: int* = (int*)(proc_addr7)
|
||||||
if proc_ptr7 is not None and proc_ptr7[0] == 0:
|
if proc_ptr7 is not None and proc_ptr7[0] == 0:
|
||||||
rem_idx[rem_count] = fi
|
rem_idx[rem_count] = fi
|
||||||
rem_depcnt[rem_count] = 0
|
rem_depcnt[rem_count] = 0
|
||||||
rem_count += 1
|
rem_count += 1
|
||||||
# 计算每个剩余文件的被依赖数(被其余剩余文件依赖)
|
# Compute each remaining file's dependents count (among the remaining files)
|
||||||
for i_a in range(rem_count):
|
for i_a in range(int(rem_count)):
|
||||||
fi_a: int = rem_idx[i_a]
|
fi_a: int = rem_idx[i_a]
|
||||||
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
|
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
|
||||||
for i_b in range(rem_count):
|
for i_b in range(int(rem_count)):
|
||||||
fi_b: int = rem_idx[i_b]
|
fi_b: int = rem_idx[i_b]
|
||||||
if fi_b == fi_a:
|
if fi_b == fi_a: continue
|
||||||
continue
|
|
||||||
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
|
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
|
||||||
cnt_ptr_b: t.CPtr = (int | t.CPtr)(cnt_addr_b)
|
cnt_ptr_b: int* = (int*)(cnt_addr_b)
|
||||||
if cnt_ptr_b is None:
|
if cnt_ptr_b is None: continue
|
||||||
continue
|
|
||||||
cnt_b: int = cnt_ptr_b[0]
|
cnt_b: int = cnt_ptr_b[0]
|
||||||
for di_b in range(cnt_b):
|
for di_b in range(int(cnt_b)):
|
||||||
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
|
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
|
||||||
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
|
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
|
||||||
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
|
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
|
||||||
break
|
break
|
||||||
# 冒泡降序(被依赖数多者在前)
|
# Bubble sort descending (more dependents first)
|
||||||
for i_a in range(rem_count):
|
for i_a in range(int(rem_count)):
|
||||||
for j_a in range(rem_count - 1):
|
for j_a in range(int(rem_count - 1)):
|
||||||
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
|
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
|
||||||
tmp_i: int = rem_idx[j_a]
|
tmp_i: int = rem_idx[j_a]
|
||||||
rem_idx[j_a] = rem_idx[j_a + 1]
|
rem_idx[j_a] = rem_idx[j_a + 1]
|
||||||
@@ -343,35 +264,32 @@ def _TopoSortFiles(mb: memhub.MemBuddy*,
|
|||||||
tmp_d: int = rem_depcnt[j_a]
|
tmp_d: int = rem_depcnt[j_a]
|
||||||
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
|
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
|
||||||
rem_depcnt[j_a + 1] = tmp_d
|
rem_depcnt[j_a + 1] = tmp_d
|
||||||
# 按序输出剩余文件
|
# Emit the remaining files in order
|
||||||
for i_a in range(rem_count):
|
for i_a in range(int(rem_count)):
|
||||||
fi_a2: int = rem_idx[i_a]
|
fi_a2: int = rem_idx[i_a]
|
||||||
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
|
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
|
||||||
proc_ptr8: t.CPtr = (int | t.CPtr)(proc_addr8)
|
proc_ptr8: int* = (int*)(proc_addr8)
|
||||||
if proc_ptr8 is None or proc_ptr8[0] != 0:
|
if proc_ptr8 is None or proc_ptr8[0] != 0: continue
|
||||||
continue
|
|
||||||
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
|
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
|
||||||
for ri in range(result.Count):
|
for ri in range(int(result.Count)):
|
||||||
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
|
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
|
||||||
ent_r2: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea_r2)
|
ent_r2: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r2)
|
||||||
if ent_r2 is not None and ent_r2.Sha1 is not None:
|
if ent_r2 is not None and ent_r2.Sha1 is not None:
|
||||||
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
|
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
|
||||||
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
|
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
|
||||||
out_int_ptr2: t.CPtr = (int | t.CPtr)(out_idx_addr2)
|
out_int_ptr2: int* = (int*)(out_idx_addr2)
|
||||||
if out_int_ptr2 is not None:
|
if out_int_ptr2 is not None: out_int_ptr2[0] = ri
|
||||||
out_int_ptr2[0] = ri
|
|
||||||
out_idx += 1
|
out_idx += 1
|
||||||
break
|
break
|
||||||
proc_ptr8[0] = 1
|
proc_ptr8[0] = 1
|
||||||
stdlib.free(rem_idx)
|
stdlib.free(rem_idx)
|
||||||
stdlib.free(rem_depcnt)
|
stdlib.free(rem_depcnt)
|
||||||
|
|
||||||
# 写入输出数量
|
# Write the output count
|
||||||
out_count_ptr: t.CPtr = (int | t.CPtr)(out_count)
|
out_count_ptr: int* = (int*)(out_count)
|
||||||
if out_count_ptr is not None:
|
if out_count_ptr is not None: out_count_ptr[0] = out_idx
|
||||||
out_count_ptr[0] = out_idx
|
|
||||||
|
|
||||||
# 释放扁平数组
|
# Free the flat arrays
|
||||||
mb.free(file_sha1s_buf)
|
mb.free(file_sha1s_buf)
|
||||||
mb.free(file_deps_buf)
|
mb.free(file_deps_buf)
|
||||||
mb.free(file_dep_counts)
|
mb.free(file_dep_counts)
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
import memhub
|
||||||
|
|
||||||
# 全局 mbuddy 指针
|
# Global mbuddy pointer
|
||||||
_mbuddy: memhub.MemBuddy*
|
_mbuddy: memhub.MemBuddy*
|
||||||
|
|
||||||
# 源代码缓冲区大小(1MB)
|
# Source buffer size (1MB)
|
||||||
SRC_BUF_SIZE: t.CDefine = 1048576
|
define SRC_BUF_SIZE = 1048576
|
||||||
|
|
||||||
# vpi 缓冲区大小(256KB)
|
# vpi buffer size (256KB).
|
||||||
PYI_BUF_SIZE: sizet = 262144
|
# Compile-time constant via define: a module-level global would emit a strong symbol per module, causing llvm-link errors.
|
||||||
|
define PYI_BUF_SIZE = 262144
|
||||||
|
|
||||||
# 拓扑排序相关常量
|
# Topological sort constants
|
||||||
MAX_CLASSES: t.CDefine = 512
|
define MAX_CLASSES = 512
|
||||||
MAX_FILE_DEPS: t.CDefine = 256
|
define MAX_FILE_DEPS = 256
|
||||||
MAX_DEP_EDGES: t.CDefine = 16
|
define MAX_DEP_EDGES = 16
|
||||||
SHA1_LEN: t.CDefine = 17
|
define SHA1_LEN = 17
|
||||||
CLASS_NAME_LEN: t.CDefine = 64
|
define CLASS_NAME_LEN = 64
|
||||||
+1530
-1723
File diff suppressed because it is too large.
Load diff
+609
-728
File diff suppressed because it is too large.
Load diff
+73
-140
@@ -1,57 +1,38 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import viperlib
|
import viperlib
|
||||||
import hashlib
|
import hashlib
|
||||||
import w32.win32file
|
import win32.win32file
|
||||||
import w32.win32base
|
import win32.win32base
|
||||||
import w32.fileio as fileio
|
import win32.fileio as fileio
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Utils - project utility functions (SHA1 hashing, directory cleanup)
|
||||||
# Utils - 工程工具函数(SHA1 计算、目录清理等)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# 全局 mbuddy 指针
|
# Global mbuddy pointer
|
||||||
_mbuddy: memhub.MemBuddy*
|
_mbuddy: memhub.MemBuddy*
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# nibble_to_hex_char - convert a nibble to a hex character
|
||||||
# nibble_to_hex_char - 半字节转十六进制字符
|
|
||||||
# ============================================================
|
|
||||||
def nibble_to_hex_char(n: int, uppercase: int) -> char:
|
def nibble_to_hex_char(n: int, uppercase: int) -> char:
|
||||||
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
|
"""Convert a 0-15 nibble to a hex character (uppercase=1 uses 'A'-'F')."""
|
||||||
if n < 10: return '0' + n
|
if n < 10: return char('0' + n)
|
||||||
if uppercase != 0: return 'A' + (n - 10)
|
if uppercase != 0: return char('A' + (n - 10))
|
||||||
return 'a' + (n - 10)
|
return char('a' + (n - 10))
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# bytes_to_hex - convert a byte sequence to a hex string
|
||||||
# bytes_to_hex - 字节序列转十六进制字符串
|
def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase: int, use_malloc: int) -> str:
|
||||||
# ============================================================
|
"""Convert the first nbytes of digest to a null-terminated hex string."""
|
||||||
def bytes_to_hex(pool: memhub.MemBuddy*,
|
if digest is None or nbytes <= 0: return None
|
||||||
digest: bytes,
|
|
||||||
nbytes: sizet,
|
|
||||||
uppercase: int,
|
|
||||||
use_malloc: int) -> str:
|
|
||||||
"""将 digest 前 nbytes 字节转为 2*nbytes 个十六进制字符(含 null 终止)
|
|
||||||
|
|
||||||
use_malloc=1 时用 stdlib.malloc(调用方负责 free),否则 pool.alloc。
|
|
||||||
合并自 Utils.compute_sha1 / IncludesScanner.compute_file_sha1 的重复 hex 转换。
|
|
||||||
"""
|
|
||||||
if digest is None or nbytes <= 0:
|
|
||||||
return None
|
|
||||||
hex_len: sizet = nbytes * 2 + 1
|
hex_len: sizet = nbytes * 2 + 1
|
||||||
hex_buf: str = None
|
hex_buf: str = None
|
||||||
if use_malloc != 0:
|
if use_malloc != 0: hex_buf = stdlib.malloc(hex_len)
|
||||||
hex_buf = stdlib.malloc(hex_len)
|
else: hex_buf = pool.alloc(hex_len)
|
||||||
else:
|
if hex_buf is None: return None
|
||||||
hex_buf = pool.alloc(hex_len)
|
for i in range(int(nbytes)):
|
||||||
if hex_buf is None:
|
|
||||||
return None
|
|
||||||
for i in range(nbytes):
|
|
||||||
hi: int = (digest[i] >> 4) & 0xF
|
hi: int = (digest[i] >> 4) & 0xF
|
||||||
lo: int = digest[i] & 0xF
|
lo: int = digest[i] & 0xF
|
||||||
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
|
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
|
||||||
@@ -60,52 +41,38 @@ def bytes_to_hex(pool: memhub.MemBuddy*,
|
|||||||
return hex_buf
|
return hex_buf
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# compute_sha1 - compute a string SHA1, return the first 16 hex chars
|
||||||
# compute_sha1 - 计算字符串的 SHA1,返回前 16 个十六进制字符(与 Projectrans.py 一致)
|
|
||||||
# ============================================================
|
|
||||||
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
|
def compute_sha1(pool: memhub.MemBuddy*, content: str) -> str:
|
||||||
"""计算字符串的 SHA1,返回前 16 个十六进制字符(用于文件命名)"""
|
"""Compute the SHA1 of a string, returning its first 16 hex chars."""
|
||||||
if content is None or pool is None:
|
if content is None or pool is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
|
# ctx is stack-allocated (alloca); no heap allocation needed
|
||||||
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
|
ctx: hashlib.sha1* = hashlib.sha1()
|
||||||
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
|
|
||||||
if ctx is None: return None
|
if ctx is None: return None
|
||||||
|
|
||||||
# 计算摘要
|
# Compute the digest
|
||||||
ctx.update(content)
|
ctx.update(content)
|
||||||
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
|
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
|
||||||
if digest is None: return None
|
if digest is None: return None
|
||||||
ctx.final(digest)
|
ctx.final(digest)
|
||||||
|
|
||||||
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
|
# Convert to a hex string (first 8 bytes = 16 hex chars)
|
||||||
return bytes_to_hex(pool, digest, 8, 0, 0)
|
return bytes_to_hex(pool, digest, 8, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# is_vp_at - check whether a path segment ends with .vp
|
||||||
# is_vp_at - 检查路径段是否以 .vp 后缀结尾
|
|
||||||
# ============================================================
|
|
||||||
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
|
def is_vp_at(p: str, start: sizet, plen: sizet) -> int:
|
||||||
"""检查 p[start, start+plen) 是否以 .vp 后缀结尾(1=是,0=否)。
|
"""Return 1 if p[start, start+plen) ends with the .vp suffix."""
|
||||||
|
if p is None or plen <= 3: return 0
|
||||||
不再识别 .py(移除 vp -> py 后缀识别回退:Viper 源文件仅 .vp)。
|
|
||||||
"""
|
|
||||||
if p is None or plen <= 3:
|
|
||||||
return 0
|
|
||||||
base: sizet = start + plen - 3
|
base: sizet = start + plen - 3
|
||||||
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p':
|
if p[base] == '.' and p[base + 1] == 'v' and p[base + 2] == 'p': return 1
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# JoinPath - join two path segments as a/b
|
||||||
# JoinPath - 拼接两个路径段为 a/b
|
|
||||||
# ============================================================
|
|
||||||
def JoinPath(a: str, b: str) -> str:
|
def JoinPath(a: str, b: str) -> str:
|
||||||
"""拼接路径段为 "a/b"(stdlib.malloc 分配,调用方负责 free),失败返回 None"""
|
"""Join two path segments as "a/b"; caller frees the malloc'd result."""
|
||||||
if a is None or b is None:
|
if a is None or b is None: return None
|
||||||
return None
|
|
||||||
a_len: sizet = string.strlen(a)
|
a_len: sizet = string.strlen(a)
|
||||||
b_len: sizet = string.strlen(b)
|
b_len: sizet = string.strlen(b)
|
||||||
total_len: sizet = a_len + b_len + 2
|
total_len: sizet = a_len + b_len + 2
|
||||||
@@ -115,17 +82,11 @@ def JoinPath(a: str, b: str) -> str:
|
|||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# read_file_to_buf - read a whole file into a null-terminated buffer
|
||||||
# read_file_to_buf - 读取整个文件到缓冲区(null 终止)
|
|
||||||
# ============================================================
|
|
||||||
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
||||||
"""读取文件到 stdlib.malloc 缓冲区并保证 null 终止
|
"""Read a whole file into a null-terminated malloc buffer, or None."""
|
||||||
|
|
||||||
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
|
|
||||||
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
|
|
||||||
"""
|
|
||||||
if path is None or buf_size <= 0: return None
|
if path is None or buf_size <= 0: return None
|
||||||
sf: rawptr[fileio.File] = fileio.File(path, fileio.MODE.R)
|
sf: fileio.File* = fileio.File(path, fileio.MODE.R)
|
||||||
if sf.closed: return None
|
if sf.closed: return None
|
||||||
buf: bytes = stdlib.malloc(buf_size)
|
buf: bytes = stdlib.malloc(buf_size)
|
||||||
if buf is None:
|
if buf is None:
|
||||||
@@ -136,25 +97,16 @@ def read_file_to_buf(path: str, buf_size: sizet) -> bytes:
|
|||||||
if br <= 0:
|
if br <= 0:
|
||||||
stdlib.free(buf)
|
stdlib.free(buf)
|
||||||
return None
|
return None
|
||||||
if br < buf_size:
|
if br < buf_size: buf[br] = 0
|
||||||
buf[br] = 0
|
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# PathToModuleName - convert a file path to a module name
|
||||||
# PathToModuleName - 文件路径转模块名
|
|
||||||
# ============================================================
|
|
||||||
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||||
"""将文件路径转换为模块名(stdlib.malloc 分配,调用方负责 free)
|
"""Convert a file path to a dotted module name; caller frees the result."""
|
||||||
|
if path is None: return None
|
||||||
strip_includes_prefix=1 时跳过 includes/ 前缀(如 includes/ast/__init__.vp → ast)。
|
|
||||||
特殊处理 __init__.vp:pkg/__init__.vp → pkg(而非 pkg.__init__)。
|
|
||||||
合并自 StubMerger._PathToModuleName / IncludesScanner 内联模块名计算。
|
|
||||||
"""
|
|
||||||
if path is None:
|
|
||||||
return None
|
|
||||||
plen: sizet = string.strlen(path)
|
plen: sizet = string.strlen(path)
|
||||||
# 跳过 includes/ 或 includes\ 前缀(Windows 反斜杠兼容)
|
# Skip the includes/ or includes\ prefix
|
||||||
prefix_len: sizet = 9
|
prefix_len: sizet = 9
|
||||||
path_start: sizet = 0
|
path_start: sizet = 0
|
||||||
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
if strip_includes_prefix != 0 and plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
|
||||||
@@ -162,59 +114,45 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
|||||||
if psep_ch == '/' or psep_ch == '\\':
|
if psep_ch == '/' or psep_ch == '\\':
|
||||||
path_start = prefix_len
|
path_start = prefix_len
|
||||||
plen = plen - prefix_len
|
plen = plen - prefix_len
|
||||||
# 去掉 .vp 后缀(Viper 源文件仅 .vp,不再识别 .py)
|
# Strip the .vp suffix
|
||||||
if is_vp_at(path, path_start, plen) != 0:
|
if is_vp_at(path, path_start, plen) != 0: plen -= 3
|
||||||
plen -= 3
|
if plen == 0: return None
|
||||||
if plen == 0:
|
# Check for a trailing /__init__ or \__init__ (package __init__.vp)
|
||||||
return None
|
|
||||||
# 检查是否以 /__init__ 或 \__init__ 结尾(包的 __init__.vp)
|
|
||||||
# 同时支持 / 和 \ 两种分隔符(TPC 写入的路径可能混合使用)
|
|
||||||
if plen > 9:
|
if plen > 9:
|
||||||
tail_off: sizet = path_start + plen - 9
|
tail_off: sizet = path_start + plen - 9
|
||||||
sep_char: char = path[tail_off]
|
sep_char: char = path[tail_off]
|
||||||
if (sep_char == '/' or sep_char == '\\') \
|
if (sep_char == '/' or sep_char == '\\') and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
|
||||||
and path[tail_off + 1] == '_' and path[tail_off + 2] == '_' \
|
# Drop the separator + __init__ suffix
|
||||||
and path[tail_off + 3] == 'i' and path[tail_off + 4] == 'n' and path[tail_off + 5] == 'i' \
|
|
||||||
and path[tail_off + 6] == 't' and path[tail_off + 7] == '_' and path[tail_off + 8] == '_':
|
|
||||||
# 去掉 分隔符+__init__ 后缀,plen 就是包名部分长度
|
|
||||||
plen = tail_off - path_start
|
plen = tail_off - path_start
|
||||||
if plen == 0:
|
if plen == 0: return None
|
||||||
return None
|
# Copy and replace / with .
|
||||||
# 复制并替换 / 为 .
|
|
||||||
result: str = stdlib.malloc(plen + 1)
|
result: str = stdlib.malloc(plen + 1)
|
||||||
if result is None:
|
if result is None: return None
|
||||||
return None
|
for ri in range(int(plen)):
|
||||||
for ri in range(plen):
|
|
||||||
ch: char = path[path_start + ri]
|
ch: char = path[path_start + ri]
|
||||||
if ch == '/' or ch == '\\':
|
if ch == '/' or ch == '\\': result[ri] = '.'
|
||||||
result[ri] = '.'
|
else: result[ri] = ch
|
||||||
else:
|
|
||||||
result[ri] = ch
|
|
||||||
result[plen] = '\0'
|
result[plen] = '\0'
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CleanDir - recursively delete all files/subdirs under dir_path (for --clean)
|
||||||
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身;用于 --clean)
|
|
||||||
# ============================================================
|
|
||||||
def CleanDir(dir_path: str) -> int:
|
def CleanDir(dir_path: str) -> int:
|
||||||
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
|
"""Recursively delete files/subdirs under dir_path, returning the count."""
|
||||||
if dir_path is None:
|
if dir_path is None: return -1
|
||||||
return -1
|
|
||||||
dir_len: sizet = string.strlen(dir_path)
|
dir_len: sizet = string.strlen(dir_path)
|
||||||
pattern: bytes = stdlib.malloc(dir_len + 8)
|
pattern: bytes = stdlib.malloc(dir_len + 8)
|
||||||
if pattern is None:
|
if pattern is None: return -1
|
||||||
return -1
|
|
||||||
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
|
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
|
||||||
|
|
||||||
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||||
if find_data is None:
|
if find_data is None:
|
||||||
stdlib.free(pattern)
|
stdlib.free(pattern)
|
||||||
return -1
|
return -1
|
||||||
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
string.memset(find_data, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||||
|
|
||||||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
|
handle: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(pattern, find_data)
|
||||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
if handle == win32.win32base.INVALID_HANDLE_VALUE:
|
||||||
stdlib.free(pattern)
|
stdlib.free(pattern)
|
||||||
stdlib.free(find_data)
|
stdlib.free(find_data)
|
||||||
return 0
|
return 0
|
||||||
@@ -223,34 +161,29 @@ def CleanDir(dir_path: str) -> int:
|
|||||||
while True:
|
while True:
|
||||||
fname: str = find_data.cFileName
|
fname: str = find_data.cFileName
|
||||||
if fname is not None:
|
if fname is not None:
|
||||||
# 跳过 . 和 ..
|
# Skip . and ..
|
||||||
if fname[0] == '.':
|
if fname[0] == '.':
|
||||||
if fname[1] == '\0':
|
if fname[1] == '\0': fname = None
|
||||||
fname = None
|
elif fname[1] == '.' and fname[2] == '\0': fname = None
|
||||||
elif fname[1] == '.' and fname[2] == '\0':
|
|
||||||
fname = None
|
|
||||||
if fname is not None:
|
if fname is not None:
|
||||||
full_path: bytes = JoinPath(dir_path, fname)
|
full_path: bytes = JoinPath(dir_path, fname)
|
||||||
if full_path is not None:
|
if full_path is not None:
|
||||||
# 检查是否为目录(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
# Check whether this is a directory
|
||||||
is_dir: uint32 = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
is_dir: uint32 = find_data.dwFileAttributes & win32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||||
if is_dir != 0:
|
if is_dir != 0:
|
||||||
# 递归清理子目录中的文件
|
# Recurse into the subdirectory
|
||||||
sub_deleted: int = CleanDir(full_path)
|
sub_deleted: int = CleanDir(full_path)
|
||||||
if sub_deleted > 0:
|
if sub_deleted > 0: deleted += sub_deleted
|
||||||
deleted += sub_deleted
|
# Remove the now-empty subdirectory
|
||||||
# 删除空子目录(RemoveDirectoryA 要求目录为空)
|
win32.win32file.RemoveDirectoryA(full_path)
|
||||||
w32.win32file.RemoveDirectoryA(full_path)
|
|
||||||
else:
|
else:
|
||||||
# 删除文件
|
# Delete the file
|
||||||
if w32.win32file.DeleteFileA(full_path) != 0:
|
if win32.win32file.DeleteFileA(full_path) != 0: deleted += 1
|
||||||
deleted += 1
|
|
||||||
stdlib.free(full_path)
|
stdlib.free(full_path)
|
||||||
|
|
||||||
if w32.win32file.FindNextFileA(handle, find_data) == 0:
|
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||||
break
|
|
||||||
|
|
||||||
w32.win32file.FindClose(handle)
|
win32.win32file.FindClose(handle)
|
||||||
stdlib.free(pattern)
|
stdlib.free(pattern)
|
||||||
stdlib.free(find_data)
|
stdlib.free(find_data)
|
||||||
return deleted
|
return deleted
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
|
|
||||||
from .Config import Load_project_config, resolve_paths
|
from .Config import Load_project_config, resolve_paths
|
||||||
from .Utils import compute_sha1, CleanDir
|
from .Utils import compute_sha1, CleanDir
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ['Load_project_config', 'resolve_paths', 'compute_sha1', 'CleanDir',]
|
||||||
'Load_project_config',
|
|
||||||
'resolve_paths',
|
|
||||||
'compute_sha1',
|
|
||||||
'CleanDir',
|
|
||||||
]
|
|
||||||
+1022
-1169
File diff suppressed because it is too large.
Load diff
+4018
-4680
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,254 +1,124 @@
|
|||||||
import t, c
|
from stdint import *
|
||||||
from stdint import *
|
import memhub
|
||||||
import memhub
|
import string
|
||||||
import string
|
import stdlib
|
||||||
import stdlib
|
import mlirlite
|
||||||
import llvmlite
|
import win32.win32file as win32file
|
||||||
import w32.win32file as win32file
|
import win32.win32base as win32base
|
||||||
import w32.win32base as win32base
|
import win32.fileio as fileio
|
||||||
import w32.fileio as fileio
|
import viperlib
|
||||||
import viperlib
|
import sys
|
||||||
import sys
|
import ast
|
||||||
import ast
|
import lib.core.VLogger as VLogger
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.IncludesScanner as IncludesScanner
|
||||||
import lib.core.IncludesScanner as IncludesScanner
|
import lib.core.Gen.Translator as HandlesTranslator
|
||||||
import lib.core.Gen.Translator as HandlesTranslator
|
import lib.Phase.BuildPipeline as BuildPipeline
|
||||||
import lib.Phase.BuildPipeline as BuildPipeline
|
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
import lib.Projectrans.Utils as Utils
|
||||||
import lib.Projectrans.Utils as Utils
|
import lib.Projectrans.Config as Config
|
||||||
import lib.Projectrans.Config as Config
|
|
||||||
|
|
||||||
|
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
import lib.Stub.StubMerger as StubMerger
|
||||||
import lib.Stub.StubMerger as StubMerger
|
|
||||||
|
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
|
||||||
# ============================================================
|
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||||
# StubMergerCollect - 切片路径构建与 stub 文件 SHA1 收集
|
|
||||||
#
|
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
|
||||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
|
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
|
||||||
# ============================================================
|
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||||
|
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
|
||||||
# ============================================================
|
if temp_dir is None or sha1 is None or ext is None: return None
|
||||||
# _sliced_path - 构建切片路径(stdlib.malloc 分配,调用者负责 free;level>0 时自动调 BuildPipeline.ensure_dir 建子目录)
|
sha1_len: sizet = string.strlen(sha1)
|
||||||
#
|
ext_len: sizet = string.strlen(ext)
|
||||||
# 按 Config.Sha1SliceLevel 分散到 SHA1 前缀子目录:level=0 → {temp_dir}/{sha1}.{ext};level=1 → {temp_dir}/b7/{sha1}.{ext};level=2 → {temp_dir}/b7/90/{sha1}.{ext}
|
|
||||||
#
|
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
|
||||||
# Args: temp_dir 基础目录;td_len temp_dir 长度(避免重复 strlen);sha1 SHA1 字符串;ext 文件扩展名(如 "stub.ll")
|
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||||
# Returns: 完整路径(stdlib.malloc 分配,需 stdlib.free),None 失败
|
sub_len: sizet = 0
|
||||||
# ============================================================
|
if subdir_buf is not None:
|
||||||
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
sub_len = string.strlen(subdir_buf)
|
||||||
"""构建切片路径(stdlib.malloc 分配,调用者负责 free)
|
|
||||||
|
# Ensure subdir {temp_dir}/{subdir} exists.
|
||||||
level>0 时自动调用 ensure_dir 创建子目录。
|
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
|
||||||
"""
|
dir_path_len: sizet = td_len + sub_len + 2
|
||||||
if temp_dir is None or sha1 is None or ext is None:
|
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||||
return None
|
if dir_path is not None:
|
||||||
sha1_len: sizet = string.strlen(sha1)
|
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||||||
ext_len: sizet = string.strlen(ext)
|
BuildPipeline.ensure_dir(dir_path)
|
||||||
|
stdlib.free(dir_path)
|
||||||
# 子目录前缀:复用 Config.slice_subdir(SHA1 切片唯一权威实现,mbuddy 分配无需 free)
|
|
||||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
# Build the full path
|
||||||
sub_len: sizet = 0
|
path: str = None
|
||||||
if subdir_buf is not None:
|
path_len: sizet = 0
|
||||||
sub_len = string.strlen(subdir_buf)
|
if sub_len > 0:
|
||||||
|
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||||||
# 确保子目录存在: {temp_dir}/{subdir}
|
path = stdlib.malloc(path_len)
|
||||||
# 不创建目录会导致 fileio.File(MODE.W) 静默失败(file.closed==True),
|
if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||||||
# 进而 BuildCombinedIR 读不到文件返回 0("BuildCombinedIR 失败")。
|
else:
|
||||||
dir_path_len: sizet = td_len + sub_len + 2
|
path_len = td_len + sha1_len + ext_len + 3
|
||||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
path = stdlib.malloc(path_len)
|
||||||
if dir_path is not None:
|
if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||||||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
|
||||||
BuildPipeline.ensure_dir(dir_path)
|
return path
|
||||||
stdlib.free(dir_path)
|
|
||||||
|
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
|
||||||
# 构建完整路径
|
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking ViperC's own lib/* stubs.
|
||||||
path: str = None
|
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||||
path_len: sizet = 0
|
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||||
if sub_len > 0:
|
if sha1_set is None: return -1
|
||||||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
if StubMerger._sha1_store_arr is None: return -1
|
||||||
path = stdlib.malloc(path_len)
|
count: int = 0
|
||||||
if path is not None:
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
if count >= StubMerger.MAX_INCLUDES_SHA1: break
|
||||||
else:
|
idx: sizet = sizet(i) * 17
|
||||||
path_len = td_len + sha1_len + ext_len + 3
|
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||||
path = stdlib.malloc(path_len)
|
count += 1
|
||||||
if path is not None:
|
return count
|
||||||
viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||||
|
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||||
return path
|
"""Whether sha1 is present in the set."""
|
||||||
# ============================================================
|
if sha1 is None or sha1_set is None: return 0
|
||||||
# _collect_stub_sha1s - 递归扫描子目录,收集 .stub.ll 文件的 SHA1(从文件名提取)
|
for i in range(int(set_count)):
|
||||||
#
|
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||||
# level=0 → 扫描 {base_dir}/*.stub.ll;level>0 → 扫描 {base_dir}/* 的子目录并对每个子目录递归调用。
|
return 0
|
||||||
#
|
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||||
# Args: base_dir 当前扫描目录;bd_len base_dir 长度;level 剩余递归层数;out_arr 输出数组(每个 SHA1 17 字节 = 16字符 + null);out_count 当前已收集数量;max_count 最大数量
|
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||||
# Returns: 收集后的总数量
|
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
|
||||||
# ============================================================
|
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||||
def _collect_stub_sha1s(base_dir: str, bd_len: sizet, level: int,
|
if temp_dir is None or scan_result is None: return 1
|
||||||
out_arr: str, out_count: int, max_count: int) -> int:
|
|
||||||
"""递归扫描子目录,收集 .stub.ll 文件的 SHA1"""
|
# Build the path temp_dir/_sha1_map.txt
|
||||||
if base_dir is None or out_arr is None:
|
dir_len: sizet = string.strlen(temp_dir)
|
||||||
return out_count
|
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||||
if out_count >= max_count:
|
if map_path is None: return 1
|
||||||
return out_count
|
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||||
|
|
||||||
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__())
|
# Open the file for writing (CREATE_ALWAYS)
|
||||||
if find_data is None:
|
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
|
||||||
return out_count
|
if f.closed: return 1
|
||||||
string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__())
|
|
||||||
|
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||||
if level <= 0:
|
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||||
# 扫描 {base_dir}/*.stub.ll
|
line_buf: bytes = stdlib.malloc(512)
|
||||||
pattern: bytes = stdlib.malloc(bd_len + 16)
|
if line_buf is None:
|
||||||
if pattern is None:
|
f.close()
|
||||||
stdlib.free(find_data)
|
return 1
|
||||||
return out_count
|
|
||||||
viperlib.snprintf(pattern, bd_len + 16, "%s/*.stub.ll", base_dir)
|
written_count: int = 0
|
||||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
for i in range(int(scan_result.Count)):
|
||||||
stdlib.free(pattern)
|
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
||||||
if handle != win32base.INVALID_HANDLE_VALUE:
|
ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w)
|
||||||
while True:
|
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
|
||||||
if out_count >= max_count:
|
# When a filter set is provided, write only its entries
|
||||||
break
|
if filter_set is not None and filter_count > 0:
|
||||||
fname: str = find_data.cFileName
|
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
|
||||||
if fname is not None:
|
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||||
if fname[0] != '\0':
|
line_len_w: sizet = string.strlen(line_buf)
|
||||||
# 从文件名提取 SHA1(前 16 字符)
|
f.write(line_buf, line_len_w)
|
||||||
string.strncpy(out_arr + out_count * 17, fname, 16)
|
written_count += 1
|
||||||
out_arr[out_count * 17 + 16] = '\0'
|
|
||||||
out_count += 1
|
f.close()
|
||||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
return 0
|
||||||
break
|
|
||||||
win32file.FindClose(handle)
|
|
||||||
else:
|
|
||||||
# 扫描 {base_dir}/* 的子目录
|
|
||||||
pattern = stdlib.malloc(bd_len + 4)
|
|
||||||
if pattern is None:
|
|
||||||
stdlib.free(find_data)
|
|
||||||
return out_count
|
|
||||||
viperlib.snprintf(pattern, bd_len + 4, "%s/*", base_dir)
|
|
||||||
handle = win32file.FindFirstFileA(pattern, find_data)
|
|
||||||
stdlib.free(pattern)
|
|
||||||
if handle != win32base.INVALID_HANDLE_VALUE:
|
|
||||||
while True:
|
|
||||||
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
|
|
||||||
attrs: uint32 = find_data.dwFileAttributes
|
|
||||||
if (attrs & 16) != 0:
|
|
||||||
fname = find_data.cFileName
|
|
||||||
if fname is not None:
|
|
||||||
# 跳过 "." 和 ".."
|
|
||||||
is_dot: int = 0
|
|
||||||
if fname[0] == '.':
|
|
||||||
if fname[1] == '\0':
|
|
||||||
is_dot = 1
|
|
||||||
elif fname[1] == '.' and fname[2] == '\0':
|
|
||||||
is_dot = 1
|
|
||||||
if is_dot == 0:
|
|
||||||
# 构建子目录路径并递归扫描
|
|
||||||
sub_dir: str = Utils.JoinPath(base_dir, fname)
|
|
||||||
if sub_dir is not None:
|
|
||||||
sub_dir_len: sizet = string.strlen(sub_dir)
|
|
||||||
out_count = _collect_stub_sha1s(sub_dir, sub_dir_len, level - 1, out_arr, out_count, max_count)
|
|
||||||
stdlib.free(sub_dir)
|
|
||||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
|
||||||
break
|
|
||||||
win32file.FindClose(handle)
|
|
||||||
|
|
||||||
stdlib.free(find_data)
|
|
||||||
return out_count
|
|
||||||
# ============================================================
|
|
||||||
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
|
|
||||||
#
|
|
||||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
|
||||||
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。
|
|
||||||
# ============================================================
|
|
||||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*,
|
|
||||||
temp_dir: str,
|
|
||||||
sha1_set: str) -> int:
|
|
||||||
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
|
|
||||||
|
|
||||||
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
|
|
||||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
|
||||||
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17(每个 SHA1 16字符+null)
|
|
||||||
返回找到的 includes SHA1 数量,-1 表示错误"""
|
|
||||||
if sha1_set is None:
|
|
||||||
return -1
|
|
||||||
if StubMerger._sha1_store_arr is None:
|
|
||||||
return -1
|
|
||||||
count: int = 0
|
|
||||||
for i in range(StubMerger._sha1_store_count):
|
|
||||||
if count >= StubMerger.MAX_INCLUDES_SHA1:
|
|
||||||
break
|
|
||||||
idx: sizet = sizet(i) * 17
|
|
||||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
# ============================================================
|
|
||||||
# _is_in_sha1_set - 检查 SHA1 是否在集合中
|
|
||||||
# ============================================================
|
|
||||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
|
||||||
"""检查 sha1 是否在集合中"""
|
|
||||||
if sha1 is None or sha1_set is None:
|
|
||||||
return 0
|
|
||||||
for i in range(set_count):
|
|
||||||
if string.strcmp(sha1_set + i * 17, sha1) == 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(仅人类可读的调试输出,程序内部不读取,
|
|
||||||
# 机器分析用 PopulateSha1MapStore 填充的全局内存存储器)
|
|
||||||
#
|
|
||||||
# Args: mb 内存池;temp_dir 临时目录(_sha1_map.txt 写入位置);scan_result IncludesScanner 扫描结果
|
|
||||||
# Returns: 0 成功,非 0 失败
|
|
||||||
# ============================================================
|
|
||||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str,
|
|
||||||
scan_result: rawptr[IncludesScanner.ScanResult],
|
|
||||||
filter_set: str,
|
|
||||||
filter_count: int) -> int:
|
|
||||||
"""将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器)
|
|
||||||
|
|
||||||
若 filter_set 不为 None 且 filter_count > 0,只写入 filter_set 中的条目。
|
|
||||||
"""
|
|
||||||
if temp_dir is None or scan_result is None:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# 构造路径 temp_dir/_sha1_map.txt
|
|
||||||
dir_len: sizet = string.strlen(temp_dir)
|
|
||||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
|
||||||
if map_path is None:
|
|
||||||
return 1
|
|
||||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
|
||||||
|
|
||||||
# 打开文件写入(CREATE_ALWAYS)
|
|
||||||
f: rawptr[fileio.File] = fileio.File(map_path, fileio.MODE.W)
|
|
||||||
if f.closed:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# 写入每个 include 条目: {sha1}:includes/{rel_path}\n
|
|
||||||
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
|
||||||
line_buf: bytes = stdlib.malloc(512)
|
|
||||||
if line_buf is None:
|
|
||||||
f.close()
|
|
||||||
return 1
|
|
||||||
|
|
||||||
written_count: int = 0
|
|
||||||
for i in range(scan_result.Count):
|
|
||||||
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
|
||||||
ent_w: rawptr[IncludesScanner.FileEntry] = (rawptr[IncludesScanner.FileEntry])(ea_w)
|
|
||||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None:
|
|
||||||
continue
|
|
||||||
# 若提供过滤集合,只写入集合中的条目
|
|
||||||
if filter_set is not None and filter_count > 0:
|
|
||||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0:
|
|
||||||
continue
|
|
||||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
|
||||||
line_len_w: sizet = string.strlen(line_buf)
|
|
||||||
f.write(line_buf, line_len_w)
|
|
||||||
written_count += 1
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
return 0
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
import lib.core.Gen.GenApi as GenApi
|
import lib.core.Gen.GenApi as GenApi
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import w32.win32file as win32file
|
import win32.win32file as win32file
|
||||||
import w32.win32base as win32base
|
import win32.win32base as win32base
|
||||||
import w32.fileio as fileio
|
import win32.fileio as fileio
|
||||||
import viperlib
|
import viperlib
|
||||||
import sys
|
import sys
|
||||||
import ast
|
import ast
|
||||||
@@ -26,329 +25,194 @@ import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
|||||||
import lib.Stub.StubMerger as StubMerger
|
import lib.Stub.StubMerger as StubMerger
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# StubMergerIR - module structured sidecar ({sha1}.meta) write/read, submodule checks and module-name/SHA1 mapping helpers
|
||||||
# StubMergerIR - IR 文本去重/注释与模块名-SHA1 映射工具
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# WriteModuleMeta - write {sha1}.meta (structured sidecar: funcs + struct layout + vtables).
|
||||||
# IsDeclOnlyModule - 判断模块是否为纯声明文件(读 {temp_dir}/{sha1}.text.ll 扫描是否含 "define " 行)
|
# Lines: FUNC <mangled> DEF|DECL, STRUCT <sha1>.<name> FIELDS <n> PACKED <0|1>|OPAQUE, VTABLE <sha1>.<name> SLOTS <n>.
|
||||||
#
|
def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||||
# 返回 1 = 纯声明(无函数体,如全 pass 的模块,无需 .obj);0 = 实现文件(含 define 函数体);-1 = text.ll 不存在/无法判断。
|
"""Write the module's func defines, struct layouts and vtables to {sha1}.meta."""
|
||||||
# 与 Phase B+ 编译缺失 includes 时的内联判断逻辑一致,供 Phase C 链接时区分「声明文件无需 .obj」与「实现文件缺失」。
|
if mod is None or temp_dir is None or sha1 is None:
|
||||||
# ============================================================
|
VLogger.error("meta write: null argument", "Meta")
|
||||||
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
|
||||||
"""判断模块是否为纯声明文件(text.ll 无 define 行)"""
|
|
||||||
if temp_dir is None or sha1 is None:
|
|
||||||
return -1
|
|
||||||
tpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "text.ll")
|
|
||||||
if tpath is None:
|
|
||||||
return -1
|
|
||||||
tf_d: fileio.File = fileio.File(tpath, fileio.MODE.R)
|
|
||||||
stdlib.free(tpath)
|
|
||||||
if tf_d.closed:
|
|
||||||
return -1
|
|
||||||
tbuf_d: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
|
||||||
tbr_d: int64 = 0
|
|
||||||
if tbuf_d is not None:
|
|
||||||
tbr_d = tf_d.read_all(tbuf_d, StubMerger.STUB_READ_BUF_SIZE)
|
|
||||||
tf_d.close()
|
|
||||||
if tbuf_d is None:
|
|
||||||
return -1
|
|
||||||
if tbr_d <= 0:
|
|
||||||
# 空 text.ll:无函数体,纯声明
|
|
||||||
stdlib.free(tbuf_d)
|
|
||||||
return 1
|
return 1
|
||||||
if tbr_d < StubMerger.STUB_READ_BUF_SIZE:
|
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||||
tbuf_d[tbr_d] = '\0'
|
if mpath is None:
|
||||||
# 逐行扫描:找 "define " 行即为实现文件
|
VLogger.error("meta write: path construction failed", "Meta")
|
||||||
tpos_d: sizet = 0
|
return 1
|
||||||
while tpos_d < tbr_d:
|
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
|
||||||
tls_d: sizet = tpos_d
|
if mf.closed:
|
||||||
while tpos_d < tbr_d:
|
VLogger.error(f"meta write: cannot open {mpath}", "Meta")
|
||||||
if tbuf_d[tpos_d] == '\n':
|
return 1
|
||||||
|
cur: mlirlite.Function* = mod.FuncHead
|
||||||
|
steps: sizet = 0
|
||||||
|
while cur is not None:
|
||||||
|
steps += 1
|
||||||
|
if steps > 100000:
|
||||||
|
VLogger.error("meta write: function list has a cycle or exceeded the limit", "Meta")
|
||||||
|
break
|
||||||
|
nm: str = mlirlite.function_get_name(cur)
|
||||||
|
if nm is not None:
|
||||||
|
nlen: sizet = string.strlen(nm)
|
||||||
|
line: bytes = stdlib.malloc(nlen + 16)
|
||||||
|
if line is None:
|
||||||
|
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||||
break
|
break
|
||||||
tpos_d += 1
|
mark: str = "DECL"
|
||||||
tll_d: sizet = tpos_d - tls_d
|
if mlirlite.function_is_declared(cur) == 0: mark = "DEF"
|
||||||
if tpos_d < tbr_d:
|
viperlib.snprintf(line, nlen + 16, "FUNC %s %s\n", nm, mark)
|
||||||
tpos_d += 1
|
mf.write(line, string.strlen(line))
|
||||||
if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0:
|
stdlib.free(line)
|
||||||
stdlib.free(tbuf_d)
|
cur = mlirlite.function_get_next(cur)
|
||||||
return 0
|
# 2) Struct layout: STRUCT <name> FIELDS <n> PACKED <0|1> (opaque -> STRUCT <name> OPAQUE)
|
||||||
stdlib.free(tbuf_d)
|
nt: mlirlite.NamedTypeNode* = mod.NamedTypeHead
|
||||||
|
nt_steps: sizet = 0
|
||||||
|
while nt is not None:
|
||||||
|
nt_steps += 1
|
||||||
|
if nt_steps > 100000:
|
||||||
|
VLogger.error("meta write: named-type list has a cycle or exceeded the limit", "Meta")
|
||||||
|
break
|
||||||
|
tn: str = mlirlite.get_struct_name(nt.Ty)
|
||||||
|
if tn is not None and tn[0] != '\0':
|
||||||
|
tl: sizet = string.strlen(tn)
|
||||||
|
lb: bytes = stdlib.malloc(tl + 48)
|
||||||
|
if lb is None:
|
||||||
|
VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||||
|
break
|
||||||
|
fc: int = mlirlite.get_struct_fcount(nt.Ty)
|
||||||
|
if fc < 0: viperlib.snprintf(lb, tl + 48, "STRUCT %s OPAQUE\n", tn)
|
||||||
|
else: viperlib.snprintf(lb, tl + 48, "STRUCT %s FIELDS %d PACKED %d\n", tn, fc, nt.IsPacked)
|
||||||
|
mf.write(lb, string.strlen(lb))
|
||||||
|
stdlib.free(lb)
|
||||||
|
nt = nt.Next
|
||||||
|
# 3) Vtable slots: VTABLE <class> SLOTS <n> (global <class>.vtable; Ty anonymous struct fcount = slots)
|
||||||
|
gv: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||||
|
gv_steps: sizet = 0
|
||||||
|
while gv is not None:
|
||||||
|
gv_steps += 1
|
||||||
|
if gv_steps > 100000:
|
||||||
|
VLogger.error("meta write: global-variable list has a cycle or exceeded the limit", "Meta")
|
||||||
|
break
|
||||||
|
gn: str = gv.Name
|
||||||
|
if gn is not None:
|
||||||
|
gnl: sizet = string.strlen(gn)
|
||||||
|
if gnl > 7 and string.strcmp(gn + gnl - 7, ".vtable") == 0:
|
||||||
|
base_len: sizet = gnl - 7
|
||||||
|
base: bytes = stdlib.malloc(base_len + 1)
|
||||||
|
if base is not None:
|
||||||
|
string.strncpy(base, gn, base_len)
|
||||||
|
base[base_len] = '\0'
|
||||||
|
ar: int = mlirlite.get_struct_fcount(gv.Ty)
|
||||||
|
if ar < 0: ar = 0
|
||||||
|
lb2: bytes = stdlib.malloc(base_len + 40)
|
||||||
|
if lb2 is None: VLogger.error("meta write: line buffer allocation failed", "Meta")
|
||||||
|
else:
|
||||||
|
viperlib.snprintf(lb2, base_len + 40, "VTABLE %s SLOTS %d\n", base, ar)
|
||||||
|
mf.write(lb2, string.strlen(lb2))
|
||||||
|
stdlib.free(lb2)
|
||||||
|
stdlib.free(base)
|
||||||
|
gv = gv.Next
|
||||||
|
mf.close()
|
||||||
|
stdlib.free(mpath)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# _MetaHasDef - read {sha1}.meta to check for a function definition ("FUNC ... DEF" line).
|
||||||
|
# Returns: 1=has DEF (impl file); 0=readable but no DEF (decl-only); -1=missing/unreadable.
|
||||||
|
def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||||
|
"""Read {sha1}.meta: 1 if it has a FUNC ... DEF line, 0 if none, -1 if unreadable."""
|
||||||
|
if temp_dir is None or sha1 is None: return -1
|
||||||
|
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||||
|
if mpath is None: return -1
|
||||||
|
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
|
||||||
|
stdlib.free(mpath)
|
||||||
|
if mf.closed: return -1
|
||||||
|
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||||
|
mbr: int64 = 0
|
||||||
|
if mbuf is not None: mbr = mf.read_all(mbuf, StubMerger.STUB_READ_BUF_SIZE)
|
||||||
|
mf.close()
|
||||||
|
if mbuf is None: return -1
|
||||||
|
if mbr <= 0:
|
||||||
|
# Empty meta: no DEF, decl-only
|
||||||
|
stdlib.free(mbuf)
|
||||||
|
return 0
|
||||||
|
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
|
||||||
|
# Scan line by line: a trailing " DEF" marks an impl file
|
||||||
|
mpos: sizet = 0
|
||||||
|
while mpos < mbr:
|
||||||
|
mls: sizet = mpos
|
||||||
|
while mpos < mbr:
|
||||||
|
if mbuf[mpos] == '\n': break
|
||||||
|
mpos += 1
|
||||||
|
mll: sizet = mpos - mls
|
||||||
|
if mpos < mbr: mpos += 1
|
||||||
|
if mll > 0 and mbuf[mls + mll - 1] == '\r': mll -= 1
|
||||||
|
if mll >= 10 and string.strncmp(mbuf + mls, "FUNC ", 5) == 0:
|
||||||
|
if mbuf[mls + mll - 4] == ' ' and string.strncmp(mbuf + mls + mll - 3, "DEF", 3) == 0:
|
||||||
|
stdlib.free(mbuf)
|
||||||
|
return 1
|
||||||
|
stdlib.free(mbuf)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# IsDeclOnlyModule - whether a module is decl-only (reads the structured definition list in {sha1}.meta).
|
||||||
|
# 1=decl-only (no FUNC ... DEF, e.g. all-pass modules, no .obj needed); 0=impl; -1=unknown/missing.
|
||||||
|
def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||||
|
"""Whether the module is decl-only (its meta has no FUNC ... DEF line)."""
|
||||||
|
h: int = _MetaHasDef(temp_dir, td_len, sha1)
|
||||||
|
if h < 0: return -1
|
||||||
|
if h == 1: return 0
|
||||||
return 1
|
return 1
|
||||||
# ============================================================
|
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
|
||||||
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
|
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
|
||||||
#
|
|
||||||
# 逐字节 strncmp 搜索 func_sig(如 @"sha1.func"(/ @printf(),命中后检查行首是否为 "declare "/"define ",跳过 call 语句
|
|
||||||
# (避免 strstr 子串匹配误命中 call ... @"func"(...)。
|
|
||||||
#
|
|
||||||
# Args: out_buf 已有 IR 文本缓冲区;func_sig 函数签名片段,如 @"sha1.func"(/ @printf(
|
|
||||||
# Returns: 1=已存在 declare/define, 0=不存在
|
|
||||||
# ============================================================
|
|
||||||
def _IsFuncDeclaredOrDefined(out_buf: bytes, func_sig: str) -> int:
|
|
||||||
"""检查 out_buf 中是否已有函数的 declare 或 define 行"""
|
|
||||||
if out_buf is None or func_sig is None:
|
|
||||||
return 0
|
|
||||||
sig_len: sizet = string.strlen(func_sig)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if sig_len == 0 or out_len == 0 or sig_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
for pos in range(0, out_len - sig_len + 1):
|
|
||||||
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
|
|
||||||
ls: sizet = pos
|
|
||||||
while ls > 0 and out_buf[ls - 1] != '\n':
|
|
||||||
ls -= 1
|
|
||||||
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
|
|
||||||
return 1
|
|
||||||
if string.strncmp(out_buf + ls, "define ", 7) == 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# _CommentOutDeclareInBuf - 将 out_buf 中匹配 func_sig 的 declare 行注释掉
|
|
||||||
#
|
|
||||||
# 用于解决 declare/define 冲突:当本地 text.ll 包含 define 而依赖
|
|
||||||
# text.ll 已生成了同函数的 declare 时,将 declare 行首字符改为 ';'
|
|
||||||
# 使其成为注释,然后追加 define 行。
|
|
||||||
# ============================================================
|
|
||||||
def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int:
|
|
||||||
"""在 out_buf 中搜索 declare 行(包含 func_sig),将其注释掉"""
|
|
||||||
if out_buf is None or func_sig is None:
|
|
||||||
return 0
|
|
||||||
sig_len: sizet = string.strlen(func_sig)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if sig_len == 0 or out_len == 0 or sig_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
while pos + sig_len <= out_len:
|
|
||||||
if string.strncmp(out_buf + pos, func_sig, sig_len) == 0:
|
|
||||||
ls: sizet = pos
|
|
||||||
while ls > 0 and out_buf[ls - 1] != '\n':
|
|
||||||
ls -= 1
|
|
||||||
if string.strncmp(out_buf + ls, "declare ", 8) == 0:
|
|
||||||
out_buf[ls] = ';'
|
|
||||||
return 1
|
|
||||||
pos += 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# _CommentOutGlobalInBuf - 将 out_buf 中匹配全局变量名的行注释掉
|
|
||||||
#
|
|
||||||
# 解决 stub/text 全局变量重复定义问题:stub 中可能是 external 声明
|
|
||||||
# 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。
|
|
||||||
# ============================================================
|
|
||||||
def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int:
|
|
||||||
"""在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';'
|
|
||||||
|
|
||||||
注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与
|
|
||||||
argparse.vp 都声明 StubMerger._mbuddy)时,本地 stub external + 依赖 stub external +
|
|
||||||
依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 →
|
|
||||||
合成 IR 重复定义 → llc "redefinition of global '@StubMerger._mbuddy'"(ArgparseTest
|
|
||||||
回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终
|
|
||||||
只保留最后一个定义。
|
|
||||||
"""
|
|
||||||
if out_buf is None or global_name is None:
|
|
||||||
return 0
|
|
||||||
name_len: sizet = string.strlen(global_name)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if name_len == 0 or out_len == 0 or name_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
commented: int = 0
|
|
||||||
while pos + name_len <= out_len:
|
|
||||||
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
|
|
||||||
# 确认是行首(前一个是换行或 pos==0)
|
|
||||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
|
||||||
out_buf[pos] = ';'
|
|
||||||
commented = 1
|
|
||||||
pos += 1
|
|
||||||
return commented
|
|
||||||
def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int:
|
|
||||||
"""检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释)
|
|
||||||
|
|
||||||
用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明,
|
|
||||||
依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方
|
|
||||||
会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。
|
|
||||||
"""
|
|
||||||
if out_buf is None or global_name is None:
|
|
||||||
return 0
|
|
||||||
name_len: sizet = string.strlen(global_name)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if name_len == 0 or out_len == 0 or name_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
while pos + name_len <= out_len:
|
|
||||||
if string.strncmp(out_buf + pos, global_name, name_len) == 0:
|
|
||||||
# 确认是行首(前一个是换行或 pos==0)
|
|
||||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
|
||||||
return 1
|
|
||||||
pos += 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉
|
|
||||||
#
|
|
||||||
# 解决跨模块类型定义冲突:本地 stub 可能包含自动生成的 opaque 声明
|
|
||||||
#(如 %"sha1.Widget" = type opaque),而依赖 stub 包含完整定义
|
|
||||||
#(如 %"sha1.Widget" = type { i8*, i32 })。去重时需要将 opaque 声明
|
|
||||||
# 注释掉,让完整定义被追加,否则 LLC 报错 "Cannot allocate unsized type"。
|
|
||||||
# ============================================================
|
|
||||||
def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
|
|
||||||
"""在 out_buf 中搜索 type_name_prefix 对应的 opaque 声明行,将行首改为 ';'
|
|
||||||
|
|
||||||
type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=' 和后续内容)
|
|
||||||
返回 1=已注释, 0=未找到
|
|
||||||
"""
|
|
||||||
if out_buf is None or type_name_prefix is None:
|
|
||||||
return 0
|
|
||||||
prefix_len: sizet = string.strlen(type_name_prefix)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
while pos + prefix_len <= out_len:
|
|
||||||
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
|
|
||||||
# 确认是行首(前一个是换行或 pos==0)
|
|
||||||
if pos == 0 or out_buf[pos - 1] == '\n':
|
|
||||||
# 检查后面是否是 " = type opaque"
|
|
||||||
p: sizet = pos + prefix_len
|
|
||||||
# 跳过空格
|
|
||||||
while p < out_len and out_buf[p] == ' ':
|
|
||||||
p += 1
|
|
||||||
if p < out_len and out_buf[p] == '=':
|
|
||||||
p += 1
|
|
||||||
while p < out_len and out_buf[p] == ' ':
|
|
||||||
p += 1
|
|
||||||
if p + 11 <= out_len:
|
|
||||||
if string.strncmp(out_buf + p, "type opaque", 11) == 0:
|
|
||||||
# 找到 opaque 声明,将行首改为 ';'
|
|
||||||
out_buf[pos] = ';'
|
|
||||||
return 1
|
|
||||||
pos += 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
|
|
||||||
#
|
|
||||||
# deps_buf 只记录直接导入的模块名(如 "ast"),但包的 __init__.vp 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer
|
|
||||||
# 子模块类型;模块名含 '.' 时检查其顶层包名是否在 deps_buf 中(deps_buf = "ast stdio string" 时 "ast.lexer" → 1,"foo.bar" → 0)。
|
|
||||||
# ============================================================
|
|
||||||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||||||
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
|
"""Whether mod_name is a submodule of a package imported in deps_buf."""
|
||||||
if deps_buf is None or mod_name is None:
|
if deps_buf is None or mod_name is None: return 0
|
||||||
return 0
|
# Find the first '.' and extract the top-level package name
|
||||||
# 查找第一个 '.',提取顶层包名
|
|
||||||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||||||
if dot_pos is None:
|
if dot_pos is None: return 0
|
||||||
return 0
|
|
||||||
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
||||||
if pkg_len == 0 or pkg_len >= 256:
|
if pkg_len == 0 or pkg_len >= 256: return 0
|
||||||
return 0
|
# Build the top-level package name (truncated before '.') by temporary in-place
|
||||||
# 构造顶层包名(截断到 '.' 之前)
|
# truncation to avoid an allocation
|
||||||
# 直接在 mod_name 上临时截断检查,避免分配
|
|
||||||
saved_ch: char = mod_name[pkg_len]
|
saved_ch: char = mod_name[pkg_len]
|
||||||
mod_name[pkg_len] = '\0'
|
mod_name[pkg_len] = '\0'
|
||||||
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
||||||
mod_name[pkg_len] = saved_ch
|
mod_name[pkg_len] = saved_ch
|
||||||
return result
|
return result
|
||||||
# ============================================================
|
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
|
||||||
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque)
|
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
|
||||||
#
|
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
|
||||||
# type_name_prefix 格式: '%"sha1.ClassName"'(不含 '=')
|
"""Copy the sha1 to module_name map from the global store into caller arrays."""
|
||||||
# 完整定义: %"name" = type { ... } 或 %"name" = type < { ... } >
|
if sha1_list is None or mod_list is None: return 0
|
||||||
# 返回 1=已有完整定义, 0=无完整定义
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||||
# ============================================================
|
|
||||||
def _HasFullTypeDefinition(out_buf: bytes, type_name_prefix: str) -> int:
|
|
||||||
"""检查 out_buf 中是否已有 type_name_prefix 的完整定义"""
|
|
||||||
if out_buf is None or type_name_prefix is None:
|
|
||||||
return 0
|
|
||||||
prefix_len: sizet = string.strlen(type_name_prefix)
|
|
||||||
out_len: sizet = string.strlen(out_buf)
|
|
||||||
if prefix_len == 0 or out_len == 0 or prefix_len > out_len:
|
|
||||||
return 0
|
|
||||||
pos: sizet = 0
|
|
||||||
while pos + prefix_len <= out_len:
|
|
||||||
if string.strncmp(out_buf + pos, type_name_prefix, prefix_len) == 0:
|
|
||||||
# 确认是行首(前一个是换行或 pos==0),且未被注释(行首不是 ';')
|
|
||||||
is_line_start: int = 0
|
|
||||||
if pos == 0:
|
|
||||||
is_line_start = 1
|
|
||||||
elif out_buf[pos - 1] == '\n':
|
|
||||||
is_line_start = 1
|
|
||||||
if is_line_start == 1:
|
|
||||||
# 检查后面是否是 " = type {" 或 " = type <"
|
|
||||||
p: sizet = pos + prefix_len
|
|
||||||
while p < out_len and out_buf[p] == ' ':
|
|
||||||
p += 1
|
|
||||||
if p < out_len and out_buf[p] == '=':
|
|
||||||
p += 1
|
|
||||||
while p < out_len and out_buf[p] == ' ':
|
|
||||||
p += 1
|
|
||||||
if p + 5 < out_len:
|
|
||||||
if string.strncmp(out_buf + p, "type ", 5) == 0:
|
|
||||||
p += 5
|
|
||||||
if p < out_len:
|
|
||||||
if out_buf[p] == '{' or out_buf[p] == '<':
|
|
||||||
return 1
|
|
||||||
pos += 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
|
|
||||||
#
|
|
||||||
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。
|
|
||||||
# 返回两个数组(sha1_list 和 mod_list),长度存储在 count 中。
|
|
||||||
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
|
|
||||||
# ============================================================
|
|
||||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet,
|
|
||||||
sha1_list: str,
|
|
||||||
mod_list: str) -> int:
|
|
||||||
"""从全局内存存储器复制 sha1→module_name 映射到调用者数组
|
|
||||||
|
|
||||||
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
|
|
||||||
数据来源是 PopulateSha1MapStore 填充的全局存储器。
|
|
||||||
"""
|
|
||||||
if sha1_list is None or mod_list is None:
|
|
||||||
return 0
|
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
|
||||||
return 0
|
|
||||||
count: int = 0
|
count: int = 0
|
||||||
for i in range(StubMerger._sha1_store_count):
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
if count >= StubMerger.MAX_INCLUDES:
|
if count >= StubMerger.MAX_INCLUDES: break
|
||||||
break
|
|
||||||
idx: sizet = sizet(i) * 17
|
idx: sizet = sizet(i) * 17
|
||||||
idx2: sizet = sizet(i) * 64
|
idx2: sizet = sizet(i) * 64
|
||||||
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
string.strcpy(sha1_list + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||||
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
string.strcpy(mod_list + sizet(count) * 64, StubMerger._sha1_store_mod + idx2)
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
# ============================================================
|
# _FindSha1ByModName - look up a SHA1 by module name (exact match) for dependency graph analysis.
|
||||||
# _FindSha1ByModName - 从模块名查找 SHA1(精确匹配;供依赖图分析「模块名 → SHA1」)
|
# sha1_arr holds 17-byte entries, mod_arr 64-byte names; returns a pointer into sha1_arr, or None.
|
||||||
#
|
def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -> str:
|
||||||
# Args: sha1_arr SHA1 数组(每个 17 字节);mod_arr 模块名数组(每个 64 字节);count 映射条目数;mod_name 要查找的模块名
|
"""Look up a SHA1 by module name, falling back to the .__init__ package form."""
|
||||||
# Returns: SHA1 字符串指针(指向 sha1_arr 内部,无需释放),未找到返回 None
|
if sha1_arr is None or mod_arr is None or mod_name is None: return None
|
||||||
# ============================================================
|
if count <= 0: return None
|
||||||
def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
|
# Pass 1: exact match
|
||||||
count: int, mod_name: str) -> str:
|
for i in range(int(count)):
|
||||||
"""从模块名查找 SHA1(精确匹配 + 包名回退)
|
|
||||||
|
|
||||||
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
|
||||||
支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
|
||||||
"""
|
|
||||||
if sha1_arr is None or mod_arr is None or mod_name is None:
|
|
||||||
return None
|
|
||||||
if count <= 0:
|
|
||||||
return None
|
|
||||||
# 第一遍:精确匹配
|
|
||||||
for i in range(count):
|
|
||||||
idx: sizet = sizet(i) * 64
|
idx: sizet = sizet(i) * 64
|
||||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
|
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
|
||||||
# (热点:本函数全表逐条 strcmp 查名)。
|
# (hot spot: this function strcmp-scans the whole table per lookup).
|
||||||
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
if mod_arr[idx] == mod_name[0] and string.strcmp(mod_arr + idx, mod_name) == 0:
|
||||||
sidx: sizet = sizet(i) * 17
|
sidx: sizet = sizet(i) * 17
|
||||||
return sha1_arr + sidx
|
return sha1_arr + sidx
|
||||||
# 第二遍:包名回退("ast" → "ast.__init__")
|
# Pass 2: package fallback ("ast" -> "ast.__init__")
|
||||||
nm_len: sizet = string.strlen(mod_name)
|
nm_len: sizet = string.strlen(mod_name)
|
||||||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||||||
if init_buf is not None:
|
if init_buf is not None:
|
||||||
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
||||||
for i in range(count):
|
for i in range(int(count)):
|
||||||
idx2: sizet = sizet(i) * 64
|
idx2: sizet = sizet(i) * 64
|
||||||
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||||||
sidx2: sizet = sizet(i) * 17
|
sidx2: sizet = sizet(i) * 17
|
||||||
@@ -356,20 +220,14 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str,
|
|||||||
return sha1_arr + sidx2
|
return sha1_arr + sidx2
|
||||||
stdlib.free(init_buf)
|
stdlib.free(init_buf)
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _AddSha1ToSet - add a SHA1 to a set (deduped; count unchanged if already present).
|
||||||
# _AddSha1ToSet - 将 SHA1 加入集合(去重;已在集合中则 count 不变)
|
# set_buf holds 17-byte entries; returns the new count.
|
||||||
#
|
|
||||||
# Args: set_buf SHA1 集合缓冲区(每个 17 字节);count 当前集合中的 SHA1 数量;sha1 要加入的 SHA1
|
|
||||||
# Returns: 新的 count
|
|
||||||
# ============================================================
|
|
||||||
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
||||||
"""将 SHA1 加入集合,返回新 count"""
|
"""Add a SHA1 to the set (deduped) and return the new count."""
|
||||||
if set_buf is None or sha1 is None:
|
if set_buf is None or sha1 is None: return count
|
||||||
return count
|
for i in range(int(count)):
|
||||||
for i in range(count):
|
|
||||||
idx: sizet = sizet(i) * 17
|
idx: sizet = sizet(i) * 17
|
||||||
if string.strcmp(set_buf + idx, sha1) == 0:
|
if string.strcmp(set_buf + idx, sha1) == 0: return count
|
||||||
return count
|
|
||||||
if count < StubMerger.MAX_INCLUDES_SHA1:
|
if count < StubMerger.MAX_INCLUDES_SHA1:
|
||||||
idx: sizet = sizet(count) * 17
|
idx: sizet = sizet(count) * 17
|
||||||
string.strcpy(set_buf + idx, sha1)
|
string.strcpy(set_buf + idx, sha1)
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import w32.win32file as win32file
|
import win32.win32file as win32file
|
||||||
import w32.win32base as win32base
|
import win32.win32base as win32base
|
||||||
import w32.fileio as fileio
|
import win32.fileio as fileio
|
||||||
import viperlib
|
import viperlib
|
||||||
import sys
|
import sys
|
||||||
import ast
|
import ast
|
||||||
@@ -26,44 +25,26 @@ import lib.Stub.StubMerger as StubMerger
|
|||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# StubMergerStore - 全局 SHA1 映射存储器(sha1→模块名/相对路径)读写
|
# StubMergerStore - global SHA1 map store (sha1 -> module name / rel path) read/write
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# Import-context accounting, single source of truth: AST import statements map to
|
||||||
# 导入上下文记账(单一真源)
|
# imported_modules / from_imports; both translate-time and vpi-injection use these helpers.
|
||||||
#
|
|
||||||
# 「AST 导入语句 → imported_modules / from_imports」的记账口径必须唯一。
|
|
||||||
# 翻译期(ImportsGenMixin.translate_import)与 vpi 注入期(VpiReader 顶层
|
|
||||||
# 遍历)此前各写一份:注入期漏掉 `import X as Y → from_imports "Y:X"`(别名
|
|
||||||
# 反查模块真名的唯一入口,见 LookupModNameByAlias)以及 `import a.b.c →
|
|
||||||
# imported_modules "a"`(顶层包名放行 is_module_imported)。前者使注解里的
|
|
||||||
# `TSpec.TypeSpec*` 在 _forward_declare_ptr_from_node 4a'/4b 全落空 → 退化
|
|
||||||
# 为 Ptr(i8)(现场:GenTypes.VarEntry.Spec.PtrLevel 属性访问失败);后者使
|
|
||||||
# 包限定解析在 stub 上下文里被误判"未导入"。
|
|
||||||
#
|
|
||||||
# 现统一到本模块,两侧只保留各自的特有副作用(SHA1 校验 / reexport 注册 /
|
|
||||||
# add_visible_struct),字符串记账一律走下面这组函数。ImportCtx 只承载两个
|
|
||||||
# 上下文串(Viper 无多返回值,用 pool 分配的小 struct 传出)。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
class ImportCtx:
|
class ImportCtx:
|
||||||
"""导入上下文容器:两个空格分隔串(ImportedModules / FromImports)"""
|
"""Import context container: two space-separated lists (ImportedModules / FromImports)"""
|
||||||
ImportedModules: str
|
ImportedModules: str
|
||||||
FromImports: str
|
FromImports: str
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# AppendImportedModule - 追加模块名到空格分隔列表(纯记账,无索引副作用)
|
# AppendImportedModule - append module name to space-separated list (pure bookkeeping)
|
||||||
# ============================================================
|
|
||||||
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
||||||
"""追加模块名到空格分隔列表(pool 分配),返回新列表"""
|
"""Append a module name to a space-separated list (pool-allocated); returns the new list"""
|
||||||
if name is None:
|
if name is None: return cur
|
||||||
return cur
|
|
||||||
if cur is None:
|
if cur is None:
|
||||||
nlen: sizet = string.strlen(name)
|
nlen: sizet = string.strlen(name)
|
||||||
buf: str = pool.alloc(nlen + 1)
|
buf: str = pool.alloc(nlen + 1)
|
||||||
if buf is not None:
|
if buf is not None: string.strcpy(buf, name)
|
||||||
string.strcpy(buf, name)
|
|
||||||
return buf
|
return buf
|
||||||
old_len: sizet = string.strlen(cur)
|
old_len: sizet = string.strlen(cur)
|
||||||
name_len: sizet = string.strlen(name)
|
name_len: sizet = string.strlen(name)
|
||||||
@@ -75,18 +56,11 @@ def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str:
|
|||||||
return buf2
|
return buf2
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# AppendFromImport - append a from-import mapping ("local:mod[:orig]") to a space-separated list.
|
||||||
# AppendFromImport - 追加 from-import 映射("local:mod[:orig]" 空格分隔)
|
# Exact-size allocation (no fixed 256 buffer) so overlong entries are never silently truncated.
|
||||||
#
|
def AppendFromImport(pool: memhub.MemBuddy*, cur: str, local: str, mod: str, orig: str) -> str:
|
||||||
# [D7·截断纪律] 旧实现固定 256 缓冲 + snprintf:三者合计超长即被静默截断,
|
"""Append a from-import mapping (pool-allocated); returns the new list"""
|
||||||
# 该条目在查找侧仍被当作合法条目解析出错误的 local/mod/orig ⇒ 跨模块类名/
|
if local is None or mod is None: return cur
|
||||||
# 别名还原取到错名字(污染 Spec.SourceName)。此处按精确长度分配,无上限。
|
|
||||||
# ============================================================
|
|
||||||
def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
|
|
||||||
local: str, mod: str, orig: str) -> str:
|
|
||||||
"""追加 from-import 映射(pool 分配),返回新列表"""
|
|
||||||
if local is None or mod is None:
|
|
||||||
return cur
|
|
||||||
llen: sizet = string.strlen(local)
|
llen: sizet = string.strlen(local)
|
||||||
mlen: sizet = string.strlen(mod)
|
mlen: sizet = string.strlen(mod)
|
||||||
use_orig: int = 0
|
use_orig: int = 0
|
||||||
@@ -96,19 +70,16 @@ def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
|
|||||||
olen = string.strlen(orig)
|
olen = string.strlen(orig)
|
||||||
# local ':' mod [':' orig] '\0'
|
# local ':' mod [':' orig] '\0'
|
||||||
need: sizet = llen + 1 + mlen + 1
|
need: sizet = llen + 1 + mlen + 1
|
||||||
if use_orig != 0:
|
if use_orig != 0: need += 1 + olen
|
||||||
need += 1 + olen
|
|
||||||
entry: str = pool.alloc(need)
|
entry: str = pool.alloc(need)
|
||||||
if entry is None:
|
if entry is None: return cur
|
||||||
return cur
|
|
||||||
string.strcpy(entry, local)
|
string.strcpy(entry, local)
|
||||||
entry[llen] = ':'
|
entry[llen] = ':'
|
||||||
string.strcpy(entry + llen + 1, mod)
|
string.strcpy(entry + llen + 1, mod)
|
||||||
if use_orig != 0:
|
if use_orig != 0:
|
||||||
entry[llen + 1 + mlen] = ':'
|
entry[llen + 1 + mlen] = ':'
|
||||||
string.strcpy(entry + llen + 1 + mlen + 1, orig)
|
string.strcpy(entry + llen + 1 + mlen + 1, orig)
|
||||||
if cur is None:
|
if cur is None: return entry
|
||||||
return entry
|
|
||||||
old_len: sizet = string.strlen(cur)
|
old_len: sizet = string.strlen(cur)
|
||||||
entry_len: sizet = string.strlen(entry)
|
entry_len: sizet = string.strlen(entry)
|
||||||
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
|
buf: str = pool.alloc(old_len + 1 + entry_len + 1)
|
||||||
@@ -120,32 +91,21 @@ def AppendFromImport(pool: memhub.MemBuddy*, cur: str,
|
|||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# AllocImportCtx - 用两个既有上下文串构造记账容器
|
# AllocImportCtx - build the accounting container from two existing context strings
|
||||||
# ============================================================
|
def AllocImportCtx(pool: memhub.MemBuddy*, imported_modules: str, from_imports: str) -> ImportCtx*:
|
||||||
def AllocImportCtx(pool: memhub.MemBuddy*,
|
"""Build an import context container (pool-allocated); either list may be None"""
|
||||||
imported_modules: str, from_imports: str) -> ImportCtx*:
|
|
||||||
"""构造导入上下文容器(pool 分配);两串可为 None"""
|
|
||||||
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
|
ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__())
|
||||||
if ctx is None:
|
if ctx is None: return None
|
||||||
return None
|
|
||||||
ctx.ImportedModules = imported_modules
|
ctx.ImportedModules = imported_modules
|
||||||
ctx.FromImports = from_imports
|
ctx.FromImports = from_imports
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# NotePlainImport - record `import X` / `import X as Y` in place on ctx.
|
||||||
# NotePlainImport - 记账 `import X` / `import X as Y`(就地更新 ctx)
|
# Aliased: append alias Y + from-import "Y:X" (sole source for reverse module lookup).
|
||||||
#
|
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, name: str, asname: str) -> int:
|
||||||
# 有别名:追加别名 `Y`,并写 from-import 映射 "Y:X"——这就是 `LookupModNameByAlias`
|
"""Record a single import in place on ctx; returns 0"""
|
||||||
# 与 `_forward_declare_ptr_from_node` 4a' 反查模块真名的唯一来源,绝不可省。
|
if ctx is None or name is None: return 1
|
||||||
# 无别名:`import a.b.c` 时把顶层包名 `a` 也追加(使 a.b.c 作为属性访问 base
|
|
||||||
# 时 is_module_imported("a") 放行)。
|
|
||||||
# ============================================================
|
|
||||||
def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*,
|
|
||||||
name: str, asname: str) -> int:
|
|
||||||
"""记账单条 import(就地更新 ctx),返回 0"""
|
|
||||||
if ctx is None or name is None:
|
|
||||||
return 1
|
|
||||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
|
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name)
|
||||||
if asname is not None:
|
if asname is not None:
|
||||||
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
|
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname)
|
||||||
@@ -159,58 +119,42 @@ def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*,
|
|||||||
if pkg_buf is not None:
|
if pkg_buf is not None:
|
||||||
string.strncpy(pkg_buf, name, pkg_len)
|
string.strncpy(pkg_buf, name, pkg_len)
|
||||||
pkg_buf[pkg_len] = '\0'
|
pkg_buf[pkg_len] = '\0'
|
||||||
ctx.ImportedModules = AppendImportedModule(
|
ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, pkg_buf)
|
||||||
pool, ctx.ImportedModules, pkg_buf)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# PopulateSha1MapStore - build sha1 -> module_name map from ScanResult.Entries into the global store.
|
||||||
# PopulateSha1MapStore - 遍历 ScanResult.Entries 构建 sha1→module_name 映射,填充全局存储器
|
# Memory via stdlib.malloc (global, not freed with mbuddy). Returns entry count, -1 on failure.
|
||||||
#
|
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||||||
# 模块名通过 _PathToModuleName(includes/rel_path) 计算;内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
"""Populate the global SHA1 store from a scan result; returns the entry count or -1"""
|
||||||
# Args: scan_result IncludesScanner 扫描结果
|
if scan_result is None: return -1
|
||||||
# Returns: 填充的条目数,-1 表示失败
|
# free the old store
|
||||||
# ============================================================
|
if StubMerger._sha1_store_arr is not None: stdlib.free(StubMerger._sha1_store_arr)
|
||||||
def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int:
|
if StubMerger._sha1_store_mod is not None: stdlib.free(StubMerger._sha1_store_mod)
|
||||||
"""从扫描结果填充全局 SHA1 映射存储器
|
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
|
||||||
|
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
|
||||||
遍历 ScanResult.Entries,为每个条目构建 sha1→module_name 映射。
|
|
||||||
模块名通过 _PathToModuleName(includes/rel_path) 计算。
|
|
||||||
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
|
|
||||||
"""
|
|
||||||
if scan_result is None:
|
|
||||||
return -1
|
|
||||||
# 释放旧存储器
|
|
||||||
if StubMerger._sha1_store_arr is not None:
|
|
||||||
stdlib.free(StubMerger._sha1_store_arr)
|
|
||||||
if StubMerger._sha1_store_mod is not None:
|
|
||||||
stdlib.free(StubMerger._sha1_store_mod)
|
|
||||||
if StubMerger._sha1_store_rel is not None:
|
|
||||||
stdlib.free(StubMerger._sha1_store_rel)
|
|
||||||
if StubMerger._sha1_store_incdir is not None:
|
|
||||||
stdlib.free(StubMerger._sha1_store_incdir)
|
|
||||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
|
||||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
|
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
return -1
|
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
|
||||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
StubMerger._sha1_store_count = 0
|
StubMerger._sha1_store_count = 0
|
||||||
p_bad: int = 0
|
p_bad: int = 0
|
||||||
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
|
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||||
for i in range(scan_result.Count):
|
for i in range(int(scan_result.Count)):
|
||||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
|
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: VLogger.error("includes SHA1 store exceeded the limit; aborting", "StubMerger")
|
||||||
VLogger.error("includes SHA1 存储超上限,终止", "StubMerger")
|
|
||||||
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
|
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
|
||||||
ent: rawptr[IncludesScanner.FileEntry] = rawptr[IncludesScanner.FileEntry](ea)
|
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea)
|
||||||
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
if ent is None or ent.Sha1 is None or ent.RelPath is None:
|
||||||
p_bad += 1
|
p_bad += 1
|
||||||
continue
|
continue
|
||||||
# 构建完整路径 includes/{RelPath}(_PathToModuleName 需要)
|
# build full path includes/{RelPath} (needed by _PathToModuleName)
|
||||||
rp_len: sizet = string.strlen(ent.RelPath)
|
rp_len: sizet = string.strlen(ent.RelPath)
|
||||||
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
|
||||||
if full_path_buf is None:
|
if full_path_buf is None:
|
||||||
@@ -224,212 +168,161 @@ def PopulateSha1MapStore(scan_result: rawptr[IncludesScanner.ScanResult]) -> int
|
|||||||
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
|
string.strcpy(StubMerger._sha1_store_arr + idx, ent.Sha1)
|
||||||
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
|
idx2: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||||
mn_len: sizet = string.strlen(mod_name)
|
mn_len: sizet = string.strlen(mod_name)
|
||||||
if mn_len < 64:
|
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
|
||||||
string.strcpy(StubMerger._sha1_store_mod + idx2, mod_name)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
|
string.strncpy(StubMerger._sha1_store_mod + idx2, mod_name, 63)
|
||||||
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
|
StubMerger._sha1_store_mod[idx2 + 63] = '\0'
|
||||||
stdlib.free(mod_name)
|
stdlib.free(mod_name)
|
||||||
# 保存 rel_path(供 Phase2 3.5 编译缺失 includes 使用)
|
# save rel_path (used by Phase2 3.5 to compile missing includes)
|
||||||
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
idx3: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||||
if rp_len < StubMerger.MAX_REL_PATH_LEN:
|
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
||||||
string.strcpy(StubMerger._sha1_store_rel + idx3, ent.RelPath)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
|
string.strncpy(StubMerger._sha1_store_rel + idx3, ent.RelPath, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||||
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
StubMerger._sha1_store_rel[idx3 + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||||
# 保存所属 includes 根目录(Phase2 预注册/按需编译拼路径用)
|
# save owning includes root dir (Phase2 pre-register / on-demand path building)
|
||||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||||
if ent.IncDir is not None:
|
if ent.IncDir is not None:
|
||||||
icd_len: sizet = string.strlen(ent.IncDir)
|
icd_len: sizet = string.strlen(ent.IncDir)
|
||||||
if icd_len < StubMerger.MAX_INC_DIR_LEN:
|
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
|
||||||
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, StubMerger.MAX_INC_DIR_LEN - 1)
|
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, ent.IncDir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||||||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||||||
StubMerger._sha1_store_count += 1
|
StubMerger._sha1_store_count += 1
|
||||||
if p_bad > 0:
|
if p_bad > 0: VLogger.error(f"includes SHA1 store has {p_bad} failed entries", "StubMerger")
|
||||||
VLogger.error(f"includes SHA1 存储有 {p_bad} 个条目失败", "StubMerger")
|
|
||||||
return StubMerger._sha1_store_count
|
return StubMerger._sha1_store_count
|
||||||
# ============================================================
|
# AppendToSha1MapStore - append an App module entry to the global SHA1 store (Phase2 use);
|
||||||
# AppendToSha1MapStore - 追加 App 模块条目到全局 SHA1 映射存储器(Phase1 先用 PopulateSha1MapStore 填 includes,
|
# auto-initializes if empty, skips on duplicate SHA1. Returns 0 ok, 1 failed.
|
||||||
# Phase2 扫描 App 源文件后调用本函数追加,否则编译 App 自身时跨模块 CDefine 查找命中不到 App 模块)
|
|
||||||
#
|
|
||||||
# 存储器未初始化时自动初始化;SHA1 已存在(去重)则跳过。
|
|
||||||
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对路径(如 "lib/core/StubMerger.vp",不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
|
|
||||||
# Returns: 0 成功(含去重跳过),1 失败
|
|
||||||
# ============================================================
|
|
||||||
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||||
"""追加条目到全局 SHA1 映射存储器(Phase2 用)"""
|
"""Append an entry to the global SHA1 store (Phase2)"""
|
||||||
if sha1 is None or rel_path is None:
|
if sha1 is None or rel_path is None: return 1
|
||||||
return 1
|
# init store if empty (Phase2 may run before Phase1)
|
||||||
# 存储器未初始化:自动初始化(Phase2 可能先于 Phase1 调用)
|
|
||||||
if StubMerger._sha1_store_arr is None:
|
if StubMerger._sha1_store_arr is None:
|
||||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
|
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
return 1
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
StubMerger._sha1_store_count = 0
|
StubMerger._sha1_store_count = 0
|
||||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
|
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||||
return 1
|
# module name: rel_path "lib/core/StubMerger.vp" -> "lib.core.StubMerger"
|
||||||
# 模块名:rel_path "lib/core/StubMerger.vp" → "lib.core.StubMerger"
|
|
||||||
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
|
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
|
||||||
if full_path_buf is None:
|
if full_path_buf is None: return 1
|
||||||
return 1
|
|
||||||
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
|
||||||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||||||
stdlib.free(full_path_buf)
|
stdlib.free(full_path_buf)
|
||||||
if mod_name is None:
|
if mod_name is None: return 1
|
||||||
return 1
|
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
|
||||||
# 去重:SHA1 与模块名都相同才跳过——同一 SHA1 可能先以全路径模块名注册(App 源文件在 includes 目录下),
|
# under a full-path name; the App short name must stay or exact lookup breaks.
|
||||||
# App 追加的短模块名必须保留,否则 import 该模块的精确查找失败、后缀匹配会劫持到其它同名模块。
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
for i in range(StubMerger._sha1_store_count):
|
|
||||||
sidx_d: sizet = sizet(i) * 17
|
sidx_d: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||||
midx_d: sizet = sizet(i) * 64
|
midx_d: sizet = sizet(i) * 64
|
||||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
||||||
stdlib.free(mod_name)
|
stdlib.free(mod_name)
|
||||||
return 0
|
return 0
|
||||||
# 写入 SHA1
|
# write SHA1
|
||||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||||
# 写入模块名
|
# write module name
|
||||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||||
mn_len: sizet = string.strlen(mod_name)
|
mn_len: sizet = string.strlen(mod_name)
|
||||||
if mn_len < 64:
|
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||||
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||||
stdlib.free(mod_name)
|
stdlib.free(mod_name)
|
||||||
# 写入相对路径
|
# write rel path
|
||||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||||
rp_len: sizet = string.strlen(rel_path)
|
rp_len: sizet = string.strlen(rel_path)
|
||||||
if rp_len < StubMerger.MAX_REL_PATH_LEN:
|
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||||
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||||
# App 源文件不属于任何 includes 目录:IncDir 留空(Phase2 回退 includes_dir 参数)
|
# App sources belong to no includes dir: leave IncDir empty (Phase2 falls back to includes_dir)
|
||||||
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
idx_inc_a: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||||
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
|
StubMerger._sha1_store_incdir[idx_inc_a] = '\0'
|
||||||
StubMerger._sha1_store_count += 1
|
StubMerger._sha1_store_count += 1
|
||||||
return 0
|
return 0
|
||||||
# ============================================================
|
# AddIncludeToSha1MapStore - register one includes entry into the global SHA1 store (on-demand).
|
||||||
# AddIncludeToSha1MapStore - 单条登记 includes 条目到全局 SHA1 映射存储器
|
# Unreferenced includes stay out; App sources still use AppendToSha1MapStore (empty inc_dir).
|
||||||
#
|
|
||||||
# 依赖图按需扫描(_BuildReachableSha1Set)解析到可达 includes 文件后逐条登记,未引用的 includes 不进 store;
|
|
||||||
# App 源文件仍用 AppendToSha1MapStore(inc_dir 留空)。
|
|
||||||
# Args: sha1 SHA1 字符串(16 字符 + null);rel_path 相对 includes 根目录的路径(如 "ast/parser.vp");inc_dir 所属 includes 根目录;mod_name 模块名(如 "ast.parser")
|
|
||||||
# Returns: 0 成功(含去重跳过),1 失败
|
|
||||||
# ============================================================
|
|
||||||
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
|
def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int:
|
||||||
"""单条登记 includes 条目到全局 SHA1 映射存储器(依赖图按需扫描用)"""
|
"""Register one includes entry in the global SHA1 store (on-demand scan)"""
|
||||||
if sha1 is None or rel_path is None or mod_name is None:
|
if sha1 is None or rel_path is None or mod_name is None: return 1
|
||||||
return 1
|
# init store if empty
|
||||||
# 存储器未初始化:自动初始化
|
|
||||||
if StubMerger._sha1_store_arr is None:
|
if StubMerger._sha1_store_arr is None:
|
||||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None:
|
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
return 1
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||||
StubMerger._sha1_store_count = 0
|
StubMerger._sha1_store_count = 0
|
||||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES:
|
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||||
return 1
|
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
|
||||||
# 去重:SHA1 与模块名都相同才跳过(对齐 AppendToSha1MapStore)
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
for i in range(StubMerger._sha1_store_count):
|
|
||||||
sidx_d: sizet = sizet(i) * 17
|
sidx_d: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||||
midx_d: sizet = sizet(i) * 64
|
midx_d: sizet = sizet(i) * 64
|
||||||
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0:
|
if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: return 0
|
||||||
return 0
|
# write SHA1
|
||||||
# 写 SHA1
|
|
||||||
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
idx_a: sizet = sizet(StubMerger._sha1_store_count) * 17
|
||||||
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1)
|
||||||
# 写模块名
|
# write module name
|
||||||
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
idx_m: sizet = sizet(StubMerger._sha1_store_count) * 64
|
||||||
mn_len: sizet = string.strlen(mod_name)
|
mn_len: sizet = string.strlen(mod_name)
|
||||||
if mn_len < 64:
|
if mn_len < 64: string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
||||||
string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63)
|
||||||
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
StubMerger._sha1_store_mod[idx_m + 63] = '\0'
|
||||||
# 写相对路径
|
# write rel path
|
||||||
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
idx_r: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN
|
||||||
rp_len: sizet = string.strlen(rel_path)
|
rp_len: sizet = string.strlen(rel_path)
|
||||||
if rp_len < StubMerger.MAX_REL_PATH_LEN:
|
if rp_len < StubMerger.MAX_REL_PATH_LEN: string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
||||||
string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1)
|
||||||
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0'
|
||||||
# 写所属 includes 根目录(非空 = includes 条目;App 条目留空)
|
# write owning includes root (non-empty = includes entry; App entries empty)
|
||||||
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
idx_inc: sizet = sizet(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN
|
||||||
if inc_dir is not None:
|
if inc_dir is not None:
|
||||||
icd_len: sizet = string.strlen(inc_dir)
|
icd_len: sizet = string.strlen(inc_dir)
|
||||||
if icd_len < StubMerger.MAX_INC_DIR_LEN:
|
if icd_len < StubMerger.MAX_INC_DIR_LEN: string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
|
||||||
string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir)
|
|
||||||
else:
|
else:
|
||||||
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
|
string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1)
|
||||||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||||||
else:
|
else: StubMerger._sha1_store_incdir[idx_inc] = '\0'
|
||||||
StubMerger._sha1_store_incdir[idx_inc] = '\0'
|
|
||||||
StubMerger._sha1_store_count += 1
|
StubMerger._sha1_store_count += 1
|
||||||
return 0
|
return 0
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# GetSha1StoreCount - 获取全局存储器条目数
|
# GetSha1StoreCount - return the global store entry count
|
||||||
# ============================================================
|
|
||||||
def GetSha1StoreCount() -> int:
|
def GetSha1StoreCount() -> int:
|
||||||
"""返回全局 SHA1 存储器的条目数"""
|
"""Return the global SHA1 store entry count"""
|
||||||
return StubMerger._sha1_store_count
|
return StubMerger._sha1_store_count
|
||||||
# ============================================================
|
# LookupSha1ByModName - find SHA1 by module name; falls back to "{mod_name}.__init__".
|
||||||
# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器)
|
# Returns a pointer into the store (do not free), None if not found.
|
||||||
#
|
|
||||||
# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
|
|
||||||
# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupSha1ByModName(mod_name: str) -> str:
|
def LookupSha1ByModName(mod_name: str) -> str:
|
||||||
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
|
"""Look up a SHA1 by module name (exact then package fallback); delegates to _FindSha1ByModName"""
|
||||||
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
|
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Generic-class owner table (generic class name -> defining module SHA1), REPL-session only;
|
||||||
# 泛型类归属表(泛型类名 → 定义模块 SHA1)——仅 REPL 会话登记/查询
|
# needed because REPL skips generic templates, so generic subscript resolution would otherwise miss.
|
||||||
#
|
define GENERIC_OWNER_MAX = 32
|
||||||
# 为什么需要:REPL 的 includes 会话初始化只注入 `.vpi` 的类(泛型类被跳过),
|
|
||||||
# 不跑批处理的 _PreRegister*GenericTemplates ⇒ 会话内泛型模板表为空 ⇒ 注解里的
|
|
||||||
# `list[rawptr[AST]]` 等泛型下标解析(`TypeGenResolve._resolve_generic_subscript_ty`)
|
|
||||||
# 恒 miss。会话侧的最后兜底要按**定义侧特化命名** "%{模板模块 sha1}.{mangled}"
|
|
||||||
# 造 opaque 具名类型(实证:`%"b56d2f995104690d.list[rawptr[AST]]"`),因此需要
|
|
||||||
# "泛型类定义在哪个模块"这一事实——由注入期扫 `.vpi` 的 ClassDef 登记(见
|
|
||||||
# VpiReader._repl_stub_collect_module)。
|
|
||||||
#
|
|
||||||
# 归属模块 sha1 无法从类名反推(模块名取自文件路径,class list ← _list.vp;用户
|
|
||||||
# 泛型类更是名字与模块名无关),故必须显式登记,不做"按名字猜"。
|
|
||||||
# 批处理不登记(表恒空 ⇒ 查询恒 None ⇒ 批处理路径一字不改)。
|
|
||||||
# 容量:includes 现有 6 个泛型类定义(list/dict 系 + Vector/GSList/ndarray),取 32 槽。
|
|
||||||
# ============================================================
|
|
||||||
GENERIC_OWNER_MAX: t.CDefine = 32
|
|
||||||
_generic_owner_names: bytes = None
|
_generic_owner_names: bytes = None
|
||||||
_generic_owner_sha1s: bytes = None
|
_generic_owner_sha1s: bytes = None
|
||||||
_generic_owner_count: int = 0
|
_generic_owner_count: int = 0
|
||||||
|
|
||||||
|
|
||||||
def ResetGenericOwners() -> None:
|
def ResetGenericOwners() -> None:
|
||||||
"""清空泛型类归属表(REPL 会话初始化开始时调用一次)"""
|
"""Clear the generic-class owner table (called once at REPL session start)"""
|
||||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||||
if _generic_owner_names is not None:
|
if _generic_owner_names is not None:
|
||||||
stdlib.free(_generic_owner_names)
|
stdlib.free(_generic_owner_names)
|
||||||
@@ -441,31 +334,23 @@ def ResetGenericOwners() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||||||
"""登记"泛型类名 → 定义模块 SHA1";1=已登记(含重复)/ 0=失败(参数/满/同名异 sha1)
|
"""Register generic-class-name to defining-module SHA1; 1 = registered, 0 = failed"""
|
||||||
|
|
||||||
同名异 sha1(跨模块同名泛型类)保留首个,与泛型模板表按类名去重的口径一致。
|
|
||||||
"""
|
|
||||||
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
|
||||||
if cls_name is None or mod_sha1 is None:
|
if cls_name is None or mod_sha1 is None: return 0
|
||||||
return 0
|
if cls_name[0] == '\0' or mod_sha1[0] == '\0': return 0
|
||||||
if cls_name[0] == '\0' or mod_sha1[0] == '\0':
|
|
||||||
return 0
|
|
||||||
if _generic_owner_names is None:
|
if _generic_owner_names is None:
|
||||||
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
|
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
|
||||||
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
|
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
|
||||||
if _generic_owner_names is None or _generic_owner_sha1s is None:
|
if _generic_owner_names is None or _generic_owner_sha1s is None: return 0
|
||||||
return 0
|
|
||||||
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
|
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
|
||||||
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
|
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
|
||||||
_generic_owner_count = 0
|
_generic_owner_count = 0
|
||||||
i_go: int
|
i_go: int
|
||||||
for i_go in range(_generic_owner_count):
|
for i_go in range(int(_generic_owner_count)):
|
||||||
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
|
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
|
||||||
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0:
|
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0: return 1
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
if _generic_owner_count >= GENERIC_OWNER_MAX:
|
if _generic_owner_count >= GENERIC_OWNER_MAX: return 0
|
||||||
return 0
|
|
||||||
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
|
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
|
||||||
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
|
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
|
||||||
_generic_owner_count += 1
|
_generic_owner_count += 1
|
||||||
@@ -473,51 +358,40 @@ def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def LookupGenericOwnerSha1(cls_name: str) -> str:
|
def LookupGenericOwnerSha1(cls_name: str) -> str:
|
||||||
"""按泛型类名取定义模块 SHA1(返回内部指针,无需释放;未登记 None)"""
|
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
|
||||||
if cls_name is None or _generic_owner_names is None:
|
if cls_name is None or _generic_owner_names is None: return None
|
||||||
return None
|
|
||||||
i_lg: int
|
i_lg: int
|
||||||
for i_lg in range(_generic_owner_count):
|
for i_lg in range(int(_generic_owner_count)):
|
||||||
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0:
|
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
|
||||||
return _generic_owner_sha1s + sizet(i_lg) * 17
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1(用于 from .X import Y:impf.module 是 ".X",而映射表键是 "pkg.X")
|
# LookupSha1ByModNameSuffix - find SHA1 by suffix; tries exact, suffix, reverse-suffix. None if absent.
|
||||||
#
|
|
||||||
# 包含三种匹配方式:精确、后缀、反向后缀。
|
|
||||||
# Returns: SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||||
"""后缀匹配查找模块 SHA1"""
|
"""Look up a module SHA1 by suffix match"""
|
||||||
if mod_suffix is None:
|
if mod_suffix is None: return None
|
||||||
return None
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
if StubMerger._sha1_store_count <= 0: return None
|
||||||
return None
|
|
||||||
if StubMerger._sha1_store_count <= 0:
|
|
||||||
return None
|
|
||||||
suffix_len_sf: sizet = string.strlen(mod_suffix)
|
suffix_len_sf: sizet = string.strlen(mod_suffix)
|
||||||
if suffix_len_sf == 0:
|
if suffix_len_sf == 0: return None
|
||||||
return None
|
|
||||||
i_sf: int
|
i_sf: int
|
||||||
for i_sf in range(StubMerger._sha1_store_count):
|
for i_sf in range(int(StubMerger._sha1_store_count)):
|
||||||
idx_sf: sizet = sizet(i_sf) * 64
|
idx_sf: sizet = sizet(i_sf) * 64
|
||||||
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
||||||
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
||||||
if mod_len_sf == 0:
|
if mod_len_sf == 0: continue
|
||||||
continue
|
# exact match
|
||||||
# 精确匹配
|
|
||||||
if mod_len_sf == suffix_len_sf:
|
if mod_len_sf == suffix_len_sf:
|
||||||
if string.strcmp(mod_name_sf, mod_suffix) == 0:
|
if string.strcmp(mod_name_sf, mod_suffix) == 0:
|
||||||
sidx_sf: sizet = sizet(i_sf) * 17
|
sidx_sf: sizet = sizet(i_sf) * 17
|
||||||
return StubMerger._sha1_store_arr + sidx_sf
|
return StubMerger._sha1_store_arr + sidx_sf
|
||||||
# 后缀匹配:mod_name 以 "." + mod_suffix 结尾
|
# suffix match: mod_name ends with "." + mod_suffix
|
||||||
if mod_len_sf > suffix_len_sf:
|
if mod_len_sf > suffix_len_sf:
|
||||||
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
|
if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.':
|
||||||
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
|
cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf
|
||||||
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
|
if string.strcmp(cmp_start_sf, mod_suffix) == 0:
|
||||||
sidx2_sf: sizet = sizet(i_sf) * 17
|
sidx2_sf: sizet = sizet(i_sf) * 17
|
||||||
return StubMerger._sha1_store_arr + sidx2_sf
|
return StubMerger._sha1_store_arr + sidx2_sf
|
||||||
# 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾
|
# reverse suffix match: mod_suffix ends with "." + mod_name
|
||||||
if suffix_len_sf > mod_len_sf:
|
if suffix_len_sf > mod_len_sf:
|
||||||
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
|
if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.':
|
||||||
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
|
cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf
|
||||||
@@ -526,47 +400,34 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
|||||||
return StubMerger._sha1_store_arr + sidx_rev_sf
|
return StubMerger._sha1_store_arr + sidx_rev_sf
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupSha1ByAlias - 从 from_imports 别名映射解析模块 SHA1
|
# LookupSha1ByAlias - resolve SHA1 from from_imports alias "local:module[:orig]" entries.
|
||||||
#
|
|
||||||
# from_imports 条目格式 "local:module" 或 "local:module:orig"(空格分隔,由 ImportsGenMixin.add_from_import 写入);
|
|
||||||
# 命中 local==alias 时对 module 执行 LookupSha1ByModName。
|
|
||||||
# Returns: SHA1 字符串指针(指向全局存储器内部),None 未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||||
"""从 from_imports 别名映射解析模块 SHA1(改名别名 import X as Y 场景)"""
|
"""Resolve a module SHA1 from a from-import alias mapping (import X as Y)"""
|
||||||
if from_imports is None or alias is None:
|
if from_imports is None or alias is None: return None
|
||||||
return None
|
|
||||||
alias_len: sizet = string.strlen(alias)
|
alias_len: sizet = string.strlen(alias)
|
||||||
if alias_len == 0:
|
if alias_len == 0: return None
|
||||||
return None
|
|
||||||
total_len: sizet = string.strlen(from_imports)
|
total_len: sizet = string.strlen(from_imports)
|
||||||
ci: sizet = 0
|
ci: sizet = 0
|
||||||
while ci < total_len:
|
while ci < total_len:
|
||||||
# 跳过前导空格
|
# skip leading spaces
|
||||||
while ci < total_len and from_imports[ci] == ' ':
|
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||||
ci += 1
|
if ci >= total_len: break
|
||||||
if ci >= total_len:
|
# entry starts at ci; find ':' (end of local_name)
|
||||||
break
|
|
||||||
# 条目起点 ci,找 ':'(local_name 结束)
|
|
||||||
colon: sizet = ci
|
colon: sizet = ci
|
||||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
|
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||||
colon += 1
|
|
||||||
local_len: sizet = colon - ci
|
local_len: sizet = colon - ci
|
||||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||||
# 比较 local_name == alias
|
# compare local_name == alias
|
||||||
match: int = 1
|
Matched: int = 1
|
||||||
li: sizet = 0
|
for li in range(int(alias_len)):
|
||||||
while li < alias_len:
|
|
||||||
if from_imports[ci + li] != alias[li]:
|
if from_imports[ci + li] != alias[li]:
|
||||||
match = 0
|
Matched = 0
|
||||||
break
|
break
|
||||||
li += 1
|
if Matched == 1:
|
||||||
if match == 1:
|
# extract module (from colon+1 to next ':' or space)
|
||||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
|
||||||
m_start: sizet = colon + 1
|
m_start: sizet = colon + 1
|
||||||
m_end: sizet = m_start
|
m_end: sizet = m_start
|
||||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
|
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||||
m_end += 1
|
|
||||||
if m_end > m_start:
|
if m_end > m_start:
|
||||||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||||||
if mbuf is not None:
|
if mbuf is not None:
|
||||||
@@ -576,52 +437,38 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
|||||||
stdlib.free(mbuf)
|
stdlib.free(mbuf)
|
||||||
return res
|
return res
|
||||||
return None
|
return None
|
||||||
# 跳到下一条目
|
# advance to next entry
|
||||||
while ci < total_len and from_imports[ci] != ' ':
|
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||||
ci += 1
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupModNameByAlias - 从 from_imports 别名映射解析模块真名
|
# LookupModNameByAlias - return real module name X for alias Y (from_imports "Y:X"; caller frees).
|
||||||
#
|
|
||||||
# import X as Y 时 ImportsGenMixin.add_from_import 写入 "Y:X"(Y=别名,X=模块真名);
|
|
||||||
# 本函数返回别名 Y 对应的模块真名 X(供限定名解析 "Y.Class" → 按 X 模块归属匹配)。
|
|
||||||
# Returns: 模块名字符串(stdlib 分配,调用方负责 free),None 未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||||
"""从 from_imports 别名映射解析模块真名(import X as Y → Y:X)"""
|
"""Resolve the real module name for an alias from from_imports (import X as Y -> Y:X)"""
|
||||||
if from_imports is None or alias is None:
|
if from_imports is None or alias is None: return None
|
||||||
return None
|
|
||||||
alias_len: sizet = string.strlen(alias)
|
alias_len: sizet = string.strlen(alias)
|
||||||
if alias_len == 0:
|
if alias_len == 0: return None
|
||||||
return None
|
|
||||||
total_len: sizet = string.strlen(from_imports)
|
total_len: sizet = string.strlen(from_imports)
|
||||||
ci: sizet = 0
|
ci: sizet = 0
|
||||||
while ci < total_len:
|
while ci < total_len:
|
||||||
# 跳过前导空格
|
# skip leading spaces
|
||||||
while ci < total_len and from_imports[ci] == ' ':
|
while ci < total_len and from_imports[ci] == ' ': ci += 1
|
||||||
ci += 1
|
if ci >= total_len: break
|
||||||
if ci >= total_len:
|
# entry starts at ci; find ':' (end of local_name)
|
||||||
break
|
|
||||||
# 条目起点 ci,找 ':'(local_name 结束)
|
|
||||||
colon: sizet = ci
|
colon: sizet = ci
|
||||||
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ':
|
while colon < total_len and from_imports[colon] != ':' and from_imports[colon] != ' ': colon += 1
|
||||||
colon += 1
|
|
||||||
local_len: sizet = colon - ci
|
local_len: sizet = colon - ci
|
||||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||||
# 比较 local_name == alias
|
# compare local_name == alias
|
||||||
match: int = 1
|
Matched: int = 1
|
||||||
li: sizet = 0
|
for li in range(int(alias_len)):
|
||||||
while li < alias_len:
|
|
||||||
if from_imports[ci + li] != alias[li]:
|
if from_imports[ci + li] != alias[li]:
|
||||||
match = 0
|
Matched = 0
|
||||||
break
|
break
|
||||||
li += 1
|
if Matched == 1:
|
||||||
if match == 1:
|
# extract module (from colon+1 to next ':' or space)
|
||||||
# 提取 module(colon+1 到下一个 ':' 或空格)
|
|
||||||
m_start: sizet = colon + 1
|
m_start: sizet = colon + 1
|
||||||
m_end: sizet = m_start
|
m_end: sizet = m_start
|
||||||
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ':
|
while m_end < total_len and from_imports[m_end] != ':' and from_imports[m_end] != ' ': m_end += 1
|
||||||
m_end += 1
|
|
||||||
if m_end > m_start:
|
if m_end > m_start:
|
||||||
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
mbuf: str = stdlib.malloc(m_end - m_start + 1)
|
||||||
if mbuf is not None:
|
if mbuf is not None:
|
||||||
@@ -629,127 +476,85 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
|||||||
mbuf[m_end - m_start] = '\0'
|
mbuf[m_end - m_start] = '\0'
|
||||||
return mbuf
|
return mbuf
|
||||||
return None
|
return None
|
||||||
# 跳到下一条目
|
# advance to next entry
|
||||||
while ci < total_len and from_imports[ci] != ' ':
|
while ci < total_len and from_imports[ci] != ' ': ci += 1
|
||||||
ci += 1
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
|
# PopulateIncludesDeps - store includes deps in memory (deps_str space-separated). 0 ok, non-0 fail.
|
||||||
#
|
|
||||||
# Phase B+ 前用 declare_only 翻译 includes 文件、提取 _imported_modules 后调用本函数存入内存;_BuildReachableSha1Set 从内存读取,不读 deps.txt。
|
|
||||||
# Args: sha1 includes 文件的 SHA1(16字符);deps_str 依赖模块名字符串(空格分隔,如 "t c stdint string")
|
|
||||||
# Returns: 0 成功,非 0 失败
|
|
||||||
# ============================================================
|
|
||||||
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||||
"""将 includes 文件的依赖存入内存"""
|
"""Store includes-file dependencies in memory"""
|
||||||
if sha1 is None:
|
if sha1 is None: return 1
|
||||||
return 1
|
# lazy init
|
||||||
# 延迟初始化
|
|
||||||
if StubMerger._includes_deps_sha1 is None:
|
if StubMerger._includes_deps_sha1 is None:
|
||||||
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||||
StubMerger._includes_deps_str = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(MAX_DEPS_STR_LEN) 会回绕成 0,显式加宽到 64 位
|
||||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None:
|
StubMerger._includes_deps_str = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||||
return 1
|
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 1
|
||||||
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
|
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
|
||||||
string.memset(StubMerger._includes_deps_str, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
string.memset(StubMerger._includes_deps_str, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||||
StubMerger._includes_deps_count = 0
|
StubMerger._includes_deps_count = 0
|
||||||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES:
|
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
|
||||||
return 1
|
# dedup: skip if SHA1 already present
|
||||||
# 去重:若 SHA1 已存在则跳过
|
for i in range(int(StubMerger._includes_deps_count)):
|
||||||
for i in range(StubMerger._includes_deps_count):
|
|
||||||
sidx: sizet = sizet(i) * 17
|
sidx: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
|
||||||
return 0
|
# write SHA1
|
||||||
# 写入 SHA1
|
|
||||||
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
|
idx: sizet = sizet(StubMerger._includes_deps_count) * 17
|
||||||
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
|
string.strcpy(StubMerger._includes_deps_sha1 + idx, sha1)
|
||||||
# 写入依赖字符串
|
# write deps string
|
||||||
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
idx2: sizet = sizet(StubMerger._includes_deps_count) * StubMerger.MAX_DEPS_STR_LEN
|
||||||
if deps_str is not None:
|
if deps_str is not None:
|
||||||
dl: sizet = string.strlen(deps_str)
|
dl: sizet = string.strlen(deps_str)
|
||||||
if dl < StubMerger.MAX_DEPS_STR_LEN:
|
if dl < StubMerger.MAX_DEPS_STR_LEN: string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
|
||||||
string.strcpy(StubMerger._includes_deps_str + idx2, deps_str)
|
else: string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
|
||||||
else:
|
else: StubMerger._includes_deps_str[idx2] = '\0'
|
||||||
string.strncpy(StubMerger._includes_deps_str + idx2, deps_str, StubMerger.MAX_DEPS_STR_LEN - 1)
|
|
||||||
else:
|
|
||||||
StubMerger._includes_deps_str[idx2] = '\0'
|
|
||||||
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
|
StubMerger._includes_deps_count = StubMerger._includes_deps_count + 1
|
||||||
return 0
|
return 0
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupIncludesDeps - 从内存查找 includes 文件的依赖
|
# LookupIncludesDeps - look up includes deps by SHA1 in memory; returns space-separated deps or None.
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# sha1: includes 文件的 SHA1(16字符)
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# 依赖模块名字符串指针(空格分隔),None 表示未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupIncludesDeps(sha1: str) -> str:
|
def LookupIncludesDeps(sha1: str) -> str:
|
||||||
"""从内存查找 includes 文件的依赖"""
|
"""Look up includes-file dependencies in memory"""
|
||||||
if sha1 is None or StubMerger._includes_deps_sha1 is None:
|
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
|
||||||
return None
|
if StubMerger._includes_deps_count <= 0: return None
|
||||||
if StubMerger._includes_deps_count <= 0:
|
for i in range(int(StubMerger._includes_deps_count)):
|
||||||
return None
|
|
||||||
for i in range(StubMerger._includes_deps_count):
|
|
||||||
sidx: sizet = sizet(i) * 17
|
sidx: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
||||||
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
|
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
|
||||||
return StubMerger._includes_deps_str + idx2
|
return StubMerger._includes_deps_str + idx2
|
||||||
return None
|
return None
|
||||||
def GetSha1StoreArrPtr() -> bytes:
|
def GetSha1StoreArrPtr() -> bytes:
|
||||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
"""Return the SHA1 array pointer (17 bytes per entry)"""
|
||||||
return StubMerger._sha1_store_arr
|
return StubMerger._sha1_store_arr
|
||||||
def GetSha1StoreModArrPtr() -> bytes:
|
def GetSha1StoreModArrPtr() -> bytes:
|
||||||
"""返回模块名数组指针(每个条目 64 字节)"""
|
"""Return the module-name array pointer (64 bytes per entry)"""
|
||||||
return StubMerger._sha1_store_mod
|
return StubMerger._sha1_store_mod
|
||||||
def GetSha1StoreRelArrPtr() -> bytes:
|
def GetSha1StoreRelArrPtr() -> bytes:
|
||||||
"""返回相对路径数组指针(每个条目 StubMerger.MAX_REL_PATH_LEN 字节)"""
|
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
|
||||||
return StubMerger._sha1_store_rel
|
return StubMerger._sha1_store_rel
|
||||||
def GetSha1StoreIncdirArrPtr() -> bytes:
|
def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||||
"""返回所属 includes 根目录数组指针(每个条目 StubMerger.MAX_INC_DIR_LEN 字节,空串表示不属于任何 includes 目录)"""
|
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
|
||||||
return StubMerger._sha1_store_incdir
|
return StubMerger._sha1_store_incdir
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
|
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||||||
#
|
|
||||||
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
|
|
||||||
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
|
|
||||||
# 内存归全局存储器所有,调用者不要 free
|
|
||||||
# ============================================================
|
|
||||||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||||||
"""获取全局存储器数组指针
|
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||||||
|
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 槽的地址(t.CPtr*),
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||||||
函数把存储器数组指针写入这些槽。返回条目数,-1 失败
|
|
||||||
"""
|
|
||||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
|
|
||||||
return -1
|
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None:
|
|
||||||
return -1
|
|
||||||
sp: rawptr[t.CPtr] = out_sha1_arr
|
sp: rawptr[t.CPtr] = out_sha1_arr
|
||||||
if sp is not None:
|
if sp is not None: sp[0] = StubMerger._sha1_store_arr
|
||||||
sp[0] = StubMerger._sha1_store_arr
|
|
||||||
mp: rawptr[t.CPtr] = out_mod_arr
|
mp: rawptr[t.CPtr] = out_mod_arr
|
||||||
if mp is not None:
|
if mp is not None: mp[0] = StubMerger._sha1_store_mod
|
||||||
mp[0] = StubMerger._sha1_store_mod
|
|
||||||
rp: rawptr[t.CPtr] = out_rel_arr
|
rp: rawptr[t.CPtr] = out_rel_arr
|
||||||
if rp is not None:
|
if rp is not None: rp[0] = StubMerger._sha1_store_rel
|
||||||
rp[0] = StubMerger._sha1_store_rel
|
|
||||||
return StubMerger._sha1_store_count
|
return StubMerger._sha1_store_count
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupSha1RelPath - 通过 SHA1 查找相对路径
|
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
|
||||||
#
|
|
||||||
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free),用于把 sha1 转成人类可读的文件路径。
|
|
||||||
# Args: sha1 SHA1 字符串(16 字符)
|
|
||||||
# Returns: 相对路径字符串指针(只读),None=未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupSha1RelPath(sha1: str) -> str:
|
def LookupSha1RelPath(sha1: str) -> str:
|
||||||
"""通过 SHA1 查找相对路径,返回只读指针,None=未找到"""
|
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||||||
if sha1 is None:
|
if sha1 is None: return None
|
||||||
return None
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None:
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
return None
|
|
||||||
for i in range(StubMerger._sha1_store_count):
|
|
||||||
sidx: sizet = sizet(i) * 17
|
sidx: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
|
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
|
||||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||||
@@ -758,29 +563,12 @@ def LookupSha1RelPath(sha1: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# LookupModNameBySha1 - 通过 SHA1 反查模块名
|
# LookupModNameBySha1 - reverse lookup module name by SHA1 (read-only ptr, no free); first hit wins.
|
||||||
#
|
|
||||||
# 用途:判定"本 stub 是否某包的 __init__"(模块名以 ".__init__" 结尾)——该事实
|
|
||||||
# **不依赖 sha1→rel_path 字段**:rel_path 反查在部分运行形态下拿不到条目 ⇒
|
|
||||||
# VpiReader 原来的 `strstr(rel_path, "__init__.vp")` 判定恒 false ⇒ 包重导出类的
|
|
||||||
# 别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类**限定名在泛型实参里保留包前缀**,
|
|
||||||
# 与构造函数侧(真定义模块前缀)分裂成两个类型(现场 ClassGenGeneric.vp:92 的
|
|
||||||
# CastError;幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物 opaque)。
|
|
||||||
# 模块名与 rel_path 是同一次登记写入的(PopulateSha1MapStore /
|
|
||||||
# AppendToSha1MapStore),但模块名可用即足以作出判定与取包前缀。
|
|
||||||
#
|
|
||||||
# 同一 SHA1 可能有多条(App 源文件按全路径模块名与短模块名各登记一次):返回
|
|
||||||
# **第一条命中**;各条的包前缀一致,调用方只取"去 .__init__ 后的前缀"。
|
|
||||||
# Args: sha1 SHA1 字符串(16 字符)
|
|
||||||
# Returns: 模块名字符串指针(只读,不要 free),None=未找到
|
|
||||||
# ============================================================
|
|
||||||
def LookupModNameBySha1(sha1: str) -> str:
|
def LookupModNameBySha1(sha1: str) -> str:
|
||||||
"""通过 SHA1 查找模块名,返回只读指针,None=未找到"""
|
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
|
||||||
if sha1 is None or sha1[0] == '\0':
|
if sha1 is None or sha1[0] == '\0': return None
|
||||||
return None
|
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None:
|
for i in range(int(StubMerger._sha1_store_count)):
|
||||||
return None
|
|
||||||
for i in range(StubMerger._sha1_store_count):
|
|
||||||
sidx_mb: sizet = sizet(i) * 17
|
sidx_mb: sizet = sizet(i) * 17
|
||||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
||||||
midx_mb: sizet = sizet(i) * 64
|
midx_mb: sizet = sizet(i) * 64
|
||||||
|
|||||||
@@ -1,163 +1,116 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
|
||||||
import string
|
import string
|
||||||
import stdlib
|
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.Gen.Translator 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.StubMergerCollect as StubMergerCollect
|
||||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
|
||||||
import lib.Stub.StubMerger as StubMerger
|
|
||||||
|
|
||||||
# ============================================================
|
# StubMergerTopo - reverse-topological sort of reachable includes (dependencies first).
|
||||||
# StubMergerTopo - 对可达 includes 做逆拓扑排序(被依赖者先编译)
|
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||||
#
|
|
||||||
# 函数实现直接位于本模块,调用方经本模块前缀访问;全局变量跨模块经 StubMerger._xxx 访问。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# BuildTopoSortedIndices - reverse-topological sort of reachable includes (dependencies first).
|
||||||
# BuildTopoSortedIndices - 对可达 includes 逆拓扑排序(被依赖者先)
|
# Uses Kahn's algorithm; reachable_count <= 0 sorts all stores; cycles are appended in store order.
|
||||||
#
|
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_arr: bytes, reachable_set: bytes, reachable_count: int, sorted_idx: sizet*) -> int:
|
||||||
# 跨模块 vtable 生成要求被依赖模块先完整翻译(子类 vtable 槽位需父类方法的类型已注册到全局表)。
|
"""Reverse-topologically sort reachable includes (dependencies first); store indices go to sorted_idx."""
|
||||||
# 算法:Kahn 逆拓扑。边 m→d 表示 m 依赖 d(d 在 m 的 deps 中);入度为 0(不依赖任何可达模块)者先输出,
|
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: return 0
|
||||||
# 输出后对依赖它的模块减入度,入度归 0 者入队。reachable_count <= 0 时对全部 store 排序(与既有全量编译行为一致)。
|
indegree: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||||
#
|
queue: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||||
# Args: store_count / store_sha1_arr / store_mod_arr store 元数据;reachable_set / reachable_count 可达集合(_BuildReachableSha1Set 输出);sorted_idx 输出数组(store_count+1 个 8 字节槽),前 N 个为拓扑序 store 索引
|
rel_idx: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
|
||||||
# Returns: 拓扑序条数;有环时剩余条目按 store 顺序补齐(避免漏编译)
|
|
||||||
# ============================================================
|
|
||||||
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
|
|
||||||
store_mod_arr: bytes, reachable_set: bytes,
|
|
||||||
reachable_count: int,
|
|
||||||
sorted_idx: sizet*) -> 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: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
|
|
||||||
queue: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
|
|
||||||
rel_idx: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
|
|
||||||
if indegree is None or queue is None or rel_idx is None:
|
if indegree is None or queue is None or rel_idx is None:
|
||||||
if indegree is not None:
|
if indegree is not None: stdlib.free(indegree)
|
||||||
stdlib.free(indegree)
|
if queue is not None: stdlib.free(queue)
|
||||||
if queue is not None:
|
if rel_idx is not None: stdlib.free(rel_idx)
|
||||||
stdlib.free(queue)
|
|
||||||
if rel_idx is not None:
|
|
||||||
stdlib.free(rel_idx)
|
|
||||||
return 0
|
return 0
|
||||||
# 清零 indegree/queue/rel_idx(malloc 返回未初始化内存,Kahn 依赖入度初值 0)
|
# Zero indegree/queue/rel_idx (malloc memory is uninitialized)
|
||||||
zi: int = 0
|
zi: int = 0
|
||||||
for zi in range(store_count + 1):
|
for zi in range(int(store_count + 1)):
|
||||||
indegree[zi] = 0
|
indegree[zi] = 0
|
||||||
queue[zi] = 0
|
queue[zi] = 0
|
||||||
rel_idx[zi] = 0
|
rel_idx[zi] = 0
|
||||||
# 收集可达 store 索引(reachable_count<=0 时全量,对齐既有全量编译行为)
|
# Collect reachable store indices (all stores when reachable_count<=0)
|
||||||
rel_count: int = 0
|
rel_count: int = 0
|
||||||
i_t: int
|
i_t: int
|
||||||
if reachable_set is None or reachable_count <= 0:
|
if reachable_set is None or reachable_count <= 0:
|
||||||
for i_t in range(store_count):
|
for i_t in range(int(store_count)):
|
||||||
rel_idx[rel_count] = i_t
|
rel_idx[rel_count] = i_t
|
||||||
rel_count += 1
|
rel_count += 1
|
||||||
else:
|
else:
|
||||||
for i_t in range(store_count):
|
for i_t in range(int(store_count)):
|
||||||
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
|
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
|
||||||
rel_idx[rel_count] = i_t
|
rel_idx[rel_count] = i_t
|
||||||
rel_count += 1
|
rel_count += 1
|
||||||
|
|
||||||
# 计算入度:m 在可达集合内的直接依赖数
|
# Compute indegree: number of direct deps of m within the reachable set
|
||||||
for i_r in range(rel_count):
|
for i_r in range(int(rel_count)):
|
||||||
si: int = rel_idx[i_r]
|
si: int = int(rel_idx[i_r])
|
||||||
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
|
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
|
||||||
if deps_si is None:
|
if deps_si is None: continue
|
||||||
continue
|
|
||||||
dl_si: sizet = string.strlen(deps_si)
|
dl_si: sizet = string.strlen(deps_si)
|
||||||
dpos: sizet = 0
|
dpos: sizet = 0
|
||||||
while dpos < dl_si:
|
while dpos < dl_si:
|
||||||
dstart: sizet = dpos
|
dstart: sizet = dpos
|
||||||
while dpos < dl_si and deps_si[dpos] != ' ':
|
while dpos < dl_si and deps_si[dpos] != ' ': dpos += 1
|
||||||
dpos += 1
|
|
||||||
dlen: sizet = dpos - dstart
|
dlen: sizet = dpos - dstart
|
||||||
if dpos < dl_si:
|
if dpos < dl_si: dpos += 1
|
||||||
dpos += 1
|
if dlen == 0: continue
|
||||||
if dlen == 0:
|
|
||||||
continue
|
|
||||||
dname_buf: bytes = stdlib.malloc(dlen + 1)
|
dname_buf: bytes = stdlib.malloc(dlen + 1)
|
||||||
if dname_buf is None:
|
if dname_buf is None: continue
|
||||||
continue
|
|
||||||
string.strncpy(dname_buf, deps_si + dstart, dlen)
|
string.strncpy(dname_buf, deps_si + dstart, dlen)
|
||||||
dname_buf[dlen] = '\0'
|
dname_buf[dlen] = '\0'
|
||||||
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
|
dsha1: str = StubMergerIR._FindSha1ByModName(store_sha1_arr, store_mod_arr, store_count, dname_buf)
|
||||||
stdlib.free(dname_buf)
|
stdlib.free(dname_buf)
|
||||||
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
||||||
# 中间 int 变量累加:绕过联合类型(CSizeT|CPtr)+ 整数的指针算术编译 bug
|
# Use an intermediate int to work around a union-pointer arithmetic codegen bug
|
||||||
indg: int = indegree[si]
|
indg: int = int(indegree[si])
|
||||||
indg = indg + 1
|
indg = indg + 1
|
||||||
indegree[si] = indg
|
indegree[si] = indg
|
||||||
# Kahn:入度为 0(不依赖任何可达模块)者先出 → 最先编译
|
# Kahn: nodes with indegree 0 (no reachable deps) are output first
|
||||||
q_tail: int = 0
|
q_tail: int = 0
|
||||||
for i_r in range(rel_count):
|
for i_r in range(int(rel_count)):
|
||||||
si2: int = rel_idx[i_r]
|
si2: int = int(rel_idx[i_r])
|
||||||
if indegree[si2] == 0:
|
if indegree[si2] == 0:
|
||||||
queue[q_tail] = si2
|
queue[q_tail] = si2
|
||||||
q_tail += 1
|
q_tail += 1
|
||||||
out_count: int = 0
|
out_count: int = 0
|
||||||
# Kahn 主循环必须用 while 而非 for range:循环体内会动态入队(for range 上界只求值一次,
|
# The Kahn main loop must use while, not for range: the body enqueues dynamically,
|
||||||
# 动态入队的模块永远不会被处理,且 indegree>0 兜底也捞不回 → 拓扑漏项)
|
# and a for range bound is evaluated once, so newly queued modules would be skipped.
|
||||||
q_head: int = 0
|
q_head: int = 0
|
||||||
while q_head < q_tail:
|
while q_head < q_tail:
|
||||||
m: int = queue[q_head]
|
m: int = int(queue[q_head])
|
||||||
sorted_idx[out_count] = m
|
sorted_idx[out_count] = m
|
||||||
out_count += 1
|
out_count += 1
|
||||||
# 对依赖 m 的可达模块减入度
|
# Decrement indegree of reachable modules that depend on m
|
||||||
mod_m: str = store_mod_arr + sizet(m) * 64
|
mod_m: str = store_mod_arr + sizet(m) * 64
|
||||||
mod_m_len: sizet = string.strlen(mod_m)
|
mod_m_len: sizet = string.strlen(mod_m)
|
||||||
for i_r in range(rel_count):
|
for i_r in range(int(rel_count)):
|
||||||
si3: int = rel_idx[i_r]
|
si3: int = int(rel_idx[i_r])
|
||||||
if indegree[si3] == 0:
|
if indegree[si3] == 0: continue
|
||||||
continue
|
|
||||||
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
|
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
|
||||||
if deps_si3 is None:
|
if deps_si3 is None: continue
|
||||||
continue
|
|
||||||
dl_si3: sizet = string.strlen(deps_si3)
|
dl_si3: sizet = string.strlen(deps_si3)
|
||||||
dpos3: sizet = 0
|
dpos3: sizet = 0
|
||||||
found_dep: int = 0
|
found_dep: int = 0
|
||||||
while dpos3 < dl_si3 and found_dep == 0:
|
while dpos3 < dl_si3 and found_dep == 0:
|
||||||
dstart3: sizet = dpos3
|
dstart3: sizet = dpos3
|
||||||
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ':
|
while dpos3 < dl_si3 and deps_si3[dpos3] != ' ': dpos3 += 1
|
||||||
dpos3 += 1
|
|
||||||
dlen3: sizet = dpos3 - dstart3
|
dlen3: sizet = dpos3 - dstart3
|
||||||
if dpos3 < dl_si3:
|
if dpos3 < dl_si3: dpos3 += 1
|
||||||
dpos3 += 1
|
if dlen3 == 0: continue
|
||||||
if dlen3 == 0:
|
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1
|
||||||
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:
|
if found_dep == 1:
|
||||||
# 中间 int 变量递减:绕过联合类型指针算术编译 bug
|
# Use an intermediate int decrement to work around a union-pointer arithmetic codegen bug
|
||||||
indg3: int = indegree[si3]
|
indg3: int = int(indegree[si3])
|
||||||
indg3 = indg3 - 1
|
indg3 = indg3 - 1
|
||||||
indegree[si3] = indg3
|
indegree[si3] = indg3
|
||||||
if indegree[si3] == 0:
|
if indegree[si3] == 0:
|
||||||
queue[q_tail] = si3
|
queue[q_tail] = si3
|
||||||
q_tail += 1
|
q_tail += 1
|
||||||
q_head += 1
|
q_head += 1
|
||||||
# 有环时剩余条目按 store 顺序补齐(避免漏编译)
|
# On cycles, append remaining entries in store order to avoid skipping compilation
|
||||||
for i_r in range(rel_count):
|
for i_r in range(int(rel_count)):
|
||||||
si4: int = rel_idx[i_r]
|
si4: int = int(rel_idx[i_r])
|
||||||
if indegree[si4] > 0:
|
if indegree[si4] > 0:
|
||||||
sorted_idx[out_count] = si4
|
sorted_idx[out_count] = si4
|
||||||
out_count += 1
|
out_count += 1
|
||||||
|
|||||||
@@ -1,71 +1,42 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
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.Gen.Translator 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
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# StubMerger - merges dependency stub declarations into the main IR (Phase2 core):
|
||||||
# StubMerger - stub 声明合并模块(Phase2 核心)
|
# collect {sha1}.stub.ll from temp, dedup, then insert after the target triple so cross-module symbols link.
|
||||||
#
|
|
||||||
# 从 temp 目录加载所有 .stub.ll,把依赖模块的声明合并到主 IR 的 target triple 之后,使链接器能解析跨模块符号引用。
|
|
||||||
# 合并策略:扫描 temp 目录收集 {sha1}.stub.ll → 读取内容 → 过滤去重(避免重复声明)→ 插入主 IR。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# 全局 mbuddy 指针
|
# Global mbuddy pointer
|
||||||
_mbuddy: memhub.MemBuddy*
|
_mbuddy: memhub.MemBuddy*
|
||||||
|
|
||||||
# 最大 includes SHA1 数(用于过滤)
|
# Max includes SHA1 entries (for filtering)
|
||||||
MAX_INCLUDES_SHA1: t.CDefine = 1024
|
define MAX_INCLUDES_SHA1 = 1024
|
||||||
# stub 读取缓冲区大小
|
# Stub/text read buffer size. 1MB is too small: text.ll files exceed 1MB, so a single
|
||||||
# 1MB 不够:ast/parser.vp 的 text.ll >1MB,BuildCombinedIR 读取本地 text.ll
|
# truncated read drops trailing data (opt: "end of file in global variable name").
|
||||||
# 单次截断 → 合并 IR 尾部丢字(opt: "end of file in global variable name")。
|
define STUB_READ_BUF_SIZE = 8388608
|
||||||
STUB_READ_BUF_SIZE: t.CDefine = 8388608
|
# Max includes entries
|
||||||
# 最大 includes 条目数
|
define MAX_INCLUDES = 1024
|
||||||
MAX_INCLUDES: t.CDefine = 1024
|
# Reachability worklist/processed buffer size. 8192 is too small: after scanning
|
||||||
# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir
|
# source_dir it holds all App imports plus BFS expansion, overflowing on append 82.
|
||||||
# 后 worklist 含全部 App 文件 imports 的拼接 + BFS 依赖展开,追加82 溢出)
|
define REACH_WORKLIST_BUF = 65536
|
||||||
REACH_WORKLIST_BUF: t.CDefine = 65536
|
|
||||||
|
|
||||||
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
# Global SHA1 map store (in memory, no dependency on _sha1_map.txt).
|
||||||
# Phase1 扫描完成后填充,供所有函数使用
|
# Filled after the Phase1 scan and shared by all functions.
|
||||||
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
_sha1_store_arr: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||||
_sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个)
|
_sha1_store_mod: bytes # Module-name array (64 bytes each, MAX_INCLUDES entries)
|
||||||
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
|
_sha1_store_rel: bytes # Relative-path array (256 bytes each, MAX_INCLUDES entries)
|
||||||
_sha1_store_incdir: bytes # 所属 includes 根目录数组(每个 512 字节,MAX_INCLUDES 个,多 includes 支持)
|
_sha1_store_incdir: bytes # Owning includes-root array (512 bytes each, MAX_INCLUDES entries, multi-includes)
|
||||||
_sha1_store_count: int # 条目数
|
_sha1_store_count: int # Entry count
|
||||||
|
|
||||||
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
|
# In-memory includes dependency store (no dependency on the deps.txt file).
|
||||||
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
|
# Before Phase B+ all includes are translated in declare_only mode; _BuildReachableSha1Set reads deps from here.
|
||||||
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
|
_includes_deps_sha1: bytes # SHA1 array (17 bytes each, MAX_INCLUDES entries)
|
||||||
_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节,MAX_INCLUDES 个)
|
_includes_deps_str: bytes # Dependency-string array (512 bytes each, MAX_INCLUDES entries)
|
||||||
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节,MAX_INCLUDES 个)
|
_includes_deps_count: int # Entry count
|
||||||
_includes_deps_count: int # 条目数
|
|
||||||
|
|
||||||
# BuildCombinedIR 全目录扫描缓存:Phase B 每个源文件调用一次 BuildCombinedIR,而 _collect_stub_sha1s 会递归枚举
|
# Max length of a single dependency string
|
||||||
# temp_dir 全部切片子目录(O(N×S) 目录枚举很慢);按 temp_dir 缓存扫描结果,首次扫描后复用,temp_dir 变化时自动重建。
|
define MAX_DEPS_STR_LEN = 512
|
||||||
_stub_scan_cache_dir: str # 缓存对应的 temp_dir(None=未初始化)
|
|
||||||
_stub_scan_cache_arr: str # 扫描结果数组(每 17 字节一个 SHA1,MAX_INCLUDES_SHA1 个)
|
|
||||||
_stub_scan_cache_count: int # 收集数量
|
|
||||||
|
|
||||||
# 单条依赖字符串最大长度
|
# Max rel_path length
|
||||||
MAX_DEPS_STR_LEN: t.CDefine = 512
|
define MAX_REL_PATH_LEN = 256
|
||||||
|
# Max includes-root path length (absolute path, multi-includes)
|
||||||
# rel_path 最大长度
|
define MAX_INC_DIR_LEN = 512
|
||||||
MAX_REL_PATH_LEN: t.CDefine = 256
|
|
||||||
# includes 根目录最大长度(绝对路径,多 includes 支持)
|
|
||||||
MAX_INC_DIR_LEN: t.CDefine = 512
|
|
||||||
+3
-8
@@ -1,13 +1,8 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
|
||||||
import memhub
|
import memhub
|
||||||
import lib.core.VLogger as VLogger
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# lib package entry - declares the global _mbuddy memory-pool pointer.
|
||||||
# lib 包入口 - 声明全局 _mbuddy 内存池指针
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# 全局 mbuddy 内存池指针(由 App/main.vp 初始化后注入)
|
# Global mbuddy memory-pool pointer (injected by App/main.vp after init).
|
||||||
# 注意: 使用 MemBuddy 类型而非 MemManager,避免虚函数分派不工作时调用父类 alloc 返回 None
|
# Uses MemBuddy, not MemManager, to avoid returning None when virtual dispatch is unavailable.
|
||||||
_mbuddy: memhub.MemBuddy*
|
_mbuddy: memhub.MemBuddy*
|
||||||
@@ -1,23 +1,20 @@
|
|||||||
import t, c
|
|
||||||
import memhub
|
import memhub
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CompilerFactory - compiler struct factory (aligned with TPC Translator assembly).
|
||||||
# CompilerFactory - 编译器结构体工厂(对齐 TPC Translator 装配模式)
|
|
||||||
# ============================================================
|
|
||||||
novirtual class CompilerFactory:
|
novirtual class CompilerFactory:
|
||||||
"""编译器结构体工厂(持有主池/永久池等共享对象)"""
|
"""Compiler struct factory holding shared objects such as the main and permanent pools."""
|
||||||
Pool: memhub.MemBuddy* # 主内存池(瞬时数据,mark/reset 回收)
|
Pool: memhub.MemBuddy* # Main memory pool (transient data; reclaimed by mark/reset)
|
||||||
PermPool: memhub.MemBuddy* # 永久内存池(持久数据,从不 reset)
|
PermPool: memhub.MemBuddy* # Permanent memory pool (persistent data; never reset)
|
||||||
|
|
||||||
# ---- REnum 变体注册表(编译期单例持有,与 perm_pool 的 struct entry 同寿命)----
|
# ---- REnum variant registry (compile-time singleton; same lifetime as perm_pool struct entries)----
|
||||||
RenumClassNames: sizet*
|
RenumClassNames: sizet*
|
||||||
RenumClassVariantCounts: sizet*
|
RenumClassVariantCounts: sizet*
|
||||||
RenumVariantNames: sizet*
|
RenumVariantNames: sizet*
|
||||||
RenumVariantTags: sizet*
|
RenumVariantTags: sizet*
|
||||||
RenumClassCount: int
|
RenumClassCount: int
|
||||||
|
|
||||||
# ---- REnum 变体字段别名表(同上,必须同寿命;_bind_variant_fields 查字段真实类型)----
|
# ---- REnum variant field alias table (same lifetime; _bind_variant_fields resolves real field types)----
|
||||||
RenumAliasClass: sizet*
|
RenumAliasClass: sizet*
|
||||||
RenumAliasField: sizet*
|
RenumAliasField: sizet*
|
||||||
RenumAliasIndex: sizet*
|
RenumAliasIndex: sizet*
|
||||||
@@ -30,5 +27,5 @@ novirtual class CompilerFactory:
|
|||||||
self.Pool = pool
|
self.Pool = pool
|
||||||
self.PermPool = perm_pool
|
self.PermPool = perm_pool
|
||||||
|
|
||||||
# 全局工厂单例
|
# Global factory singleton
|
||||||
factory: CompilerFactory* = None
|
factory: CompilerFactory* = None
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
from stdint import *
|
||||||
|
import string
|
||||||
|
import stdio
|
||||||
|
import stdlib
|
||||||
|
import win32.fileio as fileio
|
||||||
|
import lib.core.VLogger as VLogger
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# Diag - 编译期诊断(traceback 系统):源码行读取 + 统一打印 + 终止策略
|
||||||
|
# ============================================================
|
||||||
|
# 抽出来的原因:格式化 / 源码行读取 / 终止策略原先都散在 TypeGenErr 里(连同它的
|
||||||
|
# TraceCount 调用栈遍历),导致"多错误栈"(错误收集 + 每单元去重 + 上限)无处安放。
|
||||||
|
# 现在 TypeGenErr / GenApi 的 `fatal_error` / `dump_parse_traceback` 全部**委托**到这里;
|
||||||
|
# 保留在 TypeGenErr 里的只有两件"它才知道"的东西:
|
||||||
|
# ① 调用栈帧的遍历(`TypeGenBase._get_trace_frame`);
|
||||||
|
# ② 当前文件名(`TypeGenResolve.get_current_file`)。
|
||||||
|
#
|
||||||
|
# **行为与抽离前逐字一致**:报文顺序、缩进、caret 规则、fflush、repl_abort 全不变。
|
||||||
|
# 诊断模型:**单条 + 立即终止**(不收集多条错误)—— 但单条自带**整条调用链**(DiagEntry),
|
||||||
|
# 由调用方(TypeGenErr)填好 entry 后 emit,方向由 DIAG_DIR_* 决定。所有打印都拆成独立的
|
||||||
|
# emit_*,便于在不改调用点的前提下调整格式。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
# ---- traceback 打印方向 ----
|
||||||
|
define DIAG_DIR_UP = 0 # 外→内:最外层触发点在上,错误点最后(Python "most recent call last")
|
||||||
|
define DIAG_DIR_DOWN = 1 # 内→外:错误点最先,越往外越下
|
||||||
|
define DIAG_MAX_FRAMES = 32 # 调用链上限(与 TypeGenMixin.TRACE_MAX 对齐)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# DiagEntry - 单条编译期诊断(= 一次 traceback 要打印的全部内容)
|
||||||
|
# ============================================================
|
||||||
|
# **不是**错误收集容器:任何时刻只处理"出错的那一条"(打印完即终止),
|
||||||
|
# 但这一条自带**整条调用链** —— 编译器逐层下潜进各源码上下文
|
||||||
|
# (import 触发依赖加载 / 泛型特化 / 函数体翻译)时的触发点,
|
||||||
|
# 用于"从错误发生点向外展示触发过程"。
|
||||||
|
# 所有字符串都用 stdlib 深拷贝:编译期池会回绕,不能借用池内指针。
|
||||||
|
struct DiagEntry:
|
||||||
|
# 字段名不要与"本模块 import 的类名"重名:TPC 的类型/符号表按**非限定名**索引
|
||||||
|
# (Gen.structs / SymbolTable / _mangle_func_name),曾把本字段的 `File` 与
|
||||||
|
# `win32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
|
||||||
|
# `File.__before_init__` 的 self 形参解析成 `i8**`(llvmlite: "Type of #1 arg
|
||||||
|
# mismatch: i8** != %"...fileio.File"*")。故此处用 FileName 而非 File。
|
||||||
|
FileName: str # 当前帧(错误点)文件
|
||||||
|
Line: int # 当前帧行号
|
||||||
|
ColStart: int # caret 起始列(1-based;0 = 不画)
|
||||||
|
ColEnd: int # caret 结束列
|
||||||
|
Category: str # LifetimeError / SyntaxError / TypeError ...
|
||||||
|
Msg: str # 消息
|
||||||
|
Hint: str # 可选提示(可 None)
|
||||||
|
FrameCount: int # 调用链帧数
|
||||||
|
FrameFiles: sizet* # 调用链每帧文件名(深拷贝)
|
||||||
|
FrameLines: sizet* # 调用链每帧行号(与 FrameFiles 同容量)
|
||||||
|
# 泄漏点(可选):被调者那边**造成逃逸的写入点**。只有逃逸类 LifetimeError 会有值;
|
||||||
|
# 打印成独立一帧 `File lifetime leak "<file>", line N`,与调用栈帧(`File "..."`)区分。
|
||||||
|
LeakFileName: str
|
||||||
|
LeakLine: int
|
||||||
|
Cap: int # 内部:FrameFiles/FrameLines 容量(不参与打印)
|
||||||
|
|
||||||
|
|
||||||
|
# dup_str - 深拷贝到 stdlib 堆(诊断需跨池回绕点存活)
|
||||||
|
def dup_str(s: str) -> str:
|
||||||
|
if s is None: return None
|
||||||
|
n: sizet = string.strlen(s)
|
||||||
|
b: str = str(stdlib.malloc(n + 1))
|
||||||
|
if b is None: return None
|
||||||
|
string.strncpy(b, s, n)
|
||||||
|
b[n] = '\0'
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
|
# new_entry - 分配一条诊断(调用方持有;打印完用 free_entry 释放)
|
||||||
|
def new_entry(max_frames: int) -> DiagEntry*:
|
||||||
|
ent: DiagEntry* = (DiagEntry*)(stdlib.malloc(DiagEntry.__sizeof__()))
|
||||||
|
if ent is None: return None
|
||||||
|
if max_frames <= 0: max_frames = 1
|
||||||
|
ent.FileName = None
|
||||||
|
ent.Line = 0
|
||||||
|
ent.ColStart = 0
|
||||||
|
ent.ColEnd = 0
|
||||||
|
ent.Category = None
|
||||||
|
ent.Msg = None
|
||||||
|
ent.Hint = None
|
||||||
|
ent.FrameCount = 0
|
||||||
|
ent.LeakFileName = None
|
||||||
|
ent.LeakLine = 0
|
||||||
|
ent.Cap = max_frames
|
||||||
|
ent.FrameFiles = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||||
|
ent.FrameLines = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||||
|
return ent
|
||||||
|
|
||||||
|
|
||||||
|
# begin - 初始化一条诊断(随后用 add_frame/set_current 填调用链与当前位置)
|
||||||
|
def begin(ent: DiagEntry*, category: str, msg: str, hint: str = None) -> int:
|
||||||
|
if ent is None: return 0
|
||||||
|
ent.Category = dup_str(category)
|
||||||
|
ent.Msg = dup_str(msg)
|
||||||
|
ent.Hint = dup_str(hint)
|
||||||
|
ent.FileName = None
|
||||||
|
ent.Line = 0
|
||||||
|
ent.ColStart = 0
|
||||||
|
ent.ColEnd = 0
|
||||||
|
ent.FrameCount = 0
|
||||||
|
ent.LeakFileName = None
|
||||||
|
ent.LeakLine = 0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# set_current - 设置当前帧(错误点)位置
|
||||||
|
def set_current(ent: DiagEntry*, file_name: str, lineno: int, col_start: int, col_end: int) -> int:
|
||||||
|
if ent is None: return 0
|
||||||
|
ent.FileName = dup_str(file_name)
|
||||||
|
ent.Line = lineno
|
||||||
|
ent.ColStart = col_start
|
||||||
|
ent.ColEnd = col_end
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# set_leak - 设置"泄漏点"帧(File lifetime leak);仅逃逸类 LifetimeError 使用
|
||||||
|
def set_leak(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||||
|
if ent is None or lineno <= 0: return 0
|
||||||
|
ent.LeakFileName = dup_str(file_name)
|
||||||
|
ent.LeakLine = lineno
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# add_frame - 追加调用链一帧(按"自外向内"顺序追加)
|
||||||
|
def add_frame(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||||
|
if ent is None or ent.FrameFiles is None or ent.FrameLines is None: return 0
|
||||||
|
if ent.FrameCount >= ent.Cap: return 0
|
||||||
|
ent.FrameFiles[ent.FrameCount] = sizet(dup_str(file_name))
|
||||||
|
ent.FrameLines[ent.FrameCount] = sizet(lineno)
|
||||||
|
ent.FrameCount += 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit - 打印一条诊断:头 → 调用链(按 direction) → 当前帧(caret) → 类别: 消息
|
||||||
|
# 去重:① 连续两个位置完全相同的栈帧只印一次;② 与“错误点帧”位置相同的栈帧不再重复印
|
||||||
|
#(此前常见 `_start` 定义行被打印两遍:栈内最内层帧与错误点帧落在同一行)。
|
||||||
|
def emit(ent: DiagEntry*, direction: int) -> int:
|
||||||
|
if ent is None: return 0
|
||||||
|
emit_header()
|
||||||
|
last_file: str = ""
|
||||||
|
last_line: int = -1
|
||||||
|
if ent.FrameFiles is not None and ent.FrameLines is not None:
|
||||||
|
if direction == DIAG_DIR_DOWN:
|
||||||
|
i_d: int = ent.FrameCount - 1
|
||||||
|
while i_d >= 0:
|
||||||
|
ff: str = str(ent.FrameFiles[i_d])
|
||||||
|
ll: int = int(ent.FrameLines[i_d])
|
||||||
|
dup: int = 1 if last_file != "" and string.strcmp(ff, last_file) == 0 and ll == last_line else 0
|
||||||
|
if dup == 0:
|
||||||
|
emit_frame(ff, ll)
|
||||||
|
last_file = ff
|
||||||
|
last_line = ll
|
||||||
|
i_d -= 1
|
||||||
|
else:
|
||||||
|
i_u: int = 0
|
||||||
|
while i_u < ent.FrameCount:
|
||||||
|
ff2: str = str(ent.FrameFiles[i_u])
|
||||||
|
ll2: int = int(ent.FrameLines[i_u])
|
||||||
|
dup2: int = 1 if last_file != "" and string.strcmp(ff2, last_file) == 0 and ll2 == last_line else 0
|
||||||
|
cur_same: int = 1 if ent.FileName is not None and string.strcmp(ff2, ent.FileName) == 0 and ll2 == ent.Line else 0
|
||||||
|
if dup2 == 0 and cur_same == 0:
|
||||||
|
emit_frame(ff2, ll2)
|
||||||
|
last_file = ff2
|
||||||
|
last_line = ll2
|
||||||
|
i_u += 1
|
||||||
|
emit_current(ent.FileName, ent.Line, ent.ColStart, ent.ColEnd)
|
||||||
|
# 泄漏点帧紧跟在"错误点"之后(往下再一行),再印类别: 消息
|
||||||
|
if ent.LeakLine > 0: emit_leak_frame(ent.LeakFileName, ent.LeakLine)
|
||||||
|
emit_category(ent.Category, ent.Msg)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# free_entry - 释放一条诊断(含深拷贝的字符串)
|
||||||
|
def free_entry(ent: DiagEntry*) -> int:
|
||||||
|
if ent is None: return 0
|
||||||
|
if ent.FileName is not None: stdlib.free(ent.FileName)
|
||||||
|
if ent.LeakFileName is not None: stdlib.free(ent.LeakFileName)
|
||||||
|
if ent.Category is not None: stdlib.free(ent.Category)
|
||||||
|
if ent.Msg is not None: stdlib.free(ent.Msg)
|
||||||
|
if ent.Hint is not None: stdlib.free(ent.Hint)
|
||||||
|
if ent.FrameFiles is not None:
|
||||||
|
i_f: int = 0
|
||||||
|
while i_f < ent.FrameCount:
|
||||||
|
if ent.FrameFiles[i_f] != 0: stdlib.free(str(ent.FrameFiles[i_f]))
|
||||||
|
i_f += 1
|
||||||
|
stdlib.free(ent.FrameFiles)
|
||||||
|
if ent.FrameLines is not None: stdlib.free(ent.FrameLines)
|
||||||
|
stdlib.free(ent)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# read_source_line - 读源文件第 lineno 行,供 traceback 展示
|
||||||
|
def read_source_line(file_path: str, lineno: int) -> str:
|
||||||
|
"""读第 lineno 行;返回 stdlib 分配的缓冲(调用方 free),读不到返回 None"""
|
||||||
|
if file_path is None or lineno <= 0: return None
|
||||||
|
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||||
|
if f.closed: return None
|
||||||
|
# Allocate by actual file size: a fixed 256KB buffer truncated large files so late line
|
||||||
|
# numbers were unreadable. The 64MB cap guards against reading abnormally huge files.
|
||||||
|
fsize: LONGLONG = f.size()
|
||||||
|
if fsize <= 0 or fsize > 67108864:
|
||||||
|
f.close()
|
||||||
|
return None
|
||||||
|
src_size: sizet = sizet(fsize)
|
||||||
|
buf: bytes = stdlib.malloc(src_size + 1)
|
||||||
|
if buf is None:
|
||||||
|
f.close()
|
||||||
|
return None
|
||||||
|
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
|
||||||
|
f.close()
|
||||||
|
if bytes_read <= 0:
|
||||||
|
stdlib.free(buf)
|
||||||
|
return None
|
||||||
|
# Scan line by line for the target lineno
|
||||||
|
cur_line: int = 1
|
||||||
|
line_start: sizet = 0
|
||||||
|
for i in range(int(bytes_read)):
|
||||||
|
if buf[i] == '\n':
|
||||||
|
if cur_line == lineno:
|
||||||
|
line_len: sizet = i - line_start
|
||||||
|
if line_len > 0 and buf[line_start + line_len - 1] == '\r': line_len -= 1
|
||||||
|
line_buf: bytes = stdlib.malloc(line_len + 1)
|
||||||
|
if line_buf is not None:
|
||||||
|
string.strncpy(line_buf, buf + line_start, line_len)
|
||||||
|
line_buf[line_len] = '\0'
|
||||||
|
stdlib.free(buf)
|
||||||
|
return line_buf
|
||||||
|
stdlib.free(buf)
|
||||||
|
return None
|
||||||
|
cur_line += 1
|
||||||
|
line_start = i + 1
|
||||||
|
stdlib.free(buf)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# emit_header - traceback 头(解析失败与语义错误共用同一行文案)
|
||||||
|
def emit_header() -> int:
|
||||||
|
stdio.printf("Traceback (most recent call last, compile-time error):\n")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit_frame - 调用栈里的一帧(只印位置与源码行,不印 caret)
|
||||||
|
def emit_frame(file_name: str, lineno: int) -> int:
|
||||||
|
if file_name is None: return 0
|
||||||
|
if lineno > 0:
|
||||||
|
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||||
|
src: str = read_source_line(file_name, lineno)
|
||||||
|
if src is not None:
|
||||||
|
stdio.printf(" %s\n", src)
|
||||||
|
stdlib.free(src)
|
||||||
|
else: stdio.printf(' File "%s"\n', file_name)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit_leak_frame - "泄漏点"帧:标签用 `File lifetime leak`,与调用栈帧(`File "..."`)区分
|
||||||
|
def emit_leak_frame(file_name: str, lineno: int) -> int:
|
||||||
|
if file_name is None: return 0
|
||||||
|
if lineno > 0:
|
||||||
|
stdio.printf(' File lifetime leak "%s", line %d\n', file_name, lineno)
|
||||||
|
src: str = read_source_line(file_name, lineno)
|
||||||
|
if src is not None:
|
||||||
|
stdio.printf(" %s\n", src)
|
||||||
|
stdlib.free(src)
|
||||||
|
else: stdio.printf(' File lifetime leak "%s"\n', file_name)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit_current - 当前帧(带 caret 列范围;行读不出时印警告)
|
||||||
|
def emit_current(file_name: str, lineno: int, col_start: int, col_end: int) -> int:
|
||||||
|
# Sanity defense: read the line first to verify lineno belongs to the current file.
|
||||||
|
# If unreadable the file/line is untrustworthy (e.g. reused AST); print a warning instead.
|
||||||
|
src_line: str = None
|
||||||
|
line_ok: int = 1
|
||||||
|
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
||||||
|
src_line = read_source_line(file_name, lineno)
|
||||||
|
if src_line is None: line_ok = 0
|
||||||
|
if line_ok == 0:
|
||||||
|
stdio.printf(' File "%s", line %d (!! cannot read that source line: file does not exist or line number out of range; the error may come from a reused AST tree, rely on the error message)\n', file_name, lineno)
|
||||||
|
else:
|
||||||
|
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||||
|
if src_line is not None:
|
||||||
|
stdio.printf(" %s\n", src_line)
|
||||||
|
# Column-range marker: use ^ under the source line to mark the error range
|
||||||
|
if col_start > 0 and col_end >= col_start:
|
||||||
|
stdio.printf(" ")
|
||||||
|
j: int
|
||||||
|
for j in range(int(col_end)):
|
||||||
|
if j >= col_start - 1 and j < col_end: stdio.printf("^")
|
||||||
|
else: stdio.printf(" ")
|
||||||
|
stdio.printf("\n")
|
||||||
|
stdlib.free(src_line)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit_category - `类别: 消息`,随后立即 fflush
|
||||||
|
def emit_category(err_type: str, msg: str) -> int:
|
||||||
|
if err_type is None or err_type[0] == 0: err_type = "SyntaxError"
|
||||||
|
stdio.printf("%s: %s\n", err_type, msg)
|
||||||
|
# Force flush before exiting: stdout redirected to a file is fully buffered, so terminating
|
||||||
|
# directly drops the whole traceback (observed: 2.log kept only one VLogger line).
|
||||||
|
stdio.fflush(None)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# emit_parse - 解析失败的单帧 traceback(Python 风格,单个 ^ 指向 token 起始列)
|
||||||
|
def emit_parse(file_path: str, err_line: int, err_col: int, err_msg: str) -> int:
|
||||||
|
emit_header()
|
||||||
|
if file_path is None or file_path[0] == 0: file_path = "<unknown>"
|
||||||
|
if err_line > 0:
|
||||||
|
stdio.printf(' File "%s", line %d\n', file_path, err_line)
|
||||||
|
src: str = read_source_line(file_path, err_line)
|
||||||
|
if src is not None:
|
||||||
|
stdio.printf(" %s\n", src)
|
||||||
|
# Column marker: put ^ under the error token's start column (parser col_offset is
|
||||||
|
# 0-based, so 4-space indent + err_col spaces lands on the token's first char)
|
||||||
|
if err_col > 0:
|
||||||
|
stdio.printf(" ")
|
||||||
|
for _ in range(int(err_col)): stdio.printf(" ")
|
||||||
|
stdio.printf("^\n")
|
||||||
|
stdlib.free(src)
|
||||||
|
else: stdio.printf(' File "%s"\n', file_path)
|
||||||
|
if err_msg is None or err_msg[0] == 0: err_msg = "syntax error"
|
||||||
|
stdio.printf("SyntaxError: %s\n", err_msg)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# finish - 终止策略:REPL 里 longjmp 回循环(报文已印、进程存活),批处理里 sys.exit(1)
|
||||||
|
def finish() -> None:
|
||||||
|
VLogger.repl_abort()
|
||||||
+3800
-3602
File diff suppressed because it is too large.
Load diff
+2097
-2659
File diff suppressed because it is too large.
Load diff
@@ -1,39 +1,35 @@
|
|||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import lib.core.Gen.Generator as Generator
|
import lib.core.Gen.Generator as Generator
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import memhub
|
import memhub
|
||||||
import llvmlite
|
import mlirlite
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# BaseGenMixin - generator base (state container).
|
||||||
# BaseGenMixin - 生成器基座(状态容器)
|
|
||||||
# ============================================================
|
|
||||||
novirtual class BaseGenMixin:
|
novirtual class BaseGenMixin:
|
||||||
"""生成器状态容器(基座)"""
|
"""Generator state container (base)."""
|
||||||
|
|
||||||
# 内存/IR 状态
|
# Memory / IR state
|
||||||
Gen: Generator.Generator*
|
Gen: Generator.Generator*
|
||||||
Pool: memhub.MemBuddy* # 内存池
|
Pool: memhub.MemBuddy* # Memory pool
|
||||||
Module: llvmlite.LLVMModule* # 当前 LLVM 模块
|
Module: mlirlite.LLVMModule* # Current LLVM module
|
||||||
Builder: llvmlite.IRBuilder* # 当前 IRBuilder
|
Builder: mlirlite.IRBuilder* # Current IRBuilder
|
||||||
# 符号表/函数表
|
# Symbol / function tables
|
||||||
SymTab: rawptr[GenTypes.SymbolTable]
|
SymTab: GenTypes.SymbolTable*
|
||||||
Funcs: GenTypes.FuncEntry*
|
Funcs: GenTypes.FuncEntry*
|
||||||
FuncCount: int
|
FuncCount: int
|
||||||
# 导入/模块信息
|
# Import / module info
|
||||||
ImportedModules: str
|
ImportedModules: str
|
||||||
FromImports: str
|
FromImports: str
|
||||||
ModuleSha1: str
|
ModuleSha1: str
|
||||||
# 当前翻译上下文
|
# Current translation context
|
||||||
CurFunc: llvmlite.Function*
|
CurFunc: mlirlite.Function*
|
||||||
CurBuilder: llvmlite.IRBuilder*
|
CurBuilder: mlirlite.IRBuilder*
|
||||||
CurClassName: str
|
CurClassName: str
|
||||||
|
|
||||||
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
|
def InitBaseGen(self, pool: memhub.MemBuddy*) -> int:
|
||||||
"""初始化生成器状态(默认值;子类构造时调用)"""
|
"""Initialize generator state to defaults; called from subclass constructors."""
|
||||||
if self is None:
|
if self is None: return 1
|
||||||
return 1
|
|
||||||
self.Pool = pool
|
self.Pool = pool
|
||||||
self.Module = None
|
self.Module = None
|
||||||
self.Builder = None
|
self.Builder = None
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+921
-1227
File diff suppressed because it is too large.
Load diff
+1242
-1273
File diff suppressed because it is too large.
Load diff
+1417
-1818
File diff suppressed because it is too large.
Load diff
+117
-167
@@ -1,8 +1,7 @@
|
|||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import viperlib
|
import viperlib
|
||||||
@@ -12,107 +11,91 @@ import lib.core.Gen.GenApi as GenApi
|
|||||||
from . import CallGenBase
|
from . import CallGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CallGenFd - split from CallGenMixin (stage B): FakeDuck / requires field injection / provider stack
|
||||||
# CallGenFd - CallGenMixin 拆分(阶段 B):FakeDuck / requires 字段注入 / provider 栈
|
|
||||||
# ============================================================
|
|
||||||
novirtual class CallGenFd(CallGenBase.CallGenBase):
|
novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||||
"""FakeDuck / requires 字段注入 / provider 栈"""
|
"""FakeDuck / requires field injection / provider stack"""
|
||||||
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*,
|
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""FakeDuck: resolve a str variable's method call to extension class _Type/_str, return the call result or None"""
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
cls_name: str,
|
|
||||||
method_name: str,
|
|
||||||
cl: ast.Call*,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None"""
|
|
||||||
if pool is None or builder is None or cl is None or trans is None: return None
|
if pool is None or builder is None or cl is None or trans is None: return None
|
||||||
if method_name is None: return None
|
if method_name is None: return None
|
||||||
# 仅对 str 类型触发(AnnotClassName == "str",对象是 char*);
|
# Only trigger for str (AnnotClassName == "str", object is char*);
|
||||||
# cls_name 为 None(表达式接收者解析失败)时也尝试
|
# also try when cls_name is None (receiver resolution failed).
|
||||||
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
|
if cls_name is not None and string.strcmp(cls_name, "str") != 0: return None
|
||||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
|
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return None
|
||||||
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
|
fd_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||||
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
|
if fd_at is None or fd_at.value is None or fd_at.attr is None: return None
|
||||||
if string.strcmp(fd_at.attr, method_name) != 0: return None
|
if string.strcmp(fd_at.attr, method_name) != 0: return None
|
||||||
# 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等)
|
# Candidate extension classes: _Type first (a.type_id()), then _str (a.upper() etc.)
|
||||||
fd_func: llvmlite.Function* = None
|
fd_func: mlirlite.Function* = None
|
||||||
fd_cls: str = None
|
fd_cls: str = None
|
||||||
fd_key: str = pool.alloc(160)
|
fd_key: str = pool.alloc(160)
|
||||||
if fd_key is not None:
|
if fd_key is not None:
|
||||||
for fd_ci in range(2):
|
for fd_ci in range(2):
|
||||||
fd_cn: str = "_Type"
|
fd_cn: str = "_Type"
|
||||||
if fd_ci == 1:
|
if fd_ci == 1: fd_cn = "_str"
|
||||||
fd_cn = "_str"
|
|
||||||
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
|
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
|
||||||
fd_func = self.find_func_in_module(mod, fd_key)
|
fd_func = self.find_func_in_module(mod, fd_key)
|
||||||
if fd_func is not None:
|
if fd_func is not None:
|
||||||
fd_cls = fd_cn
|
fd_cls = fd_cn
|
||||||
break
|
break
|
||||||
if fd_func is None or fd_cls is None: return None
|
if fd_func is None or fd_cls is None: return None
|
||||||
# 扩展类结构体必须已注册(GEP/临时影子需要类型)
|
# The extension class struct must be registered (GEP/temp shadow needs a type)
|
||||||
fd_sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
|
fd_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(fd_cls)
|
||||||
if fd_sty is None: return None
|
if fd_sty is None: return None
|
||||||
# 扩展类字段数:有字段(_str)时 self 用影子结构体指针;
|
# Extension class field count: with fields (_str) self uses a shadow struct pointer;
|
||||||
# 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876)
|
# without fields (_Type) self bitcasts the object value to the extension pointer (matches TPC L2856-2876).
|
||||||
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
|
fd_entry_fd: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(fd_sty)
|
||||||
fd_has_fields: int = 0
|
fd_has_fields: int = 0
|
||||||
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0:
|
if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0: fd_has_fields = 1
|
||||||
fd_has_fields = 1
|
# self arg: prefer an existing shadow {var}__meta__ (created by fd2, only when the extension class has fields)
|
||||||
# self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时)
|
fd_self: mlirlite.Value* = None
|
||||||
fd_self: llvmlite.Value* = None
|
|
||||||
if fd_at.value.kind() == ast.ASTKind.Name:
|
if fd_at.value.kind() == ast.ASTKind.Name:
|
||||||
fd_onm: ast.Name* = ast.Name*(fd_at.value)
|
fd_onm: ast.Name* = ast.Name*(fd_at.value)
|
||||||
if fd_onm.id is not None:
|
if fd_onm.id is not None:
|
||||||
# pool 持久缓冲(f-string 栈缓冲在函数返回后失效)
|
# Pool-persistent buffer (an f-string stack buffer would expire after return)
|
||||||
fd_mn: str = pool.alloc(256)
|
fd_mn: str = pool.alloc(256)
|
||||||
if fd_mn is not None:
|
if fd_mn is not None: viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
|
||||||
viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
|
fd_meta: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
|
||||||
fd_meta: llvmlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
|
if fd_meta is not None and fd_has_fields != 0: fd_self = fd_meta
|
||||||
if fd_meta is not None and fd_has_fields != 0:
|
|
||||||
fd_self = fd_meta
|
|
||||||
if fd_self is None:
|
if fd_self is None:
|
||||||
if fd_has_fields != 0:
|
if fd_has_fields != 0:
|
||||||
# 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化
|
# Fielded extension class with no shadow (string literal etc.): create and init a temp
|
||||||
# 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized)
|
# shadow at entry; first ensure full _str definition (cross-module structs default to opaque and alloca would report unsized).
|
||||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
|
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, fd_sty)
|
||||||
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
|
fd_self = self.Gen.VarGen._alloca_at_entry(builder, fd_sty)
|
||||||
if fd_self is None: return None
|
if fd_self is None: return None
|
||||||
fd_obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
fd_obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
||||||
if fd_obj_val is not None:
|
if fd_obj_val is not None:
|
||||||
fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__")
|
fd_dfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__data__")
|
||||||
if fd_dfe is not None and fd_dfe.Ty is not None:
|
if fd_dfe is not None and fd_dfe.Ty is not None:
|
||||||
fd_dp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
|
fd_dp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index)
|
||||||
if fd_dp is not None:
|
if fd_dp is not None: mlirlite.build_store(builder, fd_obj_val, fd_dp)
|
||||||
llvmlite.build_store(builder, fd_obj_val, fd_dp)
|
|
||||||
fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
|
fd_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(fd_sty, "__mbuddy__")
|
||||||
if fd_bfe is not None and fd_bfe.Ty is not None:
|
if fd_bfe is not None and fd_bfe.Ty is not None:
|
||||||
fd_bp: llvmlite.Value* = llvmlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
|
fd_bp: mlirlite.Value* = mlirlite.build_gep_struct(builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index)
|
||||||
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, fd_bfe.Ty, "null")
|
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, fd_bfe.Ty, "null")
|
||||||
if fd_bp is not None and fd_null is not None:
|
if fd_bp is not None and fd_null is not None: mlirlite.build_store(builder, fd_null, fd_bp)
|
||||||
llvmlite.build_store(builder, fd_null, fd_bp)
|
# FakeDuck: auto-inject requires fields from the with context (e.g. __mbuddy__)
|
||||||
# FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)
|
|
||||||
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
|
self._inject_requires_fields(trans, pool, builder, mod, fd_self, fd_cls)
|
||||||
else:
|
else:
|
||||||
# 无字段扩展类(_Type):对象值 bitcast 到扩展类指针
|
# Fieldless extension class (_Type): bitcast the object value to the extension pointer
|
||||||
fd_obj_v: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
fd_obj_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_at.value, None, 0, trans)
|
||||||
if fd_obj_v is None: return None
|
if fd_obj_v is None: return None
|
||||||
fd_self = llvmlite.build_bitcast(builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty))
|
fd_self = mlirlite.build_bitcast(builder, fd_obj_v, mlirlite.Ptr(pool, fd_sty))
|
||||||
if fd_self is None: return None
|
if fd_self is None: return None
|
||||||
# 构建参数链表: self → 其余参数
|
# Build the argument list: self -> remaining args
|
||||||
llvmlite.value_set_next(fd_self, None)
|
mlirlite.value_set_next(fd_self, None)
|
||||||
fd_head: llvmlite.Value* = fd_self
|
fd_head: mlirlite.Value* = fd_self
|
||||||
fd_tail: llvmlite.Value* = fd_self
|
fd_tail: mlirlite.Value* = fd_self
|
||||||
fd_args: list[ast.AST*]* = cl.args
|
fd_args: list[ast.AST*]* = cl.args
|
||||||
fd_can: int = 0
|
fd_can: int = 0
|
||||||
if fd_args is not None:
|
if fd_args is not None: fd_can = int(len(fd_args))
|
||||||
fd_can = len(fd_args)
|
for fd_ai in range(int(fd_can)):
|
||||||
for fd_ai in range(fd_can):
|
|
||||||
fd_arg: ast.AST* = fd_args.get(fd_ai)
|
fd_arg: ast.AST* = fd_args.get(fd_ai)
|
||||||
if fd_arg is not None:
|
if fd_arg is not None:
|
||||||
fd_av: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
|
fd_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
|
||||||
# char 常量参数 → 单字符字符串(char 隐式转 str,
|
# char constant arg -> single-char string (implicit char->str, e.g. s1.split(',') where
|
||||||
# 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化,
|
# ',' is i32 44); passing it raw to a str param makes strlen/strncmp deref 44 and crash.
|
||||||
# 否则运行时 strlen/strncmp 解引用 44 崩溃)
|
|
||||||
if fd_arg.kind() == ast.ASTKind.Constant:
|
if fd_arg.kind() == ast.ASTKind.Constant:
|
||||||
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
|
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
|
||||||
if fd_ac.const_kind == ast.CONST_CHAR:
|
if fd_ac.const_kind == ast.CONST_CHAR:
|
||||||
@@ -122,175 +105,142 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
|||||||
ch_buf[1] = '\0'
|
ch_buf[1] = '\0'
|
||||||
fd_av = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, ch_buf, trans)
|
fd_av = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, ch_buf, trans)
|
||||||
if fd_av is not None:
|
if fd_av is not None:
|
||||||
llvmlite.value_set_next(fd_av, None)
|
mlirlite.value_set_next(fd_av, None)
|
||||||
llvmlite.value_set_next(fd_tail, fd_av)
|
mlirlite.value_set_next(fd_tail, fd_av)
|
||||||
fd_tail = fd_av
|
fd_tail = fd_av
|
||||||
# 返回类型与调用名(来自 find_func_in_module 命中的函数定义)
|
# Return type and call name (from the function definition hit by find_func_in_module)
|
||||||
fd_ret_ty: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(fd_func)
|
fd_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fd_func)
|
||||||
if fd_ret_ty is None:
|
if fd_ret_ty is None: fd_ret_ty = mlirlite.Int32(pool)
|
||||||
fd_ret_ty = llvmlite.Int32(pool)
|
fd_fname: str = mlirlite.function_get_name(fd_func)
|
||||||
fd_fname: str = llvmlite.function_get_name(fd_func)
|
|
||||||
if fd_fname is None: return None
|
if fd_fname is None: return None
|
||||||
# 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标路径而非 list 内联 __getitem__(printf %s 崩溃)。从定义模块 text.ll 读取精确返回类型;仅当函数名确为 "{16hex}.{func}"(判定走唯一真源 GenApi.sha1_prefix_len)才提取模块 sha1
|
# Refine return type: cross-module declare may simplify a list[T] specialization return type to Ptr(i8), degrading the caller's alloca type -> parts[0] takes the char-index path instead of list inline __getitem__ (printf %s crash). Authority is the defining module's vpi (fallback text.ll); extract the module sha1 only when the name is "{16hex}.{func}" (via GenApi.sha1_prefix_len)
|
||||||
if GenApi.sha1_prefix_len(fd_fname) != 0:
|
if GenApi.sha1_prefix_len(fd_fname) != 0:
|
||||||
fd_sha1: str = pool.alloc(17)
|
fd_sha1: str = pool.alloc(17)
|
||||||
if fd_sha1 is not None:
|
if fd_sha1 is not None:
|
||||||
string.strncpy(fd_sha1, fd_fname, 16)
|
string.strncpy(fd_sha1, fd_fname, 16)
|
||||||
fd_sha1[16] = '\0'
|
fd_sha1[16] = '\0'
|
||||||
fd_exact: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
|
fd_exact: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, fd_sha1, fd_cls, method_name)
|
||||||
if fd_exact is not None:
|
if fd_exact is None: fd_exact = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
|
||||||
fd_ret_ty = fd_exact
|
if fd_exact is not None: fd_ret_ty = fd_exact
|
||||||
fd_total: int = 1 + fd_can
|
fd_total: int = 1 + fd_can
|
||||||
return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
# Value-reference (&T / &temp T) params of the resolved extension method: auto-address the
|
||||||
# ============================================================
|
# user args (the shadow self pointer occupies slot 0).
|
||||||
# _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段
|
fd_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, fd_fname, fd_args, fd_head, fd_total, 1)
|
||||||
#
|
return mlirlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
||||||
# 对齐 TPC HandlesExprCall.py L4021-4079:
|
# _inject_requires_fields - FakeDuck: inject requires fields from the with context.
|
||||||
# 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用
|
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
|
||||||
# _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时
|
|
||||||
# bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖
|
|
||||||
# 用户已手动设置的值)。_find_provider 未注册(未 import
|
|
||||||
# _withcontent)时跳过注入,不影响非 with 场景。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
def _inject_requires_fields(self, trans: HT.Translator*,
|
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: str) -> int:
|
||||||
pool: memhub.MemBuddy*,
|
"""Inject requires fields from the with context, return the injected field count"""
|
||||||
builder: llvmlite.IRBuilder*,
|
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||||
mod: llvmlite.LLVMModule*,
|
if meta_ptr is None or class_name is None: return 0
|
||||||
meta_ptr: llvmlite.Value*,
|
# _withcontent._find_provider (cross-module): inject only when visible in this module
|
||||||
class_name: str) -> int:
|
# (matches TPC L4048-4050); otherwise non-with projects would emit undefined {sha1}._find_provider calls.
|
||||||
"""在 with 上下文注入 requires 字段,返回注入字段数"""
|
fd_fn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_find_provider")
|
||||||
if trans is None or pool is None or builder is None or mod is None:
|
|
||||||
return 0
|
|
||||||
if meta_ptr is None or class_name is None:
|
|
||||||
return 0
|
|
||||||
# _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent
|
|
||||||
# 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050
|
|
||||||
# (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with
|
|
||||||
# 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...)
|
|
||||||
# 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。
|
|
||||||
fd_fn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_find_provider")
|
|
||||||
if fd_fn_probe is None: return 0
|
if fd_fn_probe is None: return 0
|
||||||
fd_fname: str = llvmlite.function_get_name(fd_fn_probe)
|
fd_fname: str = mlirlite.function_get_name(fd_fn_probe)
|
||||||
if fd_fname is None: return 0
|
if fd_fname is None: return 0
|
||||||
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
|
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
|
||||||
if cnt <= 0: return 0
|
if cnt <= 0: return 0
|
||||||
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
||||||
if sty is None: return 0
|
if sty is None: return 0
|
||||||
i8_ty_j: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty_j: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
if i8_ty_j is None: return 0
|
if i8_ty_j is None: return 0
|
||||||
i8_ptr_ty_j: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_j)
|
i8_ptr_ty_j: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_j)
|
||||||
if i8_ptr_ty_j is None: return 0
|
if i8_ptr_ty_j is None: return 0
|
||||||
cur_func: llvmlite.Function* = trans._cur_func
|
cur_func: mlirlite.Function* = trans._cur_func
|
||||||
if cur_func is None: return 0
|
if cur_func is None: return 0
|
||||||
injected: int = 0
|
injected: int = 0
|
||||||
for ji in range(cnt):
|
for ji in range(int(cnt)):
|
||||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
|
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
|
||||||
if fname is None: continue
|
if fname is None: continue
|
||||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
|
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
|
||||||
if fe is None or fe.Ty is None: continue
|
if fe is None or fe.Ty is None: continue
|
||||||
# 参数:字段名字符串(i8*)
|
# Arg: field name string (i8*)
|
||||||
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||||
if fd_fstr is None: continue
|
if fd_fstr is None: continue
|
||||||
llvmlite.value_set_next(fd_fstr, None)
|
mlirlite.value_set_next(fd_fstr, None)
|
||||||
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
|
self._ensure_method_declare(pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1)
|
||||||
fd_ptr: llvmlite.Value* = llvmlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
|
fd_ptr: mlirlite.Value* = mlirlite.build_call(builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0)
|
||||||
if fd_ptr is None: continue
|
if fd_ptr is None: continue
|
||||||
# 条件注入:ptr != NULL 才写入(保留用户手动设置的值)
|
# Conditional inject: store only when ptr != NULL (preserve user-set values)
|
||||||
fd_null: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty_j, "null")
|
fd_null: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty_j, "null")
|
||||||
if fd_null is None: continue
|
if fd_null is None: continue
|
||||||
fd_cmp: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, fd_ptr, fd_null)
|
fd_cmp: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, fd_ptr, fd_null)
|
||||||
if fd_cmp is None: continue
|
if fd_cmp is None: continue
|
||||||
# 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中
|
# Unique block names (create_block does not auto-number; duplicate labels in textual IR
|
||||||
# 冲突 → llc "Terminator found in the middle of a basic block")
|
# conflict -> llc "Terminator found in the middle of a basic block").
|
||||||
vtc2: int = trans._label_counter
|
vtc2: int = trans._label_counter
|
||||||
trans._label_counter = vtc2 + 1
|
trans._label_counter = vtc2 + 1
|
||||||
fd_bb_buf2: str = pool.alloc(64)
|
fd_bb_buf2: str = pool.alloc(64)
|
||||||
if fd_bb_buf2 is None: continue
|
if fd_bb_buf2 is None: continue
|
||||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
|
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
|
||||||
fd_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
|
fd_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
|
||||||
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
|
viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2)
|
||||||
fd_inject_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, cur_func, fd_bb_buf2)
|
fd_inject_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
|
||||||
if fd_skip_bb is None or fd_inject_bb is None: continue
|
if fd_skip_bb is None or fd_inject_bb is None: continue
|
||||||
llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
|
mlirlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb)
|
||||||
# inject 块:bitcast + GEP + store,跳回 skip
|
# inject block: bitcast + GEP + store, then jump back to skip
|
||||||
llvmlite.position_at_end(builder, fd_inject_bb)
|
mlirlite.position_at_end(builder, fd_inject_bb)
|
||||||
fd_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, fd_ptr, fe.Ty)
|
fd_cast: mlirlite.Value* = mlirlite.build_bitcast(builder, fd_ptr, fe.Ty)
|
||||||
if fd_cast is not None:
|
if fd_cast is not None:
|
||||||
fd_fp: llvmlite.Value* = llvmlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
|
fd_fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, sty, fe.Ty, meta_ptr, fe.Index)
|
||||||
if fd_fp is not None: llvmlite.build_store(builder, fd_cast, fd_fp)
|
if fd_fp is not None: mlirlite.build_store(builder, fd_cast, fd_fp)
|
||||||
llvmlite.build_br(builder, fd_skip_bb)
|
mlirlite.build_br(builder, fd_skip_bb)
|
||||||
# skip 块:恢复 CurBlock,继续后续代码生成
|
# skip block: restore CurBlock and continue codegen
|
||||||
llvmlite.position_at_end(builder, fd_skip_bb)
|
mlirlite.position_at_end(builder, fd_skip_bb)
|
||||||
injected += 1
|
injected += 1
|
||||||
return injected
|
return injected
|
||||||
# ============================================================
|
# _emit_provider_push / _emit_provider_pop - FakeDuck: with-context provider push/pop (__provides__ metadata).
|
||||||
# _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文
|
# Matches TPC HandlesWith.py L138-174: push _push_provider(field, ctx_i8) on entry and equal _pop_provider() on exit.
|
||||||
# provider push/pop(__provides__ 元数据,fd1 注册)
|
|
||||||
#
|
|
||||||
# 对齐 TPC HandlesWith.py L138-174:
|
|
||||||
# with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8);
|
|
||||||
# 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责
|
|
||||||
# 维护 TLS provider 栈,供 _inject_requires_fields 查找。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
def _emit_provider_push(self, trans: HT.Translator*,
|
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, ctx_val: mlirlite.Value*) -> int:
|
||||||
pool: memhub.MemBuddy*,
|
"""Emit a _push_provider(field, ctx_i8) call per provided field, return the field count"""
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
cls_name: str,
|
|
||||||
ctx_val: llvmlite.Value*) -> int:
|
|
||||||
"""生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数"""
|
|
||||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||||
if cls_name is None or ctx_val is None: return 0
|
if cls_name is None or ctx_val is None: return 0
|
||||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||||
if fd_pc <= 0: return 0
|
if fd_pc <= 0: return 0
|
||||||
# _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫:
|
# _withcontent._push_provider (cross-module, same visibility guard as _find_provider:
|
||||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||||
fd_pfn_probe: llvmlite.Function* = self.find_func_in_module(mod, "_push_provider")
|
fd_pfn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_push_provider")
|
||||||
if fd_pfn_probe is None: return 0
|
if fd_pfn_probe is None: return 0
|
||||||
fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe)
|
fd_pfn: str = mlirlite.function_get_name(fd_pfn_probe)
|
||||||
if fd_pfn is None: return 0
|
if fd_pfn is None: return 0
|
||||||
i8_ty_p: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty_p: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
if i8_ty_p is None: return 0
|
if i8_ty_p is None: return 0
|
||||||
i8_ptr_ty_p: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_p)
|
i8_ptr_ty_p: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_p)
|
||||||
if i8_ptr_ty_p is None: return 0
|
if i8_ptr_ty_p is None: return 0
|
||||||
ret_i32_p: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
ret_i32_p: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
if ret_i32_p is None: return 0
|
if ret_i32_p is None: return 0
|
||||||
# ctx 值 bitcast 到 i8*(provider 栈存 void*)
|
# Bitcast the ctx value to i8* (provider stack stores void*)
|
||||||
fd_ctx_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
|
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
|
||||||
if fd_ctx_i8 is None: return 0
|
if fd_ctx_i8 is None: return 0
|
||||||
for pi in range(fd_pc):
|
for pi in range(int(fd_pc)):
|
||||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
|
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
|
||||||
if fname is None: continue
|
if fname is None: continue
|
||||||
fd_fstr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||||
if fd_fstr is None: continue
|
if fd_fstr is None: continue
|
||||||
llvmlite.value_set_next(fd_fstr, fd_ctx_i8)
|
mlirlite.value_set_next(fd_fstr, fd_ctx_i8)
|
||||||
llvmlite.value_set_next(fd_ctx_i8, None)
|
mlirlite.value_set_next(fd_ctx_i8, None)
|
||||||
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
|
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
|
||||||
llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
|
mlirlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
|
||||||
return fd_pc
|
return fd_pc
|
||||||
def _emit_provider_pop(self, trans: HT.Translator*,
|
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str) -> int:
|
||||||
pool: memhub.MemBuddy*,
|
"""Emit a _pop_provider() call per provided field, return the field count"""
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
cls_name: str) -> int:
|
|
||||||
"""生成 _pop_provider() 调用(每提供字段一次),返回字段数"""
|
|
||||||
if trans is None or pool is None or builder is None or mod is None: return 0
|
if trans is None or pool is None or builder is None or mod is None: return 0
|
||||||
if cls_name is None: return 0
|
if cls_name is None: return 0
|
||||||
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
fd_pc: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_PROVIDES, cls_name)
|
||||||
if fd_pc <= 0: return 0
|
if fd_pc <= 0: return 0
|
||||||
# _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫:
|
# _withcontent._pop_provider (cross-module, same visibility guard as _find_provider:
|
||||||
# 本模块未 import _withcontent 时跳过,避免 undefined symbol)
|
# skip when this module does not import _withcontent, to avoid undefined symbol).
|
||||||
fd_pon_probe: llvmlite.Function* = self.find_func_in_module(mod, "_pop_provider")
|
fd_pon_probe: mlirlite.Function* = self.find_func_in_module(mod, "_pop_provider")
|
||||||
if fd_pon_probe is None: return 0
|
if fd_pon_probe is None: return 0
|
||||||
fd_pon: str = llvmlite.function_get_name(fd_pon_probe)
|
fd_pon: str = mlirlite.function_get_name(fd_pon_probe)
|
||||||
if fd_pon is None: return 0
|
if fd_pon is None: return 0
|
||||||
ret_i32_q: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
ret_i32_q: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
if ret_i32_q is None: return 0
|
if ret_i32_q is None: return 0
|
||||||
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
|
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
|
||||||
for _pi in range(fd_pc):
|
for _pi in range(int(fd_pc)): mlirlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
||||||
llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
|
||||||
return fd_pc
|
return fd_pc
|
||||||
File diff suppressed because it is too large.
Load diff
+349
-538
File diff suppressed because it is too large.
Load diff
+1443
-1594
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,8 @@
|
|||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import lib.core.Gen.GenApi as GenApi
|
import lib.core.Gen.GenApi as GenApi
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdio
|
import stdio
|
||||||
@@ -13,57 +12,35 @@ import lib.core.Gen.Translator as HT
|
|||||||
from . import CallGenBase
|
from . import CallGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CallGenProp - split from CallGenMixin (stage B): property access (getter/setter/deleter / static class calls / list builtins)
|
||||||
# CallGenProp - CallGenMixin 拆分(阶段 B):属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
novirtual class CallGenProp(CallGenBase.CallGenBase):
|
novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||||
"""属性访问(getter/setter/deleter / 静态类调用 / list 内置方法)"""
|
"""Property access (getter/setter/deleter / static class calls / list builtins)"""
|
||||||
|
|
||||||
# ============================================================
|
# Property methods (@property/@x.setter/@x.deleter) and static/class call helpers.
|
||||||
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
|
# Translate obj.prop / obj.prop=v / del obj.prop / Class.method() into the matching {sha1}.ClassName.prop call.
|
||||||
#
|
|
||||||
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
|
|
||||||
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
|
|
||||||
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# _translate_property_call - translate a property/static method call.
|
||||||
# _translate_property_call — 翻译属性方法调用
|
# Resolve the call name like _translate_method_call; call_head/call_count is the prebuilt arg list.
|
||||||
#
|
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*, arg_nodes: list[ast.AST*]* = None, start_idx: int = 0) -> mlirlite.Value*:
|
||||||
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
|
"""Translate a property/static method call, return the call result Value"""
|
||||||
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
|
|
||||||
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
|
|
||||||
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
|
|
||||||
#
|
|
||||||
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
|
|
||||||
# ============================================================
|
|
||||||
def _translate_property_call(self, pool: memhub.MemBuddy*,
|
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
class_name: str,
|
|
||||||
method_name: str,
|
|
||||||
call_head: llvmlite.Value*,
|
|
||||||
call_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""翻译属性/静态方法调用,返回 call 结果 Value"""
|
|
||||||
if pool is None or builder is None or class_name is None or method_name is None: return None
|
if pool is None or builder is None or class_name is None or method_name is None: return None
|
||||||
|
|
||||||
# 构造查找名: ClassName.method_name
|
# Build the lookup name: ClassName.method_name
|
||||||
lookup_name: str = pool.alloc(128)
|
lookup_name: str = pool.alloc(128)
|
||||||
if lookup_name is None: return None
|
if lookup_name is None: return None
|
||||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
|
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
|
||||||
|
|
||||||
# 查找函数(精确/后缀匹配)
|
# Look up the function (exact/suffix match)
|
||||||
found_func: llvmlite.Function* = self.find_func_in_module(mod, lookup_name)
|
found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name)
|
||||||
|
|
||||||
# 确定调用名
|
# Determine the call name
|
||||||
call_name: str = lookup_name
|
call_name: str = lookup_name
|
||||||
if found_func is not None:
|
if found_func is not None:
|
||||||
found_name: str = llvmlite.function_get_name(found_func)
|
found_name: str = mlirlite.function_get_name(found_func)
|
||||||
if found_name is not None:
|
if found_name is not None: call_name = found_name
|
||||||
call_name = found_name
|
|
||||||
else:
|
else:
|
||||||
# 未找到:用类所属模块的 SHA1 构造调用名
|
# Not found: build the call name from the class's owning module SHA1
|
||||||
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
cls_sha1_pc: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||||
if cls_sha1_pc is not None:
|
if cls_sha1_pc is not None:
|
||||||
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
|
mlen_pc: sizet = string.strlen(cls_sha1_pc) + string.strlen(lookup_name) + 2
|
||||||
@@ -72,223 +49,154 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
|||||||
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
|
viperlib.snprintf(mangled_pc, mlen_pc, "%s.%s", cls_sha1_pc, lookup_name)
|
||||||
call_name = mangled_pc
|
call_name = mangled_pc
|
||||||
|
|
||||||
# 确定返回类型
|
# Determine the return type
|
||||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
call_ret_ty: llvmlite.LLVMType* = i32_ty
|
call_ret_ty: mlirlite.LLVMType* = i32_ty
|
||||||
if found_func is not None:
|
if found_func is not None:
|
||||||
frt: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(found_func)
|
frt: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(found_func)
|
||||||
if frt is not None:
|
if frt is not None: call_ret_ty = frt
|
||||||
call_ret_ty = frt
|
|
||||||
else:
|
else:
|
||||||
sp_ty1: llvmlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
|
sp_ty1: mlirlite.LLVMType* = self._special_method_ret_ty(pool, class_name, method_name)
|
||||||
if sp_ty1 is not None:
|
if sp_ty1 is not None: call_ret_ty = sp_ty1
|
||||||
call_ret_ty = sp_ty1
|
|
||||||
else:
|
else:
|
||||||
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
cls_sha1_rt: str = self.Gen.StructGen.get_struct_sha1(class_name)
|
||||||
looked_up_ty: llvmlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
|
# Authoritative source: vpi stub return-annotation cache (in place, no hardcoded whitelist)
|
||||||
if looked_up_ty is not None:
|
vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
|
||||||
call_ret_ty = looked_up_ty
|
if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty
|
||||||
else:
|
else:
|
||||||
# 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单)
|
# Fallback: parse the module text.ll define line (legacy form not covered by vpi)
|
||||||
vpi_ret_ty: llvmlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, cls_sha1_rt, class_name, method_name)
|
looked_up_ty: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cls_sha1_rt, class_name, method_name)
|
||||||
if vpi_ret_ty is not None:
|
if looked_up_ty is not None: call_ret_ty = looked_up_ty
|
||||||
call_ret_ty = vpi_ret_ty
|
|
||||||
else:
|
else:
|
||||||
# Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成):
|
# Phase A deps not ready (cls_sha1_rt None / the defining module's text.ll missing):
|
||||||
# 保持默认 i32(build_call(Void) 返回 None 会破坏表达式翻译)。
|
# keep the i32 default (build_call(Void) returning None would break expression translation).
|
||||||
if cls_sha1_rt is None \
|
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
|
||||||
or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0:
|
else: call_ret_ty = self._fail_method_ret_ty(method_name, None, class_name, None, cls_sha1_rt)
|
||||||
call_ret_ty = llvmlite.Int32(pool)
|
|
||||||
else:
|
|
||||||
call_ret_ty = self._fail_method_ret_ty(method_name)
|
|
||||||
|
|
||||||
# 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call
|
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
|
||||||
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
|
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
|
||||||
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
|
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
|
||||||
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
|
|
||||||
tgt_func_pc: llvmlite.Function* = found_func
|
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
|
||||||
if tgt_func_pc is None:
|
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
|
||||||
tgt_func_pc = self.find_func_in_module(mod, call_name)
|
tgt_func_pc: mlirlite.Function* = found_func
|
||||||
|
if tgt_func_pc is None: tgt_func_pc = self.find_func_in_module(mod, call_name)
|
||||||
if tgt_func_pc is not None:
|
if tgt_func_pc is not None:
|
||||||
prev_pc: llvmlite.Value* = None
|
prev_pc: mlirlite.Value* = None
|
||||||
cur_arg_pc: llvmlite.Value* = call_head
|
cur_arg_pc: mlirlite.Value* = call_head
|
||||||
cur_param_pc: llvmlite.Param* = llvmlite.function_get_param_head(tgt_func_pc)
|
cur_param_pc: mlirlite.Param* = mlirlite.function_get_param_head(tgt_func_pc)
|
||||||
while cur_arg_pc is not None and cur_param_pc is not None:
|
while cur_arg_pc is not None and cur_param_pc is not None:
|
||||||
param_ty_pc: llvmlite.LLVMType* = llvmlite.param_get_ty(cur_param_pc)
|
param_ty_pc: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param_pc)
|
||||||
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
||||||
coerced_pc: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
|
coerced_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, cur_arg_pc, param_ty_pc)
|
||||||
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
||||||
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
|
# Keep the list intact: the new Value inherits the node's Next; predecessor points to it
|
||||||
llvmlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
mlirlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
||||||
if prev_pc is None:
|
if prev_pc is None: call_head = coerced_pc
|
||||||
call_head = coerced_pc
|
else: mlirlite.value_set_next(prev_pc, coerced_pc)
|
||||||
else:
|
|
||||||
llvmlite.value_set_next(prev_pc, coerced_pc)
|
|
||||||
cur_arg_pc = coerced_pc
|
cur_arg_pc = coerced_pc
|
||||||
prev_pc = cur_arg_pc
|
prev_pc = cur_arg_pc
|
||||||
cur_arg_pc = cur_arg_pc.Next
|
cur_arg_pc = cur_arg_pc.Next
|
||||||
cur_param_pc = llvmlite.param_get_next(cur_param_pc)
|
cur_param_pc = mlirlite.param_get_next(cur_param_pc)
|
||||||
|
|
||||||
# 确保 declare 存在,然后调用
|
# Ensure the declare exists, then call
|
||||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
||||||
return llvmlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
return mlirlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
||||||
# ============================================================
|
# _resolve_obj_class_name - resolve an object expression's class short name.
|
||||||
# _resolve_obj_class_name — 解析对象表达式的类名(短名)
|
# Prefer the variable annotation (AnnotClassName); fall back to the LLVM type (Ptr(Struct)).
|
||||||
#
|
|
||||||
# 优先从变量注解(AnnotClassName)解析(如 "memhub.MemBuddy" → "MemBuddy"),
|
|
||||||
# 失败时从 LLVM 类型解析(Ptr(Struct) → 类名)。
|
|
||||||
# 供属性访问/赋值/删除识别 property 时使用。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, obj_node: ast.AST*) -> str:
|
||||||
def _resolve_obj_class_name(self, pool: memhub.MemBuddy*,
|
"""Resolve an object expression's class short name, None on failure"""
|
||||||
trans: HT.Translator*,
|
if pool is None or trans is None or obj_node is None: return None
|
||||||
obj_node: ast.AST*) -> str:
|
|
||||||
"""解析对象表达式的类名(短名),失败返回 None"""
|
|
||||||
if pool is None or trans is None or obj_node is None:
|
|
||||||
return None
|
|
||||||
if obj_node.kind() == ast.ASTKind.Name:
|
if obj_node.kind() == ast.ASTKind.Name:
|
||||||
obj_nm: ast.Name* = ast.Name*(obj_node)
|
obj_nm: ast.Name* = ast.Name*(obj_node)
|
||||||
if obj_nm.id is not None:
|
if obj_nm.id is not None:
|
||||||
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
|
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, obj_nm.id)
|
||||||
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
|
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
|
||||||
if annot_val is not None:
|
if annot_val is not None:
|
||||||
# [D32] 取「模块限定名」末段时只按**括号外**的 '.' 切分。
|
# [D32] Split the qualified name only on '.' outside brackets: the old strrchr over the
|
||||||
# 此前用 string.strrchr 在整个串上找最后一个 '.':泛型特化名
|
# whole string picked the '.' inside "list[sha1.AST*]", yielding a bogus short name.
|
||||||
# "list[e94ef9e139461915.AST|t.CPtr]" 的 '.' 落在 '[' 内 ⇒
|
|
||||||
# 返回 "AST|t.CPtr]" 这种垃圾短名 ⇒ 后续按它查表必落空,
|
|
||||||
# 进而可能被兜底成任意特化(.get() 误分派)。
|
|
||||||
n_av: sizet = string.strlen(annot_val)
|
n_av: sizet = string.strlen(annot_val)
|
||||||
last_dot_av: sizet = n_av
|
last_dot_av: sizet = n_av
|
||||||
depth_av: int = 0
|
depth_av: int = 0
|
||||||
ai_av: sizet = 0
|
for ai_av in range(int(n_av)):
|
||||||
while ai_av < n_av:
|
|
||||||
ch_av: int = annot_val[ai_av]
|
ch_av: int = annot_val[ai_av]
|
||||||
if ch_av == '[':
|
if ch_av == '[': depth_av += 1
|
||||||
depth_av += 1
|
elif ch_av == ']': depth_av -= 1
|
||||||
elif ch_av == ']':
|
elif ch_av == '.' and depth_av == 0: last_dot_av = ai_av
|
||||||
depth_av -= 1
|
if last_dot_av < n_av: return annot_val + last_dot_av + 1
|
||||||
elif ch_av == '.' and depth_av == 0:
|
|
||||||
last_dot_av = ai_av
|
|
||||||
ai_av += 1
|
|
||||||
if last_dot_av < n_av:
|
|
||||||
return annot_val + last_dot_av + 1
|
|
||||||
return annot_val
|
return annot_val
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _try_property_getter_attr - try to translate obj.attr into a property getter call.
|
||||||
# _try_property_getter_attr — 尝试将 obj.attr 翻译为 property getter 调用
|
# If the class has an @property/@x.getter for attr, emit call Class.attr(obj); else return None.
|
||||||
#
|
|
||||||
# 检查 obj 的类是否有 @property/@x.getter 的 attr 方法;
|
|
||||||
# 若有则生成 call Class.attr(obj) 并返回调用结果 Value;否则返回 None。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _try_property_getter_attr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, at_node: ast.Attribute*, obj_ptr: mlirlite.Value*, trans: HT.Translator*, fallback_class_name: str = None) -> mlirlite.Value*:
|
||||||
def _try_property_getter_attr(self, pool: memhub.MemBuddy*,
|
"""Try a property getter access, return the call result Value; None if not a getter."""
|
||||||
builder: llvmlite.IRBuilder*,
|
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None: return None
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
at_node: ast.Attribute*,
|
|
||||||
obj_ptr: llvmlite.Value*,
|
|
||||||
trans: HT.Translator*,
|
|
||||||
fallback_class_name: str = None) -> llvmlite.Value*:
|
|
||||||
"""尝试 property getter 属性访问,返回调用结果 Value;非 getter 返回 None
|
|
||||||
|
|
||||||
fallback_class_name: 对象表达式非 Name 时从 LLVM 类型解析出的类名兜底。
|
|
||||||
"""
|
|
||||||
if at_node is None or at_node.attr is None or obj_ptr is None or trans is None:
|
|
||||||
return None
|
|
||||||
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
|
class_name: str = self._resolve_obj_class_name(pool, trans, at_node.value)
|
||||||
if class_name is None:
|
if class_name is None: class_name = fallback_class_name
|
||||||
class_name = fallback_class_name
|
if class_name is None: return None
|
||||||
if class_name is None:
|
|
||||||
return None
|
|
||||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
||||||
if meta & GenTypes.META_PROP_GETTER == 0:
|
if meta & GenTypes.META_PROP_GETTER == 0: return None
|
||||||
return None
|
# Single-arg call: obj -> getter (no user args to auto-address; obj is slot 0)
|
||||||
# 单参数调用: obj → getter
|
mlirlite.value_set_next(obj_ptr, None)
|
||||||
llvmlite.value_set_next(obj_ptr, None)
|
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans, None, 1)
|
||||||
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
|
# _try_property_setter - try to translate obj.attr = v into a property setter call.
|
||||||
# ============================================================
|
# If the class has an @x.setter for attr, emit call Class.attr$set(obj, v) and return 1; else 0.
|
||||||
# _try_property_setter — 尝试将 obj.attr = v 翻译为 property setter 调用
|
|
||||||
#
|
|
||||||
# 检查 obj 的类是否有 @x.setter 的 attr 方法;
|
|
||||||
# 若有则生成 call Class.attr$set(obj, v),返回 1;否则返回 0。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*, rhs_node: ast.AST* = None) -> int:
|
||||||
def _try_property_setter(self, pool: memhub.MemBuddy*,
|
"""Try a property setter assignment, return 1=handled (call emitted)"""
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
target_attr: ast.Attribute*,
|
|
||||||
rhs_val: llvmlite.Value*,
|
|
||||||
trans: HT.Translator*) -> int:
|
|
||||||
"""尝试 property setter 赋值,返回 1=已处理(生成调用)"""
|
|
||||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||||
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
|
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
|
||||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||||
if class_name is None: return 0
|
if class_name is None: return 0
|
||||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||||
if meta & GenTypes.META_PROP_SETTER == 0: return 0
|
if meta & GenTypes.META_PROP_SETTER == 0: return 0
|
||||||
# 翻译对象值(self 参数)
|
# Translate the object value (self argument)
|
||||||
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||||
if obj_val is None: return 0
|
if obj_val is None: return 0
|
||||||
# 构建参数链表: obj → rhs
|
# Build the argument list: obj -> rhs
|
||||||
llvmlite.value_set_next(obj_val, None)
|
mlirlite.value_set_next(obj_val, None)
|
||||||
llvmlite.value_set_next(rhs_val, None)
|
mlirlite.value_set_next(rhs_val, None)
|
||||||
llvmlite.value_set_next(obj_val, rhs_val)
|
mlirlite.value_set_next(obj_val, rhs_val)
|
||||||
# setter 函数名带 $set 后缀
|
# The setter function name carries the $set suffix
|
||||||
setter_name_buf: str = pool.alloc(128)
|
setter_name_buf: str = pool.alloc(128)
|
||||||
if setter_name_buf is None: return 0
|
if setter_name_buf is None: return 0
|
||||||
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
||||||
self._translate_property_call(pool, builder, mod, class_name,
|
# Value-reference (&T / &temp T) setter value param: expose the rhs AST as user arg 0
|
||||||
setter_name_buf, obj_val, 2, trans)
|
# (obj is slot 0) so the helper can take the lvalue address / build a temp copy.
|
||||||
|
set_arg_nodes: list[ast.AST*]* = None
|
||||||
|
if rhs_node is not None:
|
||||||
|
set_arg_nodes = list[ast.AST*](pool, 1)
|
||||||
|
if set_arg_nodes is not None: set_arg_nodes.append(rhs_node)
|
||||||
|
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans, set_arg_nodes, 1)
|
||||||
return 1
|
return 1
|
||||||
# ============================================================
|
# _try_property_deleter - try to translate del obj.attr into a property deleter call.
|
||||||
# _try_property_deleter — 尝试将 del obj.attr 翻译为 property deleter 调用
|
# If the class has an @x.deleter for attr, emit call Class.attr$del(obj) and return 1; else 0.
|
||||||
#
|
|
||||||
# 检查 obj 的类是否有 @x.deleter 的 attr 方法;
|
|
||||||
# 若有则生成 call Class.attr$del(obj),返回 1;否则返回 0。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _try_property_deleter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, trans: HT.Translator*) -> int:
|
||||||
def _try_property_deleter(self, pool: memhub.MemBuddy*,
|
"""Try a property deleter, return 1=handled (call emitted)"""
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
target_attr: ast.Attribute*,
|
|
||||||
trans: HT.Translator*) -> int:
|
|
||||||
"""尝试 property deleter 删除,返回 1=已处理(生成调用)"""
|
|
||||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||||
if target_attr is None or target_attr.attr is None: return 0
|
if target_attr is None or target_attr.attr is None: return 0
|
||||||
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
class_name: str = self._resolve_obj_class_name(pool, trans, target_attr.value)
|
||||||
if class_name is None: return 0
|
if class_name is None: return 0
|
||||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, target_attr.attr)
|
||||||
if meta & GenTypes.META_PROP_DELETER == 0: return 0
|
if meta & GenTypes.META_PROP_DELETER == 0: return 0
|
||||||
# 翻译对象值(self 参数)
|
# Translate the object value (self argument)
|
||||||
obj_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, target_attr.value, None, 0, trans)
|
||||||
if obj_val is None: return 0
|
if obj_val is None: return 0
|
||||||
llvmlite.value_set_next(obj_val, None)
|
mlirlite.value_set_next(obj_val, None)
|
||||||
# deleter 函数名带 $del 后缀
|
# The deleter function name carries the $del suffix
|
||||||
del_name_buf: str = pool.alloc(128)
|
del_name_buf: str = pool.alloc(128)
|
||||||
if del_name_buf is None: return 0
|
if del_name_buf is None: return 0
|
||||||
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
||||||
self._translate_property_call(pool, builder, mod, class_name,
|
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans, None, 1)
|
||||||
del_name_buf, obj_val, 1, trans)
|
|
||||||
return 1
|
return 1
|
||||||
# ============================================================
|
# _translate_static_class_call - translate ClassName.method(args) for static/class methods.
|
||||||
# _translate_static_class_call — 翻译 ClassName.method(args) 静态/类方法调用
|
# @staticmethod: call Class.method(args...). @classmethod: prepend a stack-allocated class pointer.
|
||||||
#
|
|
||||||
# @staticmethod: 直接 call Class.method(args...),无 self
|
|
||||||
# @classmethod: 在参数前插入栈上分配的类实例指针(与 TPC 一致)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _translate_static_class_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
def _translate_static_class_call(self, pool: memhub.MemBuddy*,
|
"""Translate a ClassName static/class method call, return the call result Value; None otherwise"""
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
class_name: str,
|
|
||||||
method_name: str,
|
|
||||||
cl: ast.Call*,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""翻译类名静态/类方法调用,返回 call 结果 Value;非静态/类方法返回 None"""
|
|
||||||
if pool is None or builder is None or mod is None or trans is None: return None
|
if pool is None or builder is None or mod is None or trans is None: return None
|
||||||
if class_name is None or method_name is None or cl is None: return None
|
if class_name is None or method_name is None or cl is None: return None
|
||||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
|
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, method_name)
|
||||||
@@ -298,67 +206,55 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
|||||||
if meta & GenTypes.META_CLASS != 0: is_class = 1
|
if meta & GenTypes.META_CLASS != 0: is_class = 1
|
||||||
if is_static == 0 and is_class == 0: return None
|
if is_static == 0 and is_class == 0: return None
|
||||||
|
|
||||||
# 构建参数链表
|
# Build the argument list
|
||||||
head: llvmlite.Value* = None
|
head: mlirlite.Value* = None
|
||||||
total_count: int = 0
|
total_count: int = 0
|
||||||
|
|
||||||
# @classmethod: 插入栈上分配的类实例指针作为 cls 参数
|
# @classmethod: prepend a stack-allocated class pointer as the cls argument
|
||||||
if is_class != 0:
|
if is_class != 0:
|
||||||
struct_ty_cc: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
struct_ty_cc: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
||||||
if struct_ty_cc is None: return None
|
if struct_ty_cc is None: return None
|
||||||
cls_ptr: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
|
cls_ptr: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
|
||||||
if cls_ptr is None: return None
|
if cls_ptr is None: return None
|
||||||
llvmlite.value_set_next(cls_ptr, None)
|
mlirlite.value_set_next(cls_ptr, None)
|
||||||
head = cls_ptr
|
head = cls_ptr
|
||||||
total_count = 1
|
total_count = 1
|
||||||
|
|
||||||
# 翻译调用参数
|
# Translate the call arguments
|
||||||
cargs: list[ast.AST*]* = cl.args
|
cargs: list[ast.AST*]* = cl.args
|
||||||
can: sizet = 0
|
can: sizet = 0
|
||||||
if cargs is not None:
|
if cargs is not None: can = len(cargs)
|
||||||
can = len(cargs)
|
tail: mlirlite.Value* = head
|
||||||
tail: llvmlite.Value* = head
|
for ai in range(int(can)):
|
||||||
for ai in range(can):
|
|
||||||
arg_node: ast.AST* = cargs.get(ai)
|
arg_node: ast.AST* = cargs.get(ai)
|
||||||
if arg_node is None: continue
|
if arg_node is None: continue
|
||||||
arg_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
|
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
|
||||||
if arg_val is None: continue
|
if arg_val is None: continue
|
||||||
llvmlite.value_set_next(arg_val, None)
|
mlirlite.value_set_next(arg_val, None)
|
||||||
if head is None:
|
if head is None:
|
||||||
head = arg_val
|
head = arg_val
|
||||||
tail = arg_val
|
tail = arg_val
|
||||||
else:
|
else:
|
||||||
llvmlite.value_set_next(tail, arg_val)
|
mlirlite.value_set_next(tail, arg_val)
|
||||||
tail = arg_val
|
tail = arg_val
|
||||||
total_count += 1
|
total_count += 1
|
||||||
|
|
||||||
# 零参 @staticmethod(如 PyListObj.new()):head 保持 None 是合法的
|
# Zero-arg @staticmethod (e.g. PyListObj.new()): head=None is a valid no-arg call, so still route
|
||||||
# 无参调用,照常走 _translate_property_call(SHA1 前缀名 + 精确返回类型)。
|
# through _translate_property_call; returning None would fall back to bare-name lookup and fail.
|
||||||
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
|
# Value-reference (&T / &temp T) user args: static methods have no self slot (start_idx=0),
|
||||||
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
|
# classmethods carry the cls pointer at slot 0 (start_idx=1).
|
||||||
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
|
sc_start_idx: int = 0
|
||||||
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans)
|
if is_class != 0: sc_start_idx = 1
|
||||||
# ============================================================
|
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans, cargs, sc_start_idx)
|
||||||
# _is_list_builtin_method - 判断是否是 list 泛型内置容器方法
|
# _is_list_builtin_method - test whether (class_name, method_name) is a list generic builtin.
|
||||||
#
|
# list[T] methods are inlined by translate_call's list branch, not registered as functions.
|
||||||
# list[T] 是内置泛型容器(_list.vp),其方法(append/get/set/pop/clear/
|
|
||||||
# __iter__/__len__/__getitem__/__setitem__)由 translate_call 的 list
|
|
||||||
# 内置分支内联 GEP 处理,不注册为方法函数。若 obj.method() 检测把
|
|
||||||
# list 特化 struct(如 list[AST | CPtr])误当普通结构体走
|
|
||||||
# _translate_method_call,会查不到 append 返回类型 → fail-fast
|
|
||||||
# (NewAstTest body.append 根因)。此判断用于跳过 method_call 路径。
|
|
||||||
#
|
|
||||||
# 返回: 1=list 内置方法(应落到 list 内置分支),0=普通方法
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
def _is_list_builtin_method(self, class_name: str, method_name: str) -> int:
|
||||||
def _is_list_builtin_method(self, class_name: str,
|
"""Test whether (class_name, method_name) is a list generic builtin method"""
|
||||||
method_name: str) -> int:
|
|
||||||
"""判断 (class_name, method_name) 是否是 list 泛型内置方法"""
|
|
||||||
if class_name is None or method_name is None: return 0
|
if class_name is None or method_name is None: return 0
|
||||||
# 类名必须以 "list" 开头(list / list[AST | CPtr] 特化名)
|
# Class name must start with "list" (list / list[AST*] specializations)
|
||||||
if string.strncmp(class_name, "list", 4) != 0: return 0
|
if string.strncmp(class_name, "list", 4) != 0: return 0
|
||||||
# 方法名必须是 list 内置方法集合
|
# Method name must be one of the list builtin methods
|
||||||
if string.strcmp(method_name, "append") == 0: return 1
|
if string.strcmp(method_name, "append") == 0: return 1
|
||||||
if string.strcmp(method_name, "get") == 0: return 1
|
if string.strcmp(method_name, "get") == 0: return 1
|
||||||
if string.strcmp(method_name, "set") == 0: return 1
|
if string.strcmp(method_name, "set") == 0: return 1
|
||||||
@@ -370,76 +266,50 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
|||||||
if string.strcmp(method_name, "__setitem__") == 0: return 1
|
if string.strcmp(method_name, "__setitem__") == 0: return 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# ============================================================
|
# _strip_module_sha1_prefix - strip the cross-module specialization sha1 prefix.
|
||||||
# _strip_module_sha1_prefix - 去跨模块特化名的 sha1 前缀
|
# Receiver struct names often carry a "{16 hex}." prefix; use GenApi.sha1_prefix_len (C10).
|
||||||
#
|
|
||||||
# 接收者实际结构体名常带 "{16位小写hex}." 前缀(如
|
|
||||||
# "e94ef9e139461915.list[ast.ASTptr]");判定基名 | 特化名前必须先剥离。
|
|
||||||
# 前缀长度判定走唯一真源 GenApi.sha1_prefix_len(C10 收敛)。
|
|
||||||
# ============================================================
|
|
||||||
def _strip_module_sha1_prefix(self, name: str) -> str:
|
def _strip_module_sha1_prefix(self, name: str) -> str:
|
||||||
"""去除 "{16hex}." 前缀(无前缀则原样返回;返回原串内部子串)"""
|
"""Strip the "{16hex}." prefix (return the input unchanged if absent; returns a substring of the input)"""
|
||||||
if name is None:
|
if name is None: return None
|
||||||
return None
|
|
||||||
pfx: int = GenApi.sha1_prefix_len(name)
|
pfx: int = GenApi.sha1_prefix_len(name)
|
||||||
return name + pfx if pfx != 0 else name
|
return name + pfx if pfx != 0 else name
|
||||||
|
|
||||||
# _resolve_generic_receiver_name:解析接收者实际泛型特化名(IR 真值)——接收者实际 LLVM 结构体类型名比可能退化的注解/注册名可靠,故泛型判定以此为准:去 sha1 前缀取 '[' 前基名,基名是已注册泛型模板时含类型实参 ⇒ 返回实际特化名,无类型实参 ⇒ fail-fast(D25);基名非泛型模板或实际类型不可得 ⇒ 返回 None(调用方各自回退)。与 CallGenCall._translate_method_call 的 D25 分派修复共用本函数(唯一口径)
|
# _resolve_generic_receiver_name - resolve the receiver's actual generic specialization name (IR truth).
|
||||||
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*,
|
# Strip the sha1 prefix, take the base before '['; bare generic base => fail-fast (D25), else None.
|
||||||
struct_ty: llvmlite.LLVMType*,
|
def _resolve_generic_receiver_name(self, pool: memhub.MemBuddy*, struct_ty: mlirlite.LLVMType*, method_name: str, cl: ast.Call*) -> str:
|
||||||
method_name: str,
|
"""Return the actual generic specialization name (sha1 stripped) or None; a bare generic base fails fast"""
|
||||||
cl: ast.Call*) -> str:
|
if struct_ty is None: return None
|
||||||
"""返回实际泛型特化名(去 sha1)或 None;裸泛型基类 fail-fast"""
|
ty_nm: str = mlirlite.get_struct_name(struct_ty)
|
||||||
if struct_ty is None:
|
if ty_nm is None or ty_nm[0] == '\0': return None
|
||||||
return None
|
|
||||||
ty_nm: str = llvmlite.get_struct_name(struct_ty)
|
|
||||||
if ty_nm is None or ty_nm[0] == '\0':
|
|
||||||
return None
|
|
||||||
short_nm: str = self._strip_module_sha1_prefix(ty_nm)
|
short_nm: str = self._strip_module_sha1_prefix(ty_nm)
|
||||||
lb: str = string.strchr(short_nm, '[')
|
lb: str = string.strchr(short_nm, '[')
|
||||||
bare_len: sizet = 0
|
bare_len: sizet = 0
|
||||||
if lb is not None:
|
if lb is not None: bare_len = sizet(uint64(lb) - uint64(short_nm))
|
||||||
bare_len = sizet(uint64(lb) - uint64(short_nm))
|
else: bare_len = string.strlen(short_nm)
|
||||||
else:
|
if bare_len == 0: return None
|
||||||
bare_len = string.strlen(short_nm)
|
|
||||||
if bare_len == 0:
|
|
||||||
return None
|
|
||||||
base_nm: str = pool.alloc(bare_len + 1)
|
base_nm: str = pool.alloc(bare_len + 1)
|
||||||
if base_nm is None:
|
if base_nm is None: return None
|
||||||
return None
|
|
||||||
string.strncpy(base_nm, short_nm, bare_len)
|
string.strncpy(base_nm, short_nm, bare_len)
|
||||||
base_nm[bare_len] = '\0'
|
base_nm[bare_len] = '\0'
|
||||||
# 基名非已注册泛型模板(普通类接收者)⇒ 非泛型,交由调用方原口径处理
|
# Base is not a registered generic template (plain class receiver) => not generic; return None
|
||||||
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None:
|
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None: return None
|
||||||
return None
|
if lb is not None: return short_nm
|
||||||
if lb is not None:
|
# Bare generic base (no type args): element type unknown -> fail-fast (D25)
|
||||||
return short_nm
|
self.Gen.TypeGenErr.fatal_error(cl, f"the actual struct type of the receiver of method '{method_name}' is a bare generic base " f"'{short_nm}*' (type arguments lost), so the element type is unknown; " f"silently using the default element type is forbidden (treating elements as i32 would truncate pointers); " f"make the variable annotation/type carry type arguments (e.g. list[ast.AST*])", "TypeError")
|
||||||
# 裸泛型基类(无类型实参):接收者元素类型未知 → fail-fast(D25 口径)
|
|
||||||
self.Gen.TypeGenErr.fatal_error(
|
|
||||||
cl,
|
|
||||||
f"方法 '{method_name}' 的接收者实际结构体类型是裸泛型基类 "
|
|
||||||
f"'{short_nm}*'(丢失类型实参),元素类型未知;"
|
|
||||||
f"禁止静默按默认元素类型处理(元素按 i32 处理会截断指针),"
|
|
||||||
f"请让该变量的注解/类型带上实参(如 list[ast.AST*])",
|
|
||||||
"TypeError")
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# _is_list_builtin_receiver:基于接收者实际类型判定 list 内建方法,判据与 _resolve_generic_receiver_name 同源——优先采用接收者实际 LLVM 结构体类型名(去 sha1 判基名),不可得时回退注解名/注册名判定。返回 1=list 内置方法 / 0=非
|
# _is_list_builtin_receiver - test list builtin methods from the receiver's actual type.
|
||||||
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*,
|
# Prefer the actual LLVM struct name (sha1 stripped); fall back to the annotation/registered name.
|
||||||
recv_val: llvmlite.Value*,
|
def _is_list_builtin_receiver(self, pool: memhub.MemBuddy*, recv_val: mlirlite.Value*, fallback_name: str, method_name: str, cl: ast.Call*) -> int:
|
||||||
fallback_name: str,
|
"""Test list builtin methods from the receiver's actual type (fall back to the annotation name if unavailable)"""
|
||||||
method_name: str,
|
sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
|
||||||
cl: ast.Call*) -> int:
|
|
||||||
"""基于接收者实际类型判定 list 内建方法(实际类型不可得回退注解名)"""
|
|
||||||
sty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(recv_val)
|
|
||||||
if sty is not None:
|
if sty is not None:
|
||||||
ty_nm: str = llvmlite.get_struct_name(sty)
|
ty_nm: str = mlirlite.get_struct_name(sty)
|
||||||
if ty_nm is not None and ty_nm[0] != '\0':
|
if ty_nm is not None and ty_nm[0] != '\0':
|
||||||
# 泛型模板接收者:裸基类 fail-fast;含实参用实际特化名
|
# Generic template receiver: bare base fail-fast; with type args use the actual specialization
|
||||||
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
|
act_nm: str = self._resolve_generic_receiver_name(pool, sty, method_name, cl)
|
||||||
if act_nm is not None:
|
if act_nm is not None: return self._is_list_builtin_method(act_nm, method_name)
|
||||||
return self._is_list_builtin_method(act_nm, method_name)
|
# Non-generic template receiver: judge by the actual short name (sha1 stripped)
|
||||||
# 非泛型模板接收者:用去 sha1 后的实际短名判定(不复用退化注解名)
|
|
||||||
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
|
return self._is_list_builtin_method(self._strip_module_sha1_prefix(ty_nm), method_name)
|
||||||
# 实际类型不可得 → 回退注解名/注册名判定(信息缺失下的既有行为)
|
# Actual type unavailable -> fall back to the annotation/registered name
|
||||||
return self._is_list_builtin_method(fallback_name, method_name)
|
return self._is_list_builtin_method(fallback_name, method_name)
|
||||||
+153
-285
@@ -1,7 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import lib.core.Gen.Translator as HT
|
import lib.core.Gen.Translator as HT
|
||||||
@@ -9,378 +8,247 @@ import lib.core.Gen.Translator as HT
|
|||||||
from . import CallGenBase
|
from . import CallGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CallGenVa - CallGenMixin split (stage B): va_arg variadics (start/arg/end / variadic call promotion)
|
||||||
# CallGenVa - CallGenMixin 拆分(阶段 B):va_arg 变长参数(start/arg/end / 变长调用提升)
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
novirtual class CallGenVa(CallGenBase.CallGenBase):
|
novirtual class CallGenVa(CallGenBase.CallGenBase):
|
||||||
"""va_arg 变长参数(start/arg/end / 变长调用提升)"""
|
"""va_arg variadics (start/arg/end and variadic call promotion)."""
|
||||||
|
|
||||||
# ============================================================
|
# _func_has_eh_params - whether callee signature has try-except EH out params (main excluded)
|
||||||
# _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数
|
|
||||||
#
|
|
||||||
# EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/
|
|
||||||
# _translate_method 在含 raise 的函数签名末尾注入(main 除外)。
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*,
|
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Return the alloca pointer of a va_list variable without loading it."""
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
arg_node: ast.AST*,
|
|
||||||
funcs_ptr: t.CPtr,
|
|
||||||
func_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""获取 va_list 变量的 alloca 指针(不 load)
|
|
||||||
|
|
||||||
va_arg/va_start/va_end 需要的是 va_list 对象的地址(alloca 指针),
|
|
||||||
而非 va_list 的值。若 arg_node 是 Name,查符号表获取 alloca;
|
|
||||||
否则用 translate_value 翻译(调用者负责确保传入的是指针)。
|
|
||||||
"""
|
|
||||||
if arg_node is None: return None
|
if arg_node is None: return None
|
||||||
# Name 节点:直接查符号表获取 alloca 指针(不 load)
|
# Name node: look up the alloca pointer from the symbol table (no load)
|
||||||
if arg_node.kind() == ast.ASTKind.Name:
|
if arg_node.kind() == ast.ASTKind.Name:
|
||||||
nm: ast.Name* = ast.Name*(arg_node)
|
nm: ast.Name* = ast.Name*(arg_node)
|
||||||
if nm.id is not None:
|
if nm.id is not None:
|
||||||
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||||
if alloca is not None:
|
if alloca is not None: return alloca
|
||||||
return alloca
|
# Fallback: translate_value (may return a loaded value; only valid when already a pointer)
|
||||||
# 回退:translate_value(可能返回 load 后的值,仅适用于已是指针的情况)
|
|
||||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, funcs_ptr, func_count, trans)
|
||||||
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*,
|
def _resolve_va_arg_type(self, pool: memhub.MemBuddy*, type_node: ast.AST*, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||||
type_node: ast.AST*,
|
"""Resolve the va_arg/arg type argument to an LLVMType, defaulting to i32."""
|
||||||
trans: HT.Translator*) -> llvmlite.LLVMType*:
|
if type_node is None: return mlirlite.Int32(pool)
|
||||||
"""解析 va_arg/arg 的类型参数为 LLVMType
|
# Resolve the type expression via HandlesType.resolve_annotation_type
|
||||||
|
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
||||||
|
if resolved is not None: return resolved
|
||||||
|
# Type node present but unresolvable: fail-fast, never fall back to i32 (would read the vararg slot at the wrong width)
|
||||||
|
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg type resolution failed", "TypeError")
|
||||||
|
return mlirlite.Int32(pool)
|
||||||
|
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
|
"""Emit a va_arg instruction reading the next variadic argument from the va_list."""
|
||||||
|
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||||
|
|
||||||
支持的类型表达式:
|
# First arg: va_list (evaluate to an i8* address; aligned with TPC _HandleVaArgLlvm). In own *args
|
||||||
int / int32 → i32
|
# translate_value returns the buffer address; in forwarding (sprintf -> vsnprintf) it loads the param.
|
||||||
int64 → i64
|
|
||||||
t.CDouble → double
|
|
||||||
t.CPtr / str → i8*
|
|
||||||
str / bytes → i8*
|
|
||||||
无类型参数时默认 i32(vsnprintf 中 va_arg(ap) 常用于读取 i32)
|
|
||||||
"""
|
|
||||||
if type_node is None:
|
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
# 使用 HandlesType.resolve_annotation_type 解析类型表达式
|
|
||||||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, type_node, trans._imported_modules, trans._from_imports, trans, 0)
|
|
||||||
if resolved is not None:
|
|
||||||
return resolved
|
|
||||||
# 类型节点存在但解析失败:fail-fast,绝不回退 i32(会按错宽度读变参槽位)
|
|
||||||
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg 类型解析失败", "TypeError")
|
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*,
|
|
||||||
builder: llvmlite.IRBuilder*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
cl: ast.Call*,
|
|
||||||
cargs: list[ast.AST*]*,
|
|
||||||
can: sizet,
|
|
||||||
funcs_ptr: t.CPtr,
|
|
||||||
func_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""va_arg(ap, type) / va_arg(ap) → %N = va_arg <ap_ty> <ap>, <type>
|
|
||||||
|
|
||||||
生成 LLVM va_arg 指令,从 va_list 读取下一个变长参数。
|
|
||||||
"""
|
|
||||||
if can == 0 or cargs is None:
|
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# 第一个参数:va_list(求值为 i8* 地址值,对齐 TPC _HandleVaArgLlvm)
|
|
||||||
# 当前函数 *args:translate_value 经 vararg 名映射直接返回 va_list 缓冲区地址;
|
|
||||||
# 转发场景(sprintf → vsnprintf 的 ap 参数):translate_value load 参数得到地址值。
|
|
||||||
ap_node: ast.AST* = cargs.get(0)
|
ap_node: ast.AST* = cargs.get(0)
|
||||||
ap_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
|
ap_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ap_node, None, 0, trans)
|
||||||
if ap_val is None:
|
if ap_val is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
ap_ptr: mlirlite.Value* = ap_val
|
||||||
ap_ptr: llvmlite.Value* = ap_val
|
|
||||||
|
|
||||||
# 第二个参数:目标类型(可选,默认 i32)
|
# Second arg: target type (optional, defaults to i32)
|
||||||
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
if can >= 2:
|
if can >= 2:
|
||||||
type_node: ast.AST* = cargs.get(1)
|
type_node: ast.AST* = cargs.get(1)
|
||||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||||
|
|
||||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(对齐 TPC emit_va_arg L72-73),
|
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (TPC emit_va_arg L72-73)
|
||||||
# 再 trunc 回目标宽度——否则返回 8 字节含高位垃圾,%d/%i 语义错误
|
# then trunc to target width; otherwise high bits are garbage (%d/%i wrong, A6 root cause).
|
||||||
# (viperlib vsnprintf %d 非 long 分支 `int64(va_arg(ap, int))`
|
read_ty: mlirlite.LLVMType* = arg_ty
|
||||||
# 编译成直接 va_arg i64,高 32 位未定义 → 负数当大正数 A6 根因)。
|
|
||||||
read_ty: llvmlite.LLVMType* = arg_ty
|
|
||||||
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
abits_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||||
if abits_va != 0 and abits_va < 64:
|
if abits_va != 0 and abits_va < 64:
|
||||||
read_ty = llvmlite.Int64(pool)
|
read_ty = mlirlite.Int64(pool)
|
||||||
va_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
va_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||||
if va_raw is None:
|
if va_raw is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
# 8-byte slot -> target width (low bits); caller int64(...) re-applies sext/zext
|
||||||
# 8 字节槽 → 目标宽度(低 bits 位);调用方 int64(...) 会再 sext/zext
|
return mlirlite.build_trunc(builder, va_raw, arg_ty)
|
||||||
return llvmlite.build_trunc(builder, va_raw, arg_ty)
|
|
||||||
|
|
||||||
# 生成 va_arg 指令
|
# Emit the va_arg instruction
|
||||||
return llvmlite.build_va_arg(builder, ap_ptr, read_ty)
|
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
|
||||||
def _translate_arg_sugar(self, pool: memhub.MemBuddy*,
|
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Emit va_arg reading from the enclosing variadic's own va_list or a forwarded va_list."""
|
||||||
mod: llvmlite.LLVMModule*,
|
# arg() has two forms:
|
||||||
cl: ast.Call*,
|
# (1) Own-variadic form: inside `def f(a, *args)`, trans._va_list_ptr is the i8* address of
|
||||||
cargs: list[ast.AST*]*,
|
# the function's implicit va_list buffer; read the next vararg straight from it.
|
||||||
can: sizet,
|
# (2) Forwarding form: in a NON-variadic wrapper such as vsnprintf(buf, size, fmt, args),
|
||||||
funcs_ptr: t.CPtr,
|
# the caller's va_list pointer is passed by value through the 4th param named "args"
|
||||||
func_count: int,
|
# (vprintf style, see includes/viperlib.vp); there is no implicit va_list here, so
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
# trans._va_list_ptr is None and the pointer must be recovered from that param.
|
||||||
"""arg(type) → %N = va_arg <va_list_ty> <trans._va_list_ptr>, <type>
|
va_ptr: mlirlite.Value* = trans._va_list_ptr
|
||||||
|
if va_ptr is None:
|
||||||
|
# The "args" param's alloca is an i8** slot holding the forwarded i8* va_list value, so it
|
||||||
|
# MUST be loaded once: va_arg needs the pointer value itself (i8*), not the slot address.
|
||||||
|
args_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "args")
|
||||||
|
if args_alloca is None: return mlirlite.const_int32(pool, 0)
|
||||||
|
if args_alloca.Ty is None: return mlirlite.const_int32(pool, 0)
|
||||||
|
va_ptr = mlirlite.build_load(builder, args_alloca.Ty.Pointee, args_alloca)
|
||||||
|
if va_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||||
|
|
||||||
使用当前函数的隐式 va_list(trans._va_list_ptr),
|
# First arg: target type (optional, defaults to i32)
|
||||||
简化 *args 函数体内的 va_arg 调用。
|
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
"""
|
|
||||||
if trans._va_list_ptr is None:
|
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# 第一个参数:目标类型(可选,默认 i32)
|
|
||||||
arg_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
|
||||||
if can >= 1 and cargs is not None:
|
if can >= 1 and cargs is not None:
|
||||||
type_node: ast.AST* = cargs.get(0)
|
type_node: ast.AST* = cargs.get(0)
|
||||||
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
arg_ty = self._resolve_va_arg_type(pool, type_node, trans)
|
||||||
|
|
||||||
# Win64 变参槽位为 8 字节:<64 位整数按 i64 读取(与 _translate_va_arg_sugar 一致),
|
# Win64 vararg slots are 8 bytes: read sub-64-bit ints as i64 (same as _translate_va_arg_sugar),
|
||||||
# 再 trunc 回目标宽度(%d/%i 高位垃圾 A6 修复,见 _translate_va_arg_sugar)
|
# then trunc to target width (%d/%i high-bit garbage fix, see _translate_va_arg_sugar).
|
||||||
read_ty: llvmlite.LLVMType* = arg_ty
|
read_ty: mlirlite.LLVMType* = arg_ty
|
||||||
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
|
||||||
if abits_arg != 0 and abits_arg < 64:
|
if abits_arg != 0 and abits_arg < 64:
|
||||||
read_ty = llvmlite.Int64(pool)
|
read_ty = mlirlite.Int64(pool)
|
||||||
arg_raw: llvmlite.Value* = llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
arg_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, va_ptr, read_ty)
|
||||||
if arg_raw is None:
|
if arg_raw is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
return mlirlite.build_trunc(builder, arg_raw, arg_ty)
|
||||||
return llvmlite.build_trunc(builder, arg_raw, arg_ty)
|
|
||||||
|
|
||||||
# 生成 va_arg 指令(使用 trans._va_list_ptr 作为 va_list 地址)
|
# Emit va_arg, using va_ptr as the va_list address (own buffer or forwarded pointer)
|
||||||
return llvmlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
|
return mlirlite.build_va_arg(builder, va_ptr, read_ty)
|
||||||
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*,
|
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
|
||||||
mod: llvmlite.LLVMModule*,
|
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||||
cargs: list[ast.AST*]*,
|
|
||||||
can: sizet,
|
|
||||||
funcs_ptr: t.CPtr,
|
|
||||||
func_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""va_start(ap, last_arg) → call void @llvm.va_start(i8* %ap)
|
|
||||||
|
|
||||||
初始化 va_list。第二个参数 last_arg 在 LLVM intrinsic 中不使用(C 宏兼容)。
|
# First arg: va_list pointer (get its alloca, no load)
|
||||||
"""
|
|
||||||
if can == 0 or cargs is None:
|
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
|
||||||
ap_node: ast.AST* = cargs.get(0)
|
ap_node: ast.AST* = cargs.get(0)
|
||||||
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||||
if ap_ptr is None:
|
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# bitcast 到 i8*(@llvm.va_start 接受 i8*)
|
# bitcast to i8* (@llvm.va_start takes i8*)
|
||||||
i8_ty_vs: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty_vs: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
i8_ptr_ty_vs: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_vs)
|
i8_ptr_ty_vs: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_vs)
|
||||||
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_vs)
|
||||||
if ap_i8 is None:
|
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
void_ty_vs: llvmlite.LLVMType* = llvmlite.Void(pool)
|
void_ty_vs: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||||
llvmlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
mlirlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
return mlirlite.const_int32(pool, 0)
|
||||||
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*,
|
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Emit llvm.va_end to clean up the va_list (a no-op on most platforms)."""
|
||||||
mod: llvmlite.LLVMModule*,
|
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
|
||||||
cargs: list[ast.AST*]*,
|
|
||||||
can: sizet,
|
|
||||||
funcs_ptr: t.CPtr,
|
|
||||||
func_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""va_end(ap) → call void @llvm.va_end(i8* %ap)
|
|
||||||
|
|
||||||
清理 va_list。在大多数平台上是 no-op,但调用它是良好的 C ABI 习惯。
|
# First arg: va_list pointer (get its alloca, no load)
|
||||||
"""
|
|
||||||
if can == 0 or cargs is None:
|
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# 第一个参数:va_list 指针(获取 alloca,不 load)
|
|
||||||
ap_node: ast.AST* = cargs.get(0)
|
ap_node: ast.AST* = cargs.get(0)
|
||||||
ap_ptr: llvmlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
ap_ptr: mlirlite.Value* = self._va_arg_get_alloca_ptr(pool, builder, mod, ap_node, funcs_ptr, func_count, trans)
|
||||||
if ap_ptr is None:
|
if ap_ptr is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
# bitcast 到 i8*(@llvm.va_end 接受 i8*)
|
# bitcast to i8* (@llvm.va_end takes i8*)
|
||||||
i8_ty_ve: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty_ve: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
i8_ptr_ty_ve: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_ve)
|
i8_ptr_ty_ve: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_ve)
|
||||||
ap_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
ap_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ap_ptr, i8_ptr_ty_ve)
|
||||||
if ap_i8 is None:
|
if ap_i8 is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
|
|
||||||
void_ty_ve: llvmlite.LLVMType* = llvmlite.Void(pool)
|
void_ty_ve: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||||
llvmlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
mlirlite.build_call(builder, "llvm.va_end", ap_i8, 1, void_ty_ve, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
return mlirlite.const_int32(pool, 0)
|
||||||
def _get_func_fixed_param_count(self, func: llvmlite.Function*) -> int:
|
def _get_func_fixed_param_count(self, func: mlirlite.Function*) -> int:
|
||||||
"""获取函数的固定参数数量(遍历 Params 链表)"""
|
"""Return the count of fixed params by walking the Params list."""
|
||||||
if func is None:
|
if func is None: return 0
|
||||||
return 0
|
|
||||||
count: int = 0
|
count: int = 0
|
||||||
param: llvmlite.Param* = llvmlite.function_get_param_head(func)
|
param: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||||
while param is not None:
|
while param is not None:
|
||||||
count += 1
|
count += 1
|
||||||
param = llvmlite.param_get_next(param)
|
param = mlirlite.param_get_next(param)
|
||||||
return count
|
return count
|
||||||
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
def _get_known_vararg_cfunc_fixed_count(self, name: str) -> int:
|
||||||
"""已知变参 C 库函数固定参数个数(-1=未知)
|
"""Return the fixed-param count for known variadic C functions (-1 if unknown)."""
|
||||||
|
if name is None: return -1
|
||||||
跨模块 stub 未注入时 find_func_in_module 返回 None(CallGenMixin
|
if string.strcmp(name, "snprintf") == 0: return 3
|
||||||
注释:stubs 在翻译后才注入,翻译时跨模块函数 found_func 恒 None),
|
if string.strcmp(name, "sprintf") == 0: return 2
|
||||||
无法从函数对象获取 IsVarArg/固定参数个数。这些 t.CExport 变参函数
|
if string.strcmp(name, "vsprintf") == 0: return 2
|
||||||
(viperlib 定义)签名固定,按表回退(对齐 TPC _C_LIB_FUNCS 机制)。
|
|
||||||
"""
|
|
||||||
if name is None:
|
|
||||||
return -1
|
|
||||||
if string.strcmp(name, "snprintf") == 0:
|
|
||||||
return 3
|
|
||||||
if string.strcmp(name, "sprintf") == 0:
|
|
||||||
return 2
|
|
||||||
if string.strcmp(name, "vsprintf") == 0:
|
|
||||||
return 2
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*,
|
def _get_known_vararg_cfunc_param_ty(self, pool: memhub.MemBuddy*, name: str, idx: int) -> mlirlite.LLVMType*:
|
||||||
name: str, idx: int) -> llvmlite.LLVMType*:
|
"""Return the type of the idx-th fixed param of a known variadic C function."""
|
||||||
"""已知变参 C 库函数第 idx 个固定参数类型(变参位置/未知返回 None)
|
if pool is None or name is None or idx < 0: return None
|
||||||
|
i8_ptr_kv: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||||
固定参数类型是 C ABI 硬约束(snprintf 的 size 为 i64),stub 未注入
|
i64_kv: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||||
时按表 coerce,避免 call(i32 1024) 与 declare(i64 size) 不匹配。
|
|
||||||
"""
|
|
||||||
if pool is None or name is None or idx < 0:
|
|
||||||
return None
|
|
||||||
i8_ptr_kv: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
|
||||||
i64_kv: llvmlite.LLVMType* = llvmlite.Int64(pool)
|
|
||||||
if string.strcmp(name, "snprintf") == 0:
|
if string.strcmp(name, "snprintf") == 0:
|
||||||
if idx == 0:
|
if idx == 0: return i8_ptr_kv
|
||||||
return i8_ptr_kv
|
if idx == 1: return i64_kv
|
||||||
if idx == 1:
|
if idx == 2: return i8_ptr_kv
|
||||||
return i64_kv
|
|
||||||
if idx == 2:
|
|
||||||
return i8_ptr_kv
|
|
||||||
return None
|
return None
|
||||||
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
|
if string.strcmp(name, "sprintf") == 0 or string.strcmp(name, "vsprintf") == 0:
|
||||||
if idx == 0:
|
if idx == 0: return i8_ptr_kv
|
||||||
return i8_ptr_kv
|
if idx == 1: return i8_ptr_kv
|
||||||
if idx == 1:
|
|
||||||
return i8_ptr_kv
|
|
||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*,
|
def _check_and_promote_variadic_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, can: sizet, arg_nodes: list[ast.AST*]* = None, self_offset: int = 0, trans: HT.Translator* = None) -> int:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Promote variadic args of a callee to the C ABI; returns 1 if variadic else 0."""
|
||||||
mod: llvmlite.LLVMModule*,
|
if mod is None or call_name is None or head is None: return 0
|
||||||
call_name: str,
|
# Look up the function object
|
||||||
head: llvmlite.Value*,
|
func: mlirlite.Function* = self.find_func_in_module(mod, call_name)
|
||||||
can: sizet,
|
|
||||||
arg_nodes: list[ast.AST*]* = None,
|
|
||||||
self_offset: int = 0,
|
|
||||||
trans: HT.Translator* = None) -> int:
|
|
||||||
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
|
|
||||||
|
|
||||||
返回: is_variadic (1=变长调用, 0=普通调用)
|
|
||||||
副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升)
|
|
||||||
|
|
||||||
arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断
|
|
||||||
self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0)
|
|
||||||
trans: 供 _node_type_unsigned 查询变量注解
|
|
||||||
"""
|
|
||||||
if mod is None or call_name is None or head is None:
|
|
||||||
return 0
|
|
||||||
# 查找函数对象
|
|
||||||
func: llvmlite.Function* = self.find_func_in_module(mod, call_name)
|
|
||||||
fixed_count: int = -1
|
fixed_count: int = -1
|
||||||
if func is not None and func.IsVarArg == 1:
|
if func is not None and func.IsVarArg == 1: fixed_count = self._get_func_fixed_param_count(func)
|
||||||
fixed_count = self._get_func_fixed_param_count(func)
|
|
||||||
else:
|
else:
|
||||||
# 两种情况回退已知变参 C 库函数签名表(snprintf 等):func 为 None(跨模块 stub 未注入,翻译时 found_func 恒 None);func 存在但 IsVarArg=0(该 declare 由 _ensure_method_declare 按实参类型临时创建,无 ... 标记,不代表真实签名)。否则 i32 变参不提升,Win64 8 字节槽只写 4 字节 → va_arg(i64) 高 4 字节读残留栈垃圾
|
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
|
||||||
|
# or IsVarArg=0 (ad hoc declare); otherwise i32 varargs aren't promoted and the Win64 8-byte slot keeps only 4 bytes.
|
||||||
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
|
||||||
if fixed_count < 0:
|
if fixed_count < 0: return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
# 无变长参数(实参数量 <= 固定参数数量),仍需 is_variadic=1
|
# No varargs (arg count <= fixed count) but still report is_variadic=1
|
||||||
if can <= fixed_count:
|
if can <= fixed_count: return 1
|
||||||
return 1
|
|
||||||
|
|
||||||
# 遍历到第 fixed_count 个节点(固定参数的最后一个)
|
# Walk to the fixed_count-th node (the last fixed param)
|
||||||
cur: llvmlite.Value* = head
|
cur: mlirlite.Value* = head
|
||||||
prev: llvmlite.Value* = None
|
prev: mlirlite.Value* = None
|
||||||
idx: int = 0
|
idx: int = 0
|
||||||
while cur is not None and idx < fixed_count:
|
while cur is not None and idx < fixed_count:
|
||||||
prev = cur
|
prev = cur
|
||||||
cur = cur.Next
|
cur = cur.Next
|
||||||
idx += 1
|
idx += 1
|
||||||
|
|
||||||
# cur 指向第 fixed_count+1 个节点(第一个变长参数)
|
# cur now points at the first vararg; apply C ABI promotion to all varargs
|
||||||
# 对变长参数进行 C ABI 提升
|
i32_ty_promote: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
i32_ty_promote: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
double_ty_promote: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||||
double_ty_promote: llvmlite.LLVMType* = llvmlite.Double(pool)
|
|
||||||
v_idx: int = fixed_count
|
v_idx: int = fixed_count
|
||||||
while cur is not None:
|
while cur is not None:
|
||||||
next_node: llvmlite.Value* = cur.Next
|
next_node: mlirlite.Value* = cur.Next
|
||||||
promoted: llvmlite.Value* = cur
|
promoted: mlirlite.Value* = cur
|
||||||
# C ABI integer promotion: i8/i16 → i32
|
# C ABI integer promotion: i8/i16 → i32
|
||||||
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
arg_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty)
|
||||||
if arg_bits != 0 and arg_bits < 32:
|
if arg_bits != 0 and arg_bits < 32:
|
||||||
# 无符号整型(uint8/CUInt16T 等)→ zext;有符号/未知 → sext。
|
# Unsigned ints (uint8/CUInt16T etc.) -> zext; signed/unknown -> sext.
|
||||||
# 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得
|
# Fix: printf("%02x", buf[i]) with CUInt8T must zext to 0xF3, not sext to 0xFFFFFFF3.
|
||||||
# 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。
|
|
||||||
is_u_va: int = 0
|
is_u_va: int = 0
|
||||||
if arg_nodes is not None and trans is not None:
|
if arg_nodes is not None and trans is not None:
|
||||||
ast_idx_va: int = v_idx - self_offset
|
ast_idx_va: int = v_idx - self_offset
|
||||||
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
|
if ast_idx_va >= 0 and ast_idx_va < len(arg_nodes):
|
||||||
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
|
an_va: ast.AST* = arg_nodes.get(ast_idx_va)
|
||||||
if an_va is not None:
|
if an_va is not None:
|
||||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0:
|
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va, trans) != 0: is_u_va = 1
|
||||||
is_u_va = 1
|
if is_u_va != 0: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||||
if is_u_va != 0:
|
|
||||||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
|
||||||
else:
|
else:
|
||||||
# is_u_va 判断失败(arg_nodes/trans 缺失或变量注解查不到)时,
|
# If is_u_va is unknown (missing arg_nodes/trans or no annotation), default i8 to zext
|
||||||
# i8 按系统编程惯例默认 zext(对齐 should_zext_widen 的
|
# (aligned with should_zext_widen); sext would print uint8 bytes as ffffffXX garbage.
|
||||||
# "i8 → zext(系统编程中 i8 几乎总是无符号字节)"策略),
|
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8: promoted = mlirlite.build_zext(builder, cur, i32_ty_promote)
|
||||||
# 否则 printf("%02x", buf[i]) 中 uint8 字节被 sext 成
|
else: promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
||||||
# 0xFFFFFFF3,哈希摘要输出 ffffffXX 垃圾。
|
|
||||||
if self.Gen.ExprGenBase.get_llvm_type_bits(cur.Ty) == 8:
|
|
||||||
promoted = llvmlite.build_zext(builder, cur, i32_ty_promote)
|
|
||||||
else:
|
|
||||||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, cur, i32_ty_promote, None)
|
|
||||||
if promoted is not None:
|
if promoted is not None:
|
||||||
promoted.Next = next_node
|
promoted.Next = next_node
|
||||||
if prev is not None:
|
if prev is not None: prev.Next = promoted
|
||||||
prev.Next = promoted
|
|
||||||
# C ABI float promotion: float → double
|
# C ABI float promotion: float → double
|
||||||
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
|
arg_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(promoted.Ty)
|
||||||
if arg_fbits != 0 and arg_fbits < 64:
|
if arg_fbits != 0 and arg_fbits < 64:
|
||||||
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
|
promoted = self.Gen.ExprGenBase.coerce_to_type(builder, promoted, double_ty_promote, None)
|
||||||
if promoted is not None:
|
if promoted is not None:
|
||||||
promoted.Next = next_node
|
promoted.Next = next_node
|
||||||
if prev is not None:
|
if prev is not None: prev.Next = promoted
|
||||||
prev.Next = promoted
|
# Win64 vararg slots are 8 bytes: i8/i16/i32 -> i64. The va_arg read side always reads i64
|
||||||
# Win64 变参槽位 8 字节:i8/i16/i32 → i64
|
# (_translate_va_arg_sugar), so callers must fill all 8 bytes or stale stack data remains.
|
||||||
# TPV va_arg 读取端统一按 i64 读(_translate_va_arg_sugar),
|
i64_ty_promote: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||||
# 调用方必须写满 8 字节槽,否则高 4 字节残留旧栈数据,%d 读到
|
|
||||||
# 0x7ff700000052 类垃圾(clean 日志根因;对齐 CallGenMixin
|
|
||||||
# L798-811 的 found_func.IsVarArg 提升)。
|
|
||||||
i64_ty_promote: llvmlite.LLVMType* = llvmlite.Int64(pool)
|
|
||||||
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
pb_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(promoted.Ty)
|
||||||
if pb_va != 0 and pb_va < 64:
|
if pb_va != 0 and pb_va < 64:
|
||||||
promoted_va2: llvmlite.Value* = None
|
promoted_va2: mlirlite.Value* = None
|
||||||
is_u_va2: int = 0
|
is_u_va2: int = 0
|
||||||
if arg_nodes is not None and trans is not None:
|
if arg_nodes is not None and trans is not None:
|
||||||
ast_idx_va2: int = v_idx - self_offset
|
ast_idx_va2: int = v_idx - self_offset
|
||||||
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
|
if ast_idx_va2 >= 0 and ast_idx_va2 < len(arg_nodes):
|
||||||
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
|
an_va2: ast.AST* = arg_nodes.get(ast_idx_va2)
|
||||||
if an_va2 is not None:
|
if an_va2 is not None:
|
||||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0:
|
if self.Gen.ExprGenOps._node_type_unsigned(pool, an_va2, trans) != 0: is_u_va2 = 1
|
||||||
is_u_va2 = 1
|
if is_u_va2 != 0: promoted_va2 = mlirlite.build_zext(builder, promoted, i64_ty_promote)
|
||||||
if is_u_va2 != 0:
|
else: promoted_va2 = mlirlite.build_sext(builder, promoted, i64_ty_promote)
|
||||||
promoted_va2 = llvmlite.build_zext(builder, promoted, i64_ty_promote)
|
|
||||||
else:
|
|
||||||
promoted_va2 = llvmlite.build_sext(builder, promoted, i64_ty_promote)
|
|
||||||
if promoted_va2 is not None:
|
if promoted_va2 is not None:
|
||||||
promoted_va2.Next = next_node
|
promoted_va2.Next = next_node
|
||||||
if prev is not None:
|
if prev is not None: prev.Next = promoted_va2
|
||||||
prev.Next = promoted_va2
|
|
||||||
promoted = promoted_va2
|
promoted = promoted_va2
|
||||||
prev = promoted
|
prev = promoted
|
||||||
cur = next_node
|
cur = next_node
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import viperlib
|
import viperlib
|
||||||
@@ -11,167 +10,134 @@ import lib.core.Gen.GenTypes as GenTypes
|
|||||||
from . import CallGenBase
|
from . import CallGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# CallGenVt - CallGenMixin split (phase B): vtable slot writing
|
||||||
# CallGenVt - CallGenMixin 拆分(阶段 B):vtable 槽位写入
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
novirtual class CallGenVt(CallGenBase.CallGenBase):
|
novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||||
"""vtable 槽位写入"""
|
"""vtable slot writing."""
|
||||||
|
|
||||||
# _write_vtable_slot / _write_vtable_attr:把模块级函数指针写入对象 vtable 的方法槽位(devfs 驱动注册模式,对齐 TPC HandlesAssign.py L905-949 写时复制)——vtable 方法不是数据字段,get_attribute_ptr 查不到;对象 vtable 仍指向类默认 vtable 时先复制一份(internal 全局 + llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store i8*。多次写入互不覆盖
|
# _write_vtable_slot / _write_vtable_attr: write a module-level function pointer into an object vtable slot via copy-on-write (vtable slots are not data fields, so get_attribute_ptr cannot find them).
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
def _write_vtable_slot(self, pool: memhub.MemBuddy*,
|
def _write_vtable_slot(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, obj_ptr: mlirlite.Value*, class_name: str, method_index: int, func_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||||
builder: llvmlite.IRBuilder*,
|
"""Emit a copy-on-write vtable slot write."""
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
obj_ptr: llvmlite.Value*,
|
|
||||||
class_name: str,
|
|
||||||
method_index: int,
|
|
||||||
func_val: llvmlite.Value*,
|
|
||||||
trans: HT.Translator*) -> int:
|
|
||||||
"""生成 vtable 槽位写入(写时复制),成功返回 1"""
|
|
||||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||||
if obj_ptr is None or class_name is None or func_val is None: return 0
|
if obj_ptr is None or class_name is None or func_val is None: return 0
|
||||||
if method_index < 0 or trans._cur_func is None: return 0
|
if method_index < 0 or trans._cur_func is None: return 0
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
if i8_ty is None: return 0
|
if i8_ty is None: return 0
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
if i8_ptr_ty is None: return 0
|
if i8_ptr_ty is None: return 0
|
||||||
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
|
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||||
if i8_ptr_ptr_ty is None: return 0
|
if i8_ptr_ptr_ty is None: return 0
|
||||||
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
|
method_count: int = self.Gen.StructGen.get_vtable_method_count(class_name)
|
||||||
if method_count <= 0: return 0
|
if method_count <= 0: return 0
|
||||||
# 构造 vtable 类型 { i8*, i8*, ... }
|
# Build the vtable type { i8*, i8*, ... }; its field list uses mlirlite.ParamNode, not Function*.
|
||||||
# vtable 类型的字段链表是 llvmlite.ParamNode(new_param_node / paramnode_set_next /
|
vt_first: mlirlite.ParamNode* = None
|
||||||
# Struct 形参均为 rawptr[ParamNode])——原先误注解为 Function*。
|
vt_prev: mlirlite.ParamNode* = None
|
||||||
vt_first: llvmlite.ParamNode* = None
|
for vi in range(int(method_count)):
|
||||||
vt_prev: llvmlite.ParamNode* = None
|
pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||||
for vi in range(method_count):
|
|
||||||
pn: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i8_ptr_ty)
|
|
||||||
if pn is None: continue
|
if pn is None: continue
|
||||||
if vt_first is None:
|
if vt_first is None: vt_first = pn
|
||||||
vt_first = pn
|
if vt_prev is not None: mlirlite.paramnode_set_next(vt_prev, pn)
|
||||||
if vt_prev is not None:
|
|
||||||
llvmlite.paramnode_set_next(vt_prev, pn)
|
|
||||||
vt_prev = pn
|
vt_prev = pn
|
||||||
vtable_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, vt_first, method_count, None)
|
vtable_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, vt_first, method_count, None)
|
||||||
if vtable_ty is None:
|
if vtable_ty is None: return 0
|
||||||
return 0
|
# Object vtable slot (object header -> i8**; the vtable pointer is always the first field).
|
||||||
# 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段)
|
vtable_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
|
||||||
vtable_slot: llvmlite.Value* = llvmlite.build_bitcast(builder, obj_ptr, i8_ptr_ptr_ty)
|
|
||||||
if vtable_slot is None: return 0
|
if vtable_slot is None: return 0
|
||||||
vtable_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
vtable_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||||
if vtable_ptr is None: return 0
|
if vtable_ptr is None: return 0
|
||||||
# 类默认 vtable 地址(比较用)
|
# Address of the class default vtable (for comparison).
|
||||||
class_vt: llvmlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
|
class_vt: mlirlite.Value* = self.Gen.CallGenLeafKit._make_vtable_ref_i8(pool, builder, mod, class_name, trans)
|
||||||
if class_vt is None: return 0
|
if class_vt is None: return 0
|
||||||
cmp_vt: llvmlite.Value* = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt)
|
cmp_vt: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, vtable_ptr, class_vt)
|
||||||
if cmp_vt is None: return 0
|
if cmp_vt is None: return 0
|
||||||
# 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如
|
# Unique block names: create_block does not auto-number, so repeated vtable writes in one function would collide.
|
||||||
# devfs open/close/read/write 连续赋值)同名 label 冲突)
|
|
||||||
vtc3: int = trans._label_counter
|
vtc3: int = trans._label_counter
|
||||||
trans._label_counter = vtc3 + 1
|
trans._label_counter = vtc3 + 1
|
||||||
vt_bb_buf3: str = pool.alloc(64)
|
vt_bb_buf3: str = pool.alloc(64)
|
||||||
if vt_bb_buf3 is None: return 0
|
if vt_bb_buf3 is None: return 0
|
||||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
|
viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3)
|
||||||
vt_copy_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
vt_copy_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
||||||
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
|
viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3)
|
||||||
vt_skip_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
vt_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, vt_bb_buf3)
|
||||||
if vt_copy_bb is None or vt_skip_bb is None: return 0
|
if vt_copy_bb is None or vt_skip_bb is None: return 0
|
||||||
llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
|
mlirlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb)
|
||||||
# copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本
|
# copy block: create internal copy global (zeroinit) + memcpy + repoint object at it.
|
||||||
llvmlite.position_at_end(builder, vt_copy_bb)
|
mlirlite.position_at_end(builder, vt_copy_bb)
|
||||||
vtc: int = trans._label_counter
|
vtc: int = trans._label_counter
|
||||||
trans._label_counter = vtc + 1
|
trans._label_counter = vtc + 1
|
||||||
vcopy_buf: str = pool.alloc(256)
|
vcopy_buf: str = pool.alloc(256)
|
||||||
if vcopy_buf is not None:
|
if vcopy_buf is not None:
|
||||||
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
|
viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc)
|
||||||
vcopy_gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, vcopy_buf, vtable_ty)
|
vcopy_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, vcopy_buf, vtable_ty)
|
||||||
if vcopy_gv is not None:
|
if vcopy_gv is not None:
|
||||||
llvmlite.module_add_global(mod, vcopy_gv)
|
mlirlite.module_add_global(mod, vcopy_gv)
|
||||||
vcopy_gv.Linkage = None
|
vcopy_gv.Linkage = None
|
||||||
vcopy_gv.Initializer = "zeroinitializer"
|
vcopy_gv.Initializer = "zeroinitializer"
|
||||||
# 引用名必须带 @ 前缀:SSAValue 名原样输出,裸名
|
# Reference names need the @ prefix: SSAValue emits the name verbatim (unlike new_global_variable, it adds no @).
|
||||||
# devfs_file.vtable.copy.71 无 @ → llc "expected value token"
|
|
||||||
# (devfs vtable.copy 根因;全局定义 @name 是 new_global_variable
|
|
||||||
# 输出时加的,引用侧不自动加)
|
|
||||||
vcopy_ref_nm: str = pool.alloc(272)
|
vcopy_ref_nm: str = pool.alloc(272)
|
||||||
if vcopy_ref_nm is None:
|
if vcopy_ref_nm is None: vcopy_ref_nm = vcopy_buf
|
||||||
vcopy_ref_nm = vcopy_buf
|
else: viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
|
||||||
else:
|
vcopy_ref: mlirlite.Value* = mlirlite.SSAValue(pool, mlirlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
|
||||||
viperlib.snprintf(vcopy_ref_nm, 272, "@%s", vcopy_buf)
|
|
||||||
vcopy_ref: llvmlite.Value* = llvmlite.SSAValue(pool, llvmlite.Ptr(pool, vtable_ty), vcopy_ref_nm)
|
|
||||||
if vcopy_ref is not None:
|
if vcopy_ref is not None:
|
||||||
vcopy_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
|
vcopy_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, vcopy_ref, i8_ptr_ty)
|
||||||
if vcopy_i8 is not None:
|
if vcopy_i8 is not None:
|
||||||
# ConstInt 的 name 必须是数字文本(如 "56"),不能是
|
# ConstInt's name must be numeric text (e.g. "56"); an identifier would emit verbatim without % and break llc.
|
||||||
# 标识符 "vtsz"——参数经 Name 原样输出,裸名 vtsz 无 %
|
|
||||||
# → llc "expected value token"(devfs memcpy 根因)
|
|
||||||
vtsz_buf: str = pool.alloc(32)
|
vtsz_buf: str = pool.alloc(32)
|
||||||
if vtsz_buf is not None:
|
if vtsz_buf is not None:
|
||||||
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
|
viperlib.snprintf(vtsz_buf, 32, "%lld", method_count * 8)
|
||||||
vt_size: llvmlite.Value* = llvmlite.ConstInt(pool, llvmlite.Int64(pool), method_count * 8, vtsz_buf)
|
vt_size: mlirlite.Value* = mlirlite.ConstInt(pool, mlirlite.Int64(pool), method_count * 8, vtsz_buf)
|
||||||
if vt_size is not None:
|
if vt_size is not None: self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
|
||||||
self.Gen.CallGenLib._emit_llvm_memcpy_intrinsic(pool, builder, mod, vcopy_i8, class_vt, vt_size)
|
mlirlite.build_store(builder, vcopy_i8, vtable_slot)
|
||||||
llvmlite.build_store(builder, vcopy_i8, vtable_slot)
|
mlirlite.build_br(builder, vt_skip_bb)
|
||||||
llvmlite.build_br(builder, vt_skip_bb)
|
# skip block: reload vtable -> GEP the slot -> store the function pointer (i8*).
|
||||||
# skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*)
|
mlirlite.position_at_end(builder, vt_skip_bb)
|
||||||
llvmlite.position_at_end(builder, vt_skip_bb)
|
vt_ptr2: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||||
vt_ptr2: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
|
||||||
if vt_ptr2 is None: return 0
|
if vt_ptr2 is None: return 0
|
||||||
vt_typed: llvmlite.Value* = llvmlite.build_bitcast(builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty))
|
vt_typed: mlirlite.Value* = mlirlite.build_bitcast(builder, vt_ptr2, mlirlite.Ptr(pool, vtable_ty))
|
||||||
if vt_typed is None: return 0
|
if vt_typed is None: return 0
|
||||||
vt_slotp: llvmlite.Value* = llvmlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
|
vt_slotp: mlirlite.Value* = mlirlite.build_gep_struct(builder, vtable_ty, i8_ptr_ty, vt_typed, method_index)
|
||||||
if vt_slotp is None: return 0
|
if vt_slotp is None: return 0
|
||||||
fptr_i8: llvmlite.Value* = llvmlite.build_bitcast(builder, func_val, i8_ptr_ty)
|
fptr_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, func_val, i8_ptr_ty)
|
||||||
if fptr_i8 is not None:
|
if fptr_i8 is not None: mlirlite.build_store(builder, fptr_i8, vt_slotp)
|
||||||
llvmlite.build_store(builder, fptr_i8, vt_slotp)
|
|
||||||
return 1
|
return 1
|
||||||
def _write_vtable_attr(self, builder: llvmlite.IRBuilder*,
|
def _write_vtable_attr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||||
pool: memhub.MemBuddy*,
|
"""Handle obj.method = func writes that target a vtable slot."""
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
node: ast.AST*,
|
|
||||||
rhs_val: llvmlite.Value*,
|
|
||||||
trans: HT.Translator*) -> int:
|
|
||||||
"""属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)"""
|
|
||||||
if node is None or builder is None or pool is None or mod is None: return 0
|
if node is None or builder is None or pool is None or mod is None: return 0
|
||||||
if rhs_val is None or trans is None: return 0
|
if rhs_val is None or trans is None: return 0
|
||||||
at: ast.Attribute* = ast.Attribute*(node)
|
at: ast.Attribute* = ast.Attribute*(node)
|
||||||
if at is None or at.value is None or at.attr is None: return 0
|
if at is None or at.value is None or at.attr is None: return 0
|
||||||
# 解析类名(AnnotClassName;去模块前缀)
|
# Resolve the class name (AnnotClassName; strip the module prefix).
|
||||||
class_nm_v: str = None
|
class_nm_v: str = None
|
||||||
if at.value.kind() == ast.ASTKind.Name:
|
if at.value.kind() == ast.ASTKind.Name:
|
||||||
nm_v: ast.Name* = ast.Name*(at.value)
|
nm_v: ast.Name* = ast.Name*(at.value)
|
||||||
if nm_v.id is not None:
|
if nm_v.id is not None:
|
||||||
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
|
ve_v: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_v.id)
|
||||||
if ve_v is not None:
|
if ve_v is not None: class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
|
||||||
class_nm_v = self.Gen.VarGen.var_annot_class_name(ve_v)
|
|
||||||
elif at.value.kind() == ast.ASTKind.Attribute:
|
elif at.value.kind() == ast.ASTKind.Attribute:
|
||||||
# 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenAttr,经 self.Gen 调用
|
# Cross-mixin: _resolve_attr_field_class lives in ExprGenAttr, called via self.Gen.
|
||||||
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
|
class_nm_v = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, at.value)
|
||||||
if class_nm_v is None: return 0
|
if class_nm_v is None: return 0
|
||||||
fd_dot_v: str = string.strrchr(class_nm_v, '.')
|
fd_dot_v: str = string.strrchr(class_nm_v, '.')
|
||||||
if fd_dot_v is not None:
|
if fd_dot_v is not None: class_nm_v = fd_dot_v + 1
|
||||||
class_nm_v = fd_dot_v + 1
|
|
||||||
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
|
if self.Gen.StructGen.get_struct_type(class_nm_v) is None: return 0
|
||||||
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
|
vm_idx_v: int = self.Gen.StructGen.get_vtable_method_index(class_nm_v, at.attr)
|
||||||
if vm_idx_v < 0: return 0
|
if vm_idx_v < 0: return 0
|
||||||
# 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca)
|
# Resolve obj_ptr (same as the first half of get_attribute_ptr: Name looks up the alloca directly).
|
||||||
obj_ptr_v: llvmlite.Value* = None
|
obj_ptr_v: mlirlite.Value* = None
|
||||||
if at.value.kind() == ast.ASTKind.Name:
|
if at.value.kind() == ast.ASTKind.Name: obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
|
||||||
obj_ptr_v = self.Gen.VarGen.lookup_var(trans.SymTab, (ast.Name*(at.value)).id)
|
else: obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||||||
else:
|
|
||||||
obj_ptr_v = self.Gen.ExprGen.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
|
||||||
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
|
if obj_ptr_v is None or obj_ptr_v.Ty is None: return 0
|
||||||
# X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针
|
# X* variable (Ptr(Ptr)): load-dereference to get the object pointer.
|
||||||
match obj_ptr_v.Ty:
|
match obj_ptr_v.Ty:
|
||||||
case llvmlite.LLVMType.Ptr(inner_v):
|
case mlirlite.LLVMType.Ptr(inner_v):
|
||||||
match inner_v:
|
match inner_v:
|
||||||
case llvmlite.LLVMType.Ptr(_):
|
case mlirlite.LLVMType.Ptr(_):
|
||||||
obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v)
|
obj_ptr_v = mlirlite.build_load(builder, inner_v, obj_ptr_v)
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
if obj_ptr_v is None:
|
if obj_ptr_v is None: return 0
|
||||||
return 0
|
|
||||||
return self._write_vtable_slot(pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
|
return self._write_vtable_slot(pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans)
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
from . import ClassGenBase
|
from . import ClassGenBase
|
||||||
|
|
||||||
# ============================================================
|
# ClassGenMixin - class definition / inheritance / generics / vtable / enum / union / renum.
|
||||||
# ClassGenMixin - 类定义/继承/泛型/vtable/enum/union/renum
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
|
novirtual class ClassGenMixin(ClassGenBase.ClassGenBase):
|
||||||
"""类定义处理 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
|
"""Class-definition mixin (top level after the stage-D split; methods live in sub-mixins)."""
|
||||||
File diff suppressed because it is too large.
Load diff
+137
-230
@@ -1,77 +1,54 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
import viperlib
|
import viperlib
|
||||||
import w32.win32file as w32file
|
import win32.win32file as win32file
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.VLogger as VLogger
|
||||||
|
|
||||||
# ============================================================
|
# DebugInfoGen - emits function-level DWARF metadata (mlirlite text IR), mirroring TPC LLVMCG/DebugInfo.py node-for-node.
|
||||||
# DebugInfoGen - function-level DWARF metadata emitter (llvmlite text IR)
|
# Active only under --traceback (SetEnabled); hooks come from the Gen layer. When disabled every entry point returns before touching DI APIs, keeping IR byte-identical.
|
||||||
#
|
|
||||||
# Node layout mirrors TPC lib/core/LLVMCG/DebugInfo.py node-for-node:
|
|
||||||
# !DIFile -> !{ null } -> !DISubroutineType -> distinct !DICompileUnit
|
|
||||||
# -> distinct !DISubprogram (per define) -> !DILocation (per statement)
|
|
||||||
# module tail: !llvm.dbg.cu + !llvm.module.flags (Debug Info Version 3)
|
|
||||||
#
|
|
||||||
# Enabled only while --traceback is on: the Phase layer mirrors
|
|
||||||
# BuildPipeline._traceback_mode here via SetEnabled(). While disabled every
|
|
||||||
# entry point returns before touching any llvmlite DI API, so the emitted IR
|
|
||||||
# stays byte-identical to the pre-DWARF output.
|
|
||||||
#
|
|
||||||
# Scope/statement hooks are driven from the Gen layer:
|
|
||||||
# Translator.translate -> BeginModule / Finalize
|
|
||||||
# FuncGenDef.translate_function_def / ClassGenVTable._translate_method /
|
|
||||||
# FuncGenMixin (nested + lambda) -> PushFunction / PopFunction
|
|
||||||
# StmtGenMixin.translate_stmt -> SetStatement
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ---- module-scope state (reset per module by BeginModule) ----
|
# ---- module-scope state (reset per module by BeginModule) ----
|
||||||
_g_enabled: int = 0
|
_g_enabled: int = 0
|
||||||
_g_finalized: int = 0
|
_g_finalized: int = 0
|
||||||
_g_mod: llvmlite.LLVMModule* = None
|
_g_mod: mlirlite.LLVMModule* = None
|
||||||
_g_file_path: str = None
|
_g_file_path: str = None
|
||||||
|
_g_abs_file_path: str = None
|
||||||
_g_file_id: int = -1
|
_g_file_id: int = -1
|
||||||
_g_null_id: int = -1
|
_g_null_id: int = -1
|
||||||
_g_subtype_id: int = -1
|
_g_subtype_id: int = -1
|
||||||
_g_cu_id: int = -1
|
_g_cu_id: int = -1
|
||||||
_g_cur_sp: int = -1
|
_g_cur_sp: int = -1
|
||||||
_g_cur_entry_ref: str = None
|
_g_cur_entry_ref: str = None
|
||||||
_g_cur_builder: llvmlite.IRBuilder* = None
|
_g_cur_builder: mlirlite.IRBuilder* = None
|
||||||
_g_depth: int = 0
|
_g_depth: int = 0
|
||||||
|
|
||||||
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder)
|
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder) per level; +1 lets 0 mean "no enclosing function".
|
||||||
# per level. +1 so 0 means "no enclosing function" (sp ids are >= 0). The saved
|
# The saved builder makes SetStatement reject a builder not pushed by the current DISubprogram (e.g. an async state machine in a bracketed function); such instrs must carry no !dbg or LLVM's verifier fails.
|
||||||
# builder is what makes SetStatement reject a builder that is not the one the
|
define DBG_STACK_MAX = 256
|
||||||
# current DISubprogram was pushed on (e.g. an async state machine emitted inside
|
|
||||||
# a bracketed function): such instructions must carry no !dbg, otherwise LLVM's
|
|
||||||
# verifier rejects "!dbg attachment points at wrong subprogram for function".
|
|
||||||
DBG_STACK_MAX: t.CDefine = 256
|
|
||||||
_g_stk_sp: sizet* = None
|
_g_stk_sp: sizet* = None
|
||||||
_g_stk_ref: str = None
|
_g_stk_ref: str = None
|
||||||
_g_stk_bld: str = None
|
_g_stk_bld: str = None
|
||||||
|
|
||||||
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
|
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
|
||||||
DBG_LOC_SLOTS: t.CDefine = 8192
|
define DBG_LOC_SLOTS = 8192
|
||||||
DBG_LOC_PROBE: t.CDefine = 8
|
define DBG_LOC_PROBE = 8
|
||||||
DBG_LOC_KEY_STRIDE: t.CDefine = 3
|
define DBG_LOC_KEY_STRIDE = 3
|
||||||
_g_loc_key: sizet* = None
|
_g_loc_key: sizet* = None
|
||||||
_g_loc_ref: str = None
|
_g_loc_ref: str = None
|
||||||
_g_loc_used: bytes = None
|
_g_loc_used: bytes = None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# SetEnabled / IsEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
|
||||||
# SetEnabled / IsEnabled - mirror the --traceback switch
|
|
||||||
# ============================================================
|
|
||||||
def SetEnabled(flag: bool) -> int:
|
def SetEnabled(flag: bool) -> int:
|
||||||
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
|
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
|
||||||
global _g_enabled
|
global _g_enabled
|
||||||
if flag:
|
if flag: _g_enabled = 1
|
||||||
_g_enabled = 1
|
else: _g_enabled = 0
|
||||||
else:
|
# Forward the same switch to mlirlite's debug call-site recorder (under --traceback we emit a <sha1>.vpcall side file for WriteStackMap+objdump).
|
||||||
_g_enabled = 0
|
mlirlite.DebugSetEnabled(flag)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -79,13 +56,10 @@ def IsEnabled() -> int:
|
|||||||
return _g_enabled
|
return _g_enabled
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers.
|
||||||
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers
|
|
||||||
# ============================================================
|
|
||||||
def _EnsureStorage() -> int:
|
def _EnsureStorage() -> int:
|
||||||
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
|
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
|
||||||
if _g_stk_sp is not None:
|
if _g_stk_sp is not None: return 0
|
||||||
return 0
|
|
||||||
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||||
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||||
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
|
||||||
@@ -93,7 +67,7 @@ def _EnsureStorage() -> int:
|
|||||||
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
|
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
|
||||||
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
|
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
|
||||||
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
|
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
|
||||||
VLogger.error("--traceback: DWARF 元数据缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DWARF metadata buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
|
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
|
||||||
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
|
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
|
||||||
@@ -113,25 +87,23 @@ def _EnsureStorage() -> int:
|
|||||||
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
|
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
|
||||||
"""Allocate a persistent "!N" reference string for a DI node id."""
|
"""Allocate a persistent "!N" reference string for a DI node id."""
|
||||||
buf: str = pool.alloc(24)
|
buf: str = pool.alloc(24)
|
||||||
if buf is None:
|
if buf is None: return None
|
||||||
return None
|
|
||||||
viperlib.snprintf(buf, 24, "!%d", node_id)
|
viperlib.snprintf(buf, 24, "!%d", node_id)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# BeginModule - start of one module translation.
|
||||||
# BeginModule - start of one module translation
|
def BeginModule(mod: mlirlite.LLVMModule*, file_path: str) -> int:
|
||||||
# ============================================================
|
|
||||||
def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
|
|
||||||
"""Reset per-module DI state. Call once at the start of each module."""
|
"""Reset per-module DI state. Call once at the start of each module."""
|
||||||
global _g_mod, _g_file_path, _g_finalized, _g_file_id, _g_null_id
|
global _g_mod, _g_file_path, _g_abs_file_path, _g_finalized, _g_file_id, _g_null_id
|
||||||
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||||
if _g_enabled == 0:
|
if _g_enabled == 0: return 0
|
||||||
return 0
|
if _EnsureStorage() != 0: return 1
|
||||||
if _EnsureStorage() != 0:
|
|
||||||
return 1
|
|
||||||
_g_mod = mod
|
_g_mod = mod
|
||||||
_g_file_path = file_path
|
_g_file_path = file_path
|
||||||
|
_g_abs_file_path = None
|
||||||
|
# Module-boundary fallback: clear any un-Pop'd debug function context (mid-translation failure) so the previous module's caller is not attributed here.
|
||||||
|
mlirlite.DebugResetFunc()
|
||||||
_g_finalized = 0
|
_g_finalized = 0
|
||||||
_g_file_id = -1
|
_g_file_id = -1
|
||||||
_g_null_id = -1
|
_g_null_id = -1
|
||||||
@@ -141,62 +113,46 @@ def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
|
|||||||
_g_cur_entry_ref = None
|
_g_cur_entry_ref = None
|
||||||
_g_cur_builder = None
|
_g_cur_builder = None
|
||||||
_g_depth = 0
|
_g_depth = 0
|
||||||
if _g_loc_used is not None:
|
if _g_loc_used is not None: string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
|
||||||
string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# _EnsureModuleNodes - lazily create DIFile / !{ null } / DISubroutineType / DICompileUnit at the first define (once per module).
|
||||||
# _EnsureModuleNodes - DIFile / !{ null } / DISubroutineType / DICompileUnit
|
|
||||||
# (created lazily at the first define, once per module)
|
|
||||||
# ============================================================
|
|
||||||
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||||
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id
|
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id, _g_abs_file_path
|
||||||
if _g_cu_id >= 0:
|
if _g_cu_id >= 0: return 0
|
||||||
return 0
|
mod: mlirlite.LLVMModule* = _g_mod
|
||||||
mod: llvmlite.LLVMModule* = _g_mod
|
if mod is None: return 1
|
||||||
if mod is None:
|
# DIFile: absolute path (forward slashes), filename keeps the whole path.
|
||||||
return 1
|
# Must be absolute: relative paths make LLVM's DWARF line records degrade to a bare "main.vp" that runtime cannot reload; absolutizing works whether LLVM keeps the full name or splits dir+basename.
|
||||||
# DIFile: **绝对**路径(正斜杠),filename 保留整条路径。
|
|
||||||
# 为什么必须绝对:本工程给 CurrentFile 的是**项目相对**路径(如
|
|
||||||
# "./Test/LLvmLiteTest/App/main.vp"),LLVM 会把该目录写成 CU 的 DW_AT_comp_dir,
|
|
||||||
# 而主文件条目用 `dir_index: 0` 指向 comp_dir;stackmap 写入端只解析
|
|
||||||
# `llvm-dwarfdump --debug-line`(其中**没有** comp_dir)⇒ 行记录只能退化成裸文件名
|
|
||||||
# "main.vp":运行期既显示不全,也无法按路径重读源码行(实证:曾整段栈都打印
|
|
||||||
# `File "main.vp", line N` 而无源码行)。
|
|
||||||
# 绝对化后,无论 LLVM 保留整条 name(写入端 `_tb_is_abs_path` 命中 ⇒ 原样采用)
|
|
||||||
# 还是把它拆成 dir+basename(dir_index 指向同一个绝对目录 ⇒ 拼回绝对路径),
|
|
||||||
# 结果都是绝对路径 —— 故本改动不需要动写入端。
|
|
||||||
src: str = _g_file_path
|
src: str = _g_file_path
|
||||||
if src is None:
|
if src is None: src = ""
|
||||||
src = ""
|
|
||||||
slen: sizet = string.strlen(src)
|
slen: sizet = string.strlen(src)
|
||||||
cap_n: sizet = slen + 4112
|
cap_n: sizet = slen + 4112
|
||||||
nbuf: str = pool.alloc(cap_n)
|
nbuf: str = pool.alloc(cap_n)
|
||||||
if nbuf is None:
|
if nbuf is None:
|
||||||
VLogger.error("--traceback: DIFile 路径缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DIFile path buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
if slen > 0:
|
if slen > 0: string.strncpy(nbuf, src, slen)
|
||||||
string.strncpy(nbuf, src, slen)
|
|
||||||
nbuf[slen] = '\0'
|
nbuf[slen] = '\0'
|
||||||
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
|
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
|
||||||
# 相对路径 → 前置当前工作目录。用 w32file 低层调用(同 Repl.vp),
|
# Relative path -> prefix CWD via a win32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
|
||||||
# 不经 os._mbuddy:编译器进程未必初始化过那个内存管理器。
|
|
||||||
CwdBuf: bytes = pool.alloc(4112)
|
CwdBuf: bytes = pool.alloc(4112)
|
||||||
if CwdBuf is not None:
|
if CwdBuf is not None:
|
||||||
CwdBuf[0] = '\0'
|
CwdBuf[0] = '\0'
|
||||||
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
nc: ULONG = win32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
||||||
if nc > 0:
|
if nc > 0:
|
||||||
# 防御:缓冲区不足时 GetCurrentDirectoryA 的写入内容未定义,强制补 NUL
|
# Defensive: when the buffer is too small GetCurrentDirectoryA writes undefined content; force a NUL.
|
||||||
CwdBuf[4110] = '\0'
|
CwdBuf[4110] = '\0'
|
||||||
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
|
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
|
||||||
# 归一所有反斜杠(CWD 段同样可能含 '\')
|
# Normalize all backslashes (the CWD segment may contain '\' too).
|
||||||
flen: sizet = string.strlen(nbuf)
|
flen: sizet = string.strlen(nbuf)
|
||||||
ni: sizet = 0
|
ni: sizet = 0
|
||||||
for ni in range(flen):
|
for ni in range(int(flen)):
|
||||||
if nbuf[ni] == '\\':
|
if nbuf[ni] == '\\': nbuf[ni] = '/'
|
||||||
nbuf[ni] = '/'
|
# Save the absolute path for mlirlite's debug call-site recorder (.vpcall file paths must match DIFile: absolute + forward slash).
|
||||||
# filename 取整条路径;directory 另存其目录前缀(两段各自 NUL 结尾,互不污染)
|
_g_abs_file_path = nbuf
|
||||||
|
# filename = whole path; directory = its prefix (both NUL-terminated, non-overlapping).
|
||||||
fname: str = nbuf
|
fname: str = nbuf
|
||||||
directory: str = ""
|
directory: str = ""
|
||||||
slash: str = string.strrchr(nbuf, '/')
|
slash: str = string.strrchr(nbuf, '/')
|
||||||
@@ -210,87 +166,74 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
|||||||
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
|
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
|
||||||
dbuf: str = pool.alloc(dsize)
|
dbuf: str = pool.alloc(dsize)
|
||||||
if dbuf is None:
|
if dbuf is None:
|
||||||
VLogger.error("--traceback: DIFile 节点缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DIFile node buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
|
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
|
||||||
fid: int = llvmlite.module_add_di_node(mod, pool, dbuf)
|
fid: int = mlirlite.module_add_di_node(mod, pool, dbuf)
|
||||||
if fid < 0:
|
if fid < 0:
|
||||||
VLogger.error("--traceback: DIFile 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DIFile metadata node, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
_g_file_id = fid
|
_g_file_id = fid
|
||||||
# types: !{ null } placeholder list, then DISubroutineType referencing it
|
# types: !{ null } placeholder list, then DISubroutineType referencing it
|
||||||
nid: int = llvmlite.module_add_di_node(mod, pool, "!{ null }")
|
nid: int = mlirlite.module_add_di_node(mod, pool, "!{ null }")
|
||||||
if nid < 0:
|
if nid < 0:
|
||||||
VLogger.error("--traceback: DISubroutineType 类型表分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubroutineType type table, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
_g_null_id = nid
|
_g_null_id = nid
|
||||||
tbuf: str = pool.alloc(64)
|
tbuf: str = pool.alloc(64)
|
||||||
if tbuf is None:
|
if tbuf is None:
|
||||||
VLogger.error("--traceback: DISubroutineType 缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubroutineType buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
|
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
|
||||||
tid: int = llvmlite.module_add_di_node(mod, pool, tbuf)
|
tid: int = mlirlite.module_add_di_node(mod, pool, tbuf)
|
||||||
if tid < 0:
|
if tid < 0:
|
||||||
VLogger.error("--traceback: DISubroutineType 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubroutineType metadata node, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
_g_subtype_id = tid
|
_g_subtype_id = tid
|
||||||
cbuf: str = pool.alloc(256)
|
cbuf: str = pool.alloc(256)
|
||||||
if cbuf is None:
|
if cbuf is None:
|
||||||
VLogger.error("--traceback: DICompileUnit 缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DICompileUnit buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
viperlib.snprintf(cbuf, 256,
|
viperlib.snprintf(cbuf, 256, "distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)", _g_file_id)
|
||||||
"distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)",
|
cid: int = mlirlite.module_add_di_node(mod, pool, cbuf)
|
||||||
_g_file_id)
|
|
||||||
cid: int = llvmlite.module_add_di_node(mod, pool, cbuf)
|
|
||||||
if cid < 0:
|
if cid < 0:
|
||||||
VLogger.error("--traceback: DICompileUnit 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DICompileUnit metadata node, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
_g_cu_id = cid
|
_g_cu_id = cid
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# _LocRefAt - read a cached DILocation ref string pointer.
|
||||||
# _LocRefAt - read a cached DILocation ref string pointer
|
|
||||||
# ============================================================
|
|
||||||
def _LocRefAt(slot: int) -> str:
|
def _LocRefAt(slot: int) -> str:
|
||||||
slotp: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot) * 8)
|
slotp: str* = (str*)(uint64(_g_loc_ref) + sizet(slot) * 8)
|
||||||
r: str = slotp[0]
|
r: str = slotp[0]
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once.
|
||||||
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once
|
|
||||||
# ============================================================
|
|
||||||
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
|
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
|
||||||
if line <= 0:
|
if line <= 0: return _g_cur_entry_ref
|
||||||
return _g_cur_entry_ref
|
|
||||||
c: int = col
|
c: int = col
|
||||||
if c <= 0:
|
if c <= 0: c = 1
|
||||||
c = 1
|
if _g_loc_used is None or _g_mod is None: return None
|
||||||
if _g_loc_used is None or _g_mod is None:
|
|
||||||
return None
|
|
||||||
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
|
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
|
||||||
pi: int = 0
|
pi: int = 0
|
||||||
while pi < DBG_LOC_PROBE:
|
while pi < DBG_LOC_PROBE:
|
||||||
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
||||||
if _g_loc_used[slot] == 0:
|
if _g_loc_used[slot] == 0: break
|
||||||
break
|
|
||||||
kbase: int = slot * DBG_LOC_KEY_STRIDE
|
kbase: int = slot * DBG_LOC_KEY_STRIDE
|
||||||
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line \
|
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line and int(_g_loc_key[kbase + 2]) == c: return _LocRefAt(slot)
|
||||||
and int(_g_loc_key[kbase + 2]) == c:
|
|
||||||
return _LocRefAt(slot)
|
|
||||||
pi += 1
|
pi += 1
|
||||||
lbuf: str = pool.alloc(96)
|
lbuf: str = pool.alloc(96)
|
||||||
if lbuf is None:
|
if lbuf is None: return None
|
||||||
return None
|
|
||||||
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
|
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
|
||||||
lid: int = llvmlite.module_add_di_node(_g_mod, pool, lbuf)
|
lid: int = mlirlite.module_add_di_node(_g_mod, pool, lbuf)
|
||||||
if lid < 0:
|
if lid < 0:
|
||||||
VLogger.error("--traceback: DILocation 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DILocation metadata node, aborting compilation", "DWARF")
|
||||||
return None
|
return None
|
||||||
lref: str = _RefOf(pool, lid)
|
lref: str = _RefOf(pool, lid)
|
||||||
if lref is None:
|
if lref is None:
|
||||||
VLogger.error("--traceback: DILocation 引用缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DILocation reference buffer, aborting compilation", "DWARF")
|
||||||
return None
|
return None
|
||||||
if pi < DBG_LOC_PROBE:
|
if pi < DBG_LOC_PROBE:
|
||||||
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
|
||||||
@@ -298,174 +241,138 @@ def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str
|
|||||||
_g_loc_key[kbase2] = sizet(sp)
|
_g_loc_key[kbase2] = sizet(sp)
|
||||||
_g_loc_key[kbase2 + 1] = sizet(line)
|
_g_loc_key[kbase2 + 1] = sizet(line)
|
||||||
_g_loc_key[kbase2 + 2] = sizet(c)
|
_g_loc_key[kbase2 + 2] = sizet(c)
|
||||||
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot2) * 8)
|
rslot: str* = (str*)(uint64(_g_loc_ref) + sizet(slot2) * 8)
|
||||||
rslot[0] = lref
|
rslot[0] = lref
|
||||||
_g_loc_used[slot2] = 1
|
_g_loc_used[slot2] = 1
|
||||||
return lref
|
return lref
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# PushFunction - create the DISubprogram for one define.
|
||||||
# PushFunction - create the DISubprogram for one define
|
def PushFunction(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, func: mlirlite.Function*, line: int, col: int) -> int:
|
||||||
# ============================================================
|
"""Attach a DISubprogram to one define and set the builder's entry DILocation."""
|
||||||
def PushFunction(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
|
|
||||||
func: llvmlite.Function*, line: int, col: int) -> int:
|
|
||||||
"""Attach a DISubprogram to one define and set the builder's entry DILocation.
|
|
||||||
|
|
||||||
Returns 1 when pushed (caller must call PopFunction), 0 when DI is disabled
|
|
||||||
or not applicable. Every instruction emitted afterwards on `builder` carries
|
|
||||||
", !dbg !N" until the next SetStatement.
|
|
||||||
"""
|
|
||||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||||
if _g_enabled == 0:
|
if _g_enabled == 0: return 0
|
||||||
return 0
|
if pool is None or builder is None or func is None or _g_mod is None: return 0
|
||||||
if pool is None or builder is None or func is None or _g_mod is None:
|
|
||||||
return 0
|
|
||||||
if _g_depth >= DBG_STACK_MAX:
|
if _g_depth >= DBG_STACK_MAX:
|
||||||
VLogger.error("--traceback: DWARF 函数嵌套深度超上限,终止编译", "DWARF")
|
VLogger.error("--traceback: DWARF function nesting depth exceeds the limit, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
if _EnsureModuleNodes(pool) != 0:
|
if _EnsureModuleNodes(pool) != 0: return 0
|
||||||
return 0
|
name: str = mlirlite.function_get_name(func)
|
||||||
name: str = llvmlite.function_get_name(func)
|
|
||||||
if name is None:
|
if name is None:
|
||||||
VLogger.error("--traceback: 函数名为空,无法发射 DISubprogram,终止编译", "DWARF")
|
VLogger.error("--traceback: function name is empty, cannot emit DISubprogram, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
dline: int = line
|
dline: int = line
|
||||||
if dline <= 0:
|
if dline <= 0: dline = 1
|
||||||
dline = 1
|
|
||||||
dcol: int = col
|
dcol: int = col
|
||||||
if dcol <= 0:
|
if dcol <= 0: dcol = 1
|
||||||
dcol = 1
|
|
||||||
ssize: sizet = string.strlen(name) + 320
|
ssize: sizet = string.strlen(name) + 320
|
||||||
spbuf: str = pool.alloc(ssize)
|
spbuf: str = pool.alloc(ssize)
|
||||||
if spbuf is None:
|
if spbuf is None:
|
||||||
VLogger.error("--traceback: DISubprogram 缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubprogram buffer, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
viperlib.snprintf(spbuf, ssize,
|
viperlib.snprintf(spbuf, ssize, "distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)", name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
|
||||||
"distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)",
|
sp_id: int = mlirlite.module_add_di_node(_g_mod, pool, spbuf)
|
||||||
name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
|
|
||||||
sp_id: int = llvmlite.module_add_di_node(_g_mod, pool, spbuf)
|
|
||||||
if sp_id < 0:
|
if sp_id < 0:
|
||||||
VLogger.error("--traceback: DISubprogram 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubprogram metadata node, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
sp_ref: str = _RefOf(pool, sp_id)
|
sp_ref: str = _RefOf(pool, sp_id)
|
||||||
if sp_ref is None:
|
if sp_ref is None:
|
||||||
VLogger.error("--traceback: DISubprogram 引用缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DISubprogram reference buffer, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
llvmlite.function_set_dbg(func, sp_ref)
|
mlirlite.function_set_dbg(func, sp_ref)
|
||||||
# save the enclosing function's DI context (LIFO), then switch to this one
|
# save the enclosing function's DI context (LIFO), then switch to this one
|
||||||
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
|
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
|
||||||
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
||||||
rslot[0] = _g_cur_entry_ref
|
rslot[0] = _g_cur_entry_ref
|
||||||
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
||||||
bslot[0] = _g_cur_builder
|
bslot[0] = _g_cur_builder
|
||||||
_g_depth += 1
|
_g_depth += 1
|
||||||
_g_cur_sp = sp_id
|
_g_cur_sp = sp_id
|
||||||
_g_cur_builder = builder
|
_g_cur_builder = builder
|
||||||
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
|
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
|
||||||
if entry_ref is None:
|
if entry_ref is None:
|
||||||
VLogger.error("--traceback: 入口 DILocation 生成失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to generate entry DILocation, aborting compilation", "DWARF")
|
||||||
return 0
|
return 0
|
||||||
_g_cur_entry_ref = entry_ref
|
_g_cur_entry_ref = entry_ref
|
||||||
llvmlite.builder_set_debug_location(builder, entry_ref)
|
mlirlite.builder_set_debug_location(builder, entry_ref)
|
||||||
|
# Enter this define: tell mlirlite's call-site recorder the current symbol/file/def-line and write an F record (for the .vpcall file + WriteStackMap entry fallback line).
|
||||||
|
mlirlite.DebugNoteFunc(name, _g_abs_file_path, dline)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# PopFunction - restore the enclosing function's DI context.
|
||||||
# PopFunction - restore the enclosing function's DI context
|
def PopFunction(builder: mlirlite.IRBuilder*) -> int:
|
||||||
# ============================================================
|
|
||||||
def PopFunction(builder: llvmlite.IRBuilder*) -> int:
|
|
||||||
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
|
||||||
if _g_enabled == 0:
|
if _g_enabled == 0: return 0
|
||||||
return 0
|
if _g_depth <= 0: return 0
|
||||||
if _g_depth <= 0:
|
|
||||||
return 0
|
|
||||||
_g_depth -= 1
|
_g_depth -= 1
|
||||||
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
|
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
|
||||||
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
rslot: str* = (str*)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
||||||
_g_cur_entry_ref = rslot[0]
|
_g_cur_entry_ref = rslot[0]
|
||||||
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
bslot: str* = (str*)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
|
||||||
_g_cur_builder = (llvmlite.IRBuilder*)(bslot[0])
|
_g_cur_builder = (mlirlite.IRBuilder*)(bslot[0])
|
||||||
if builder is not None:
|
if builder is not None: mlirlite.builder_clear_debug_location(builder)
|
||||||
llvmlite.builder_clear_debug_location(builder)
|
# Leave this define: restore mlirlite's outer function context (for nested defines).
|
||||||
|
mlirlite.DebugClearFunc()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# SetStatement - point the current builder at a statement DILocation.
|
||||||
# SetStatement - point the current builder at a statement DILocation
|
def SetStatement(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, line: int, col: int) -> int:
|
||||||
# ============================================================
|
"""Update the builder's DILocation to the current statement (line precision)."""
|
||||||
def SetStatement(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
|
if _g_enabled == 0 or pool is None or builder is None: return 0
|
||||||
line: int, col: int) -> int:
|
# Record the current statement line: C records from build_call in this statement use it as the call-site line.
|
||||||
"""Update the builder's DILocation to the current statement (line precision).
|
mlirlite.DebugNoteLine(line)
|
||||||
|
if _g_cur_sp < 0: return 0
|
||||||
No-op outside a DI-attached define (module-level code / synthetic defines),
|
|
||||||
so instructions outside a function body never reference a function's scope.
|
|
||||||
"""
|
|
||||||
if _g_enabled == 0 or pool is None or builder is None:
|
|
||||||
return 0
|
|
||||||
if _g_cur_sp < 0:
|
|
||||||
return 0
|
|
||||||
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
|
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
|
||||||
if ref is None:
|
if ref is None: return 0
|
||||||
return 0
|
mlirlite.builder_set_debug_location(builder, ref)
|
||||||
llvmlite.builder_set_debug_location(builder, ref)
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
|
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
|
||||||
# ============================================================
|
def _ModuleHasDefine(mod: mlirlite.LLVMModule*) -> int:
|
||||||
def _ModuleHasDefine(mod: llvmlite.LLVMModule*) -> int:
|
if mod is None: return 0
|
||||||
if mod is None:
|
cur: mlirlite.Function* = mod.FuncHead
|
||||||
return 0
|
|
||||||
cur: llvmlite.Function* = mod.FuncHead
|
|
||||||
steps: int = 0
|
steps: int = 0
|
||||||
while cur is not None:
|
while cur is not None:
|
||||||
steps += 1
|
steps += 1
|
||||||
if steps > 100000:
|
if steps > 100000: break
|
||||||
break
|
if mlirlite.function_is_declared(cur) == 0: return 1
|
||||||
if llvmlite.function_is_declared(cur) == 0:
|
cur = mlirlite.function_get_next(cur)
|
||||||
return 1
|
|
||||||
cur = llvmlite.function_get_next(cur)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir.
|
||||||
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir
|
|
||||||
# ============================================================
|
|
||||||
def Finalize(pool: memhub.MemBuddy*) -> int:
|
def Finalize(pool: memhub.MemBuddy*) -> int:
|
||||||
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
|
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
|
||||||
global _g_finalized
|
global _g_finalized
|
||||||
if _g_enabled == 0:
|
if _g_enabled == 0: return 0
|
||||||
return 0
|
if _g_finalized != 0: return 0
|
||||||
if _g_finalized != 0:
|
mod: mlirlite.LLVMModule* = _g_mod
|
||||||
return 0
|
if mod is None or pool is None: return 0
|
||||||
mod: llvmlite.LLVMModule* = _g_mod
|
|
||||||
if mod is None or pool is None:
|
|
||||||
return 0
|
|
||||||
_g_finalized = 1
|
_g_finalized = 1
|
||||||
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
|
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
|
||||||
# define but carries no DI at all loses line info for its frames.
|
# define but carries no DI at all loses line info for its frames.
|
||||||
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0:
|
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0: VLogger.warning("--traceback: this module emits a define but its IR carries no DWARF (!llvm.dbg.cu); its frames will show only addresses in the Ctrl+C stack", "DWARF")
|
||||||
VLogger.warning("--traceback: 本模块发射了 define 但 IR 未携带 DWARF (!llvm.dbg.cu)——该模块帧在 Ctrl+C 栈里将只显示地址", "DWARF")
|
if _g_cu_id < 0: return 0
|
||||||
if _g_cu_id < 0:
|
|
||||||
return 0
|
|
||||||
cu_ref: str = _RefOf(pool, _g_cu_id)
|
cu_ref: str = _RefOf(pool, _g_cu_id)
|
||||||
if cu_ref is None:
|
if cu_ref is None:
|
||||||
VLogger.error("--traceback: DICompileUnit 引用缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate DICompileUnit reference buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
llvmlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
|
mlirlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
|
||||||
fbuf: str = pool.alloc(64)
|
fbuf: str = pool.alloc(64)
|
||||||
if fbuf is None:
|
if fbuf is None:
|
||||||
VLogger.error("--traceback: 模块级调试标志缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate module-level debug flags buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
|
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
|
||||||
flags_id: int = llvmlite.module_add_di_node(mod, pool, fbuf)
|
flags_id: int = mlirlite.module_add_di_node(mod, pool, fbuf)
|
||||||
if flags_id < 0:
|
if flags_id < 0:
|
||||||
VLogger.error("--traceback: llvm.module.flags 元数据节点分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate llvm.module.flags metadata node, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
fl_ref: str = _RefOf(pool, flags_id)
|
fl_ref: str = _RefOf(pool, flags_id)
|
||||||
if fl_ref is None:
|
if fl_ref is None:
|
||||||
VLogger.error("--traceback: llvm.module.flags 引用缓冲分配失败,终止编译", "DWARF")
|
VLogger.error("--traceback: failed to allocate llvm.module.flags reference buffer, aborting compilation", "DWARF")
|
||||||
return 1
|
return 1
|
||||||
llvmlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
|
mlirlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
|
||||||
return 0
|
return 0
|
||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+919
-1193
File diff suppressed because it is too large.
Load diff
+108
-186
@@ -1,7 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
@@ -15,42 +14,31 @@ import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
|||||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# docstring registry capacity constants (kept in sync with ExprGenMixin)
|
||||||
# docstring 登记表容量常量(与 ExprGenMixin 保持一致,存疑
|
|
||||||
# ============================================================
|
|
||||||
define DOC_MAX = 512
|
define DOC_MAX = 512
|
||||||
define DOC_KEY_SZ = 192
|
define DOC_KEY_SZ = 192
|
||||||
define DOC_VAL_SZ = 4096
|
define DOC_VAL_SZ = 4096
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ExprGenCls - split of ExprGenMixin (stage D): class-name inference / docstring registry
|
||||||
# ExprGenCls - ExprGenMixin 拆分(阶段 D):类名推断/docstring 登记
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||||
"""类名推断/docstring 登记"""
|
"""Expression class inference and docstring registry."""
|
||||||
|
|
||||||
# ============================================================
|
# docstring extract/register/lookup (__doc__ support): pre-registration scans
|
||||||
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
|
# ASTs into a global "{module_sha1}.{symbol}" table; lookups inline the string.
|
||||||
#
|
|
||||||
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
|
|
||||||
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
|
|
||||||
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
|
|
||||||
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
|
|
||||||
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
|
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
|
||||||
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
|
"""Extract a docstring from a body when body[0] is an Expr(Constant str)."""
|
||||||
if body is None or len(body) == 0: return None
|
if body is None or len(body) == 0: return None
|
||||||
first: ast.AST* = body.get(0)
|
first: ast.AST* = body.get(0)
|
||||||
if first is None or first.kind() != ast.ASTKind.Expr: return None
|
if first is None or first.kind() != ast.ASTKind.Expr: return None
|
||||||
ex: rawptr[ast.Expr] = rawptr[ast.Expr](first)
|
ex: ast.Expr* = ast.Expr*(first)
|
||||||
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: return None
|
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: return None
|
||||||
cn: ast.Constant* = ast.Constant*(ex.value)
|
cn: ast.Constant* = ast.Constant*(ex.value)
|
||||||
if cn is None or cn.const_kind != ast.CONST_STR: return None
|
if cn is None or cn.const_kind != ast.CONST_STR: return None
|
||||||
return cn.str_val
|
return cn.str_val
|
||||||
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
||||||
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
|
"""Register a docstring under key "{module_sha1}.{symbol}", deduplicated."""
|
||||||
if module_sha1 is None or symbol is None or doc is None: return
|
if module_sha1 is None or symbol is None or doc is None: return
|
||||||
if string.strlen(doc) == 0: return
|
if string.strlen(doc) == 0: return
|
||||||
if self.DocKeys is None:
|
if self.DocKeys is None:
|
||||||
@@ -65,7 +53,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||||
if key_buf is None: return
|
if key_buf is None: return
|
||||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||||
for i in range(self.DocCount):
|
for i in range(int(self.DocCount)):
|
||||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||||
stdlib.free(key_buf)
|
stdlib.free(key_buf)
|
||||||
return
|
return
|
||||||
@@ -74,26 +62,26 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
self.DocCount += 1
|
self.DocCount += 1
|
||||||
stdlib.free(key_buf)
|
stdlib.free(key_buf)
|
||||||
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
||||||
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
|
"""Look up a docstring by "{module_sha1}.{symbol}", or None on miss."""
|
||||||
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
|
if self.DocKeys is None or module_sha1 is None or symbol is None: return None
|
||||||
ml: sizet = string.strlen(module_sha1)
|
ml: sizet = string.strlen(module_sha1)
|
||||||
sl: sizet = string.strlen(symbol)
|
sl: sizet = string.strlen(symbol)
|
||||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||||
if key_buf is None: return None
|
if key_buf is None: return None
|
||||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||||
for i in range(self.DocCount):
|
for i in range(int(self.DocCount)):
|
||||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||||
stdlib.free(key_buf)
|
stdlib.free(key_buf)
|
||||||
return self.DocValues + i * DOC_VAL_SZ
|
return self.DocValues + i * DOC_VAL_SZ
|
||||||
stdlib.free(key_buf)
|
stdlib.free(key_buf)
|
||||||
return None
|
return None
|
||||||
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
|
def extract_module_docstrings(self, tree: ast.AST*, module_sha1: str) -> None:
|
||||||
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
|
"""Scan a module AST and register docstrings of its functions, classes and methods."""
|
||||||
if tree is None or module_sha1 is None: return
|
if tree is None or module_sha1 is None: return
|
||||||
ch: list[ast.AST*]* = tree.children
|
ch: list[ast.AST*]* = tree.children
|
||||||
if ch is None: return
|
if ch is None: return
|
||||||
cn: sizet = len(ch)
|
cn: sizet = len(ch)
|
||||||
for ci in range(cn):
|
for ci in range(int(cn)):
|
||||||
child: ast.AST* = ch.get(ci)
|
child: ast.AST* = ch.get(ci)
|
||||||
if child is None: continue
|
if child is None: continue
|
||||||
kd: int = child.kind()
|
kd: int = child.kind()
|
||||||
@@ -102,20 +90,18 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
if fd is not None and fd.name is not None:
|
if fd is not None and fd.name is not None:
|
||||||
fd_body: list[ast.AST*]* = fd.children
|
fd_body: list[ast.AST*]* = fd.children
|
||||||
fdoc: str = self._extract_docstring_from_body(fd_body)
|
fdoc: str = self._extract_docstring_from_body(fd_body)
|
||||||
if fdoc is not None:
|
if fdoc is not None: self.register_docstring(module_sha1, fd.name, fdoc)
|
||||||
self.register_docstring(module_sha1, fd.name, fdoc)
|
|
||||||
elif kd == ast.ASTKind.ClassDef:
|
elif kd == ast.ASTKind.ClassDef:
|
||||||
cd: ast.ClassDef* = ast.ClassDef*(child)
|
cd: ast.ClassDef* = ast.ClassDef*(child)
|
||||||
if cd is None or cd.name is None: continue
|
if cd is None or cd.name is None: continue
|
||||||
cd_body0: list[ast.AST*]* = cd.children
|
cd_body0: list[ast.AST*]* = cd.children
|
||||||
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
||||||
if cdoc is not None:
|
if cdoc is not None: self.register_docstring(module_sha1, cd.name, cdoc)
|
||||||
self.register_docstring(module_sha1, cd.name, cdoc)
|
# class method docstring (key="{ClassName}.{method}")
|
||||||
# 类方法 docstring(key="{ClassName}.{method}")
|
|
||||||
cd_body: list[ast.AST*]* = cd.children
|
cd_body: list[ast.AST*]* = cd.children
|
||||||
if cd_body is not None:
|
if cd_body is not None:
|
||||||
cbn: sizet = len(cd_body)
|
cbn: sizet = len(cd_body)
|
||||||
for mi in range(cbn):
|
for mi in range(int(cbn)):
|
||||||
m: ast.AST* = cd_body.get(mi)
|
m: ast.AST* = cd_body.get(mi)
|
||||||
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
|
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
|
||||||
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
|
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
|
||||||
@@ -129,53 +115,39 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
|
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
|
||||||
self.register_docstring(module_sha1, sym_buf, mdoc)
|
self.register_docstring(module_sha1, sym_buf, mdoc)
|
||||||
stdlib.free(sym_buf)
|
stdlib.free(sym_buf)
|
||||||
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str,
|
def _resolve_doc_symbol_name(self, trans: HT.Translator*, name_id: str, out_mod: str, out_sym: str) -> int:
|
||||||
out_mod: str, out_sym: str) -> int:
|
"""Resolve a Name symbol's docstring owner into out_mod/out_sym; return 1 on success."""
|
||||||
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
|
|
||||||
|
|
||||||
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
|
|
||||||
"""
|
|
||||||
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
|
if trans is None or name_id is None or out_mod is None or out_sym is None: return 0
|
||||||
if trans.ModuleSha1 is None: return 0
|
if trans.ModuleSha1 is None: return 0
|
||||||
# 1. 本地变量(函数变量/全局变量)
|
# 1. local variable (function/global)
|
||||||
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
|
if self.Gen.VarGen.lookup_var(trans.SymTab, name_id) is not None:
|
||||||
string.strcpy(out_mod, trans.ModuleSha1)
|
string.strcpy(out_mod, trans.ModuleSha1)
|
||||||
string.strcpy(out_sym, name_id)
|
string.strcpy(out_sym, name_id)
|
||||||
return 1
|
return 1
|
||||||
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
|
# 2. struct class (entry.ModuleSha1 distinguishes cross-module pre-registration)
|
||||||
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
|
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(name_id)
|
||||||
if se is not None:
|
if se is not None:
|
||||||
if se.ModuleSha1 is not None:
|
if se.ModuleSha1 is not None: string.strcpy(out_mod, se.ModuleSha1)
|
||||||
string.strcpy(out_mod, se.ModuleSha1)
|
else: string.strcpy(out_mod, trans.ModuleSha1)
|
||||||
else:
|
|
||||||
string.strcpy(out_mod, trans.ModuleSha1)
|
|
||||||
string.strcpy(out_sym, name_id)
|
string.strcpy(out_sym, name_id)
|
||||||
return 1
|
return 1
|
||||||
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
|
# 3. module name (rare); use StubMerger module-level functions to avoid a
|
||||||
# 跨 Mixin self 调用生成不存在符号)
|
# cross-Mixin self call that would emit a nonexistent symbol.
|
||||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
|
mod_sha1: str = StubMergerStore.LookupSha1ByModName(name_id)
|
||||||
if mod_sha1 is None:
|
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
|
||||||
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(name_id)
|
|
||||||
if mod_sha1 is not None:
|
if mod_sha1 is not None:
|
||||||
string.strcpy(out_mod, mod_sha1)
|
string.strcpy(out_mod, mod_sha1)
|
||||||
string.strcpy(out_sym, name_id)
|
string.strcpy(out_sym, name_id)
|
||||||
return 1
|
return 1
|
||||||
# 4. 兜底当前模块
|
# 4. fall back to the current module
|
||||||
string.strcpy(out_mod, trans.ModuleSha1)
|
string.strcpy(out_mod, trans.ModuleSha1)
|
||||||
string.strcpy(out_sym, name_id)
|
string.strcpy(out_sym, name_id)
|
||||||
return 1
|
return 1
|
||||||
def _resolve_doc_symbol_attr(self, trans: HT.Translator*,
|
def _resolve_doc_symbol_attr(self, trans: HT.Translator*, at: ast.Attribute*, out_mod: str, out_sym: str) -> int:
|
||||||
at: ast.Attribute*,
|
"""Resolve a nested Attribute chain's docstring owner into out_mod/out_sym."""
|
||||||
out_mod: str, out_sym: str) -> int:
|
|
||||||
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
|
|
||||||
|
|
||||||
支持形态:remote.remote_func / remote.RemoteClass /
|
|
||||||
remote.RemoteClass.remote_method / LocalClass.local_method 等。
|
|
||||||
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
|
|
||||||
"""
|
|
||||||
if trans is None or at is None or out_mod is None or out_sym is None: return 0
|
if trans is None or at is None or out_mod is None or out_sym is None: return 0
|
||||||
if trans.ModuleSha1 is None: return 0
|
if trans.ModuleSha1 is None: return 0
|
||||||
# 收集 Attribute 链(最外层 attr 在前)
|
# collect the Attribute chain (outermost attr first)
|
||||||
DOC_CHAIN_MAX: int = 8
|
DOC_CHAIN_MAX: int = 8
|
||||||
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
||||||
if items is None: return 0
|
if items is None: return 0
|
||||||
@@ -187,7 +159,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
string.strcpy(items + n_items * 128, cat.attr)
|
string.strcpy(items + n_items * 128, cat.attr)
|
||||||
n_items += 1
|
n_items += 1
|
||||||
cur = cat.value
|
cur = cat.value
|
||||||
# 最左应为 Name
|
# leftmost must be a Name
|
||||||
head_id: str = None
|
head_id: str = None
|
||||||
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
||||||
hnm: ast.Name* = ast.Name*(cur)
|
hnm: ast.Name* = ast.Name*(cur)
|
||||||
@@ -195,85 +167,63 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
if head_id is None:
|
if head_id is None:
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 0
|
return 0
|
||||||
# head 判定:模块名 or 变量/类
|
# head decision: module name or variable/class
|
||||||
is_mod: int = 0
|
is_mod: int = 0
|
||||||
mod_sha1: str = None
|
mod_sha1: str = None
|
||||||
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
|
if self.Gen.VarGen.lookup_var(trans.SymTab, head_id) is None:
|
||||||
mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id)
|
mod_sha1 = StubMergerStore.LookupSha1ByModName(head_id)
|
||||||
if mod_sha1 is None:
|
if mod_sha1 is None: mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
|
||||||
mod_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(head_id)
|
if mod_sha1 is not None: is_mod = 1
|
||||||
if mod_sha1 is not None:
|
|
||||||
is_mod = 1
|
|
||||||
if is_mod != 0:
|
if is_mod != 0:
|
||||||
# 模块限定:symbol = items 反转(不含 head_id)
|
# module-qualified: symbol = items reversed (excluding head_id)
|
||||||
if n_items == 0:
|
if n_items == 0:
|
||||||
string.strcpy(out_mod, mod_sha1)
|
string.strcpy(out_mod, mod_sha1)
|
||||||
string.strcpy(out_sym, head_id)
|
string.strcpy(out_sym, head_id)
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 1
|
return 1
|
||||||
total: sizet = 0
|
total: sizet = 0
|
||||||
for j in range(n_items):
|
for j in range(int(n_items)): total += string.strlen(items + j * 128) + 1
|
||||||
total += string.strlen(items + j * 128) + 1
|
|
||||||
sym_buf: bytes = stdlib.malloc(total + 1)
|
sym_buf: bytes = stdlib.malloc(total + 1)
|
||||||
if sym_buf is None:
|
if sym_buf is None:
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 0
|
return 0
|
||||||
sym_buf[0] = '\0'
|
sym_buf[0] = '\0'
|
||||||
for j in range(n_items):
|
for j in range(int(n_items)):
|
||||||
if j > 0:
|
if j > 0: string.strcat(sym_buf, ".")
|
||||||
string.strcat(sym_buf, ".")
|
|
||||||
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
||||||
string.strcpy(out_mod, mod_sha1)
|
string.strcpy(out_mod, mod_sha1)
|
||||||
string.strcpy(out_sym, sym_buf)
|
string.strcpy(out_sym, sym_buf)
|
||||||
stdlib.free(sym_buf)
|
stdlib.free(sym_buf)
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 1
|
return 1
|
||||||
# 变量/类:symbol = head_id + '.' + items 反转
|
# variable/class: symbol = head_id + '.' + items reversed
|
||||||
total2: sizet = string.strlen(head_id)
|
total2: sizet = string.strlen(head_id)
|
||||||
for j in range(n_items):
|
for j in range(int(n_items)): total2 += string.strlen(items + j * 128) + 1
|
||||||
total2 += string.strlen(items + j * 128) + 1
|
|
||||||
sym2: bytes = stdlib.malloc(total2 + 1)
|
sym2: bytes = stdlib.malloc(total2 + 1)
|
||||||
if sym2 is None:
|
if sym2 is None:
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 0
|
return 0
|
||||||
sym2[0] = '\0'
|
sym2[0] = '\0'
|
||||||
string.strcat(sym2, head_id)
|
string.strcat(sym2, head_id)
|
||||||
for j in range(n_items):
|
for j in range(int(n_items)):
|
||||||
string.strcat(sym2, ".")
|
string.strcat(sym2, ".")
|
||||||
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
||||||
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
|
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
|
||||||
if se2 is not None and se2.ModuleSha1 is not None:
|
if se2 is not None and se2.ModuleSha1 is not None: string.strcpy(out_mod, se2.ModuleSha1)
|
||||||
string.strcpy(out_mod, se2.ModuleSha1)
|
else: string.strcpy(out_mod, trans.ModuleSha1)
|
||||||
else:
|
|
||||||
string.strcpy(out_mod, trans.ModuleSha1)
|
|
||||||
string.strcpy(out_sym, sym2)
|
string.strcpy(out_sym, sym2)
|
||||||
stdlib.free(sym2)
|
stdlib.free(sym2)
|
||||||
stdlib.free(items)
|
stdlib.free(items)
|
||||||
return 1
|
return 1
|
||||||
# ============================================================
|
# P3 migrated methods (from HandlesExprOps.vp: operator overload + binary ops)
|
||||||
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# infer_expr_class - infer the struct class name from an expression (shared by
|
||||||
# infer_expr_class - 从表达式推断结构体类名(共享推断)
|
# unannotated assignment and method dispatch); returns None if undecidable.
|
||||||
#
|
def infer_expr_class(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator*) -> str:
|
||||||
# 无注解赋值(HandlesAssign)与方法调用分派(CallGenMixin)共用:
|
"""Infer the struct class name from an expression, or None if undecidable."""
|
||||||
# 联合类型 X|t.CPtr 简化为 Ptr(i8),不含类名,需按表达式结构推断:
|
|
||||||
# Call → 类实例化(func 为已注册结构体名);func 为 Attribute
|
|
||||||
# 时是方法调用,经 vpi 返回注解缓存解析返回类名
|
|
||||||
# Name → 源变量的 AnnotClassName
|
|
||||||
# BinOp/UnaryOp → lhs 类名 + 存在对应 dunder 方法(返回同类对象)
|
|
||||||
# Attribute → 字段注解类名(_resolve_attr_field_class)
|
|
||||||
# 仅采用已注册结构体;无法精确推断返回 None(fail-fast 由调用方暴露)。
|
|
||||||
# ============================================================
|
|
||||||
def infer_expr_class(self, pool: memhub.MemBuddy*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
node: ast.AST*,
|
|
||||||
trans: HT.Translator*) -> str:
|
|
||||||
"""从表达式推断结构体类名,无法推断返回 None"""
|
|
||||||
if node is None or trans is None: return None
|
if node is None or trans is None: return None
|
||||||
k: int = node.kind()
|
k: int = node.kind()
|
||||||
# Call: 类实例化 / 方法调用
|
# Call: class instantiation / method call
|
||||||
if k == ast.ASTKind.Call:
|
if k == ast.ASTKind.Call:
|
||||||
cl: ast.Call* = ast.Call*(node)
|
cl: ast.Call* = ast.Call*(node)
|
||||||
if cl is None or cl.func is None: return None
|
if cl is None or cl.func is None: return None
|
||||||
@@ -283,48 +233,39 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
if self.Gen.StructGen.find_struct(cand) is not None: return cand
|
if self.Gen.StructGen.find_struct(cand) is not None: return cand
|
||||||
return None
|
return None
|
||||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||||
# 方法调用 a.cross(b):vpi 返回注解缓存精确解析返回类名
|
# Method call a.cross(b): resolve the return class from the vpi return
|
||||||
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3')。
|
# annotation cache (may differ from the receiver class).
|
||||||
ca: ast.Attribute* = ast.Attribute*(cl.func)
|
ca: ast.Attribute* = ast.Attribute*(cl.func)
|
||||||
if ca is None or ca.attr is None or ca.value is None: return None
|
if ca is None or ca.attr is None or ca.value is None: return None
|
||||||
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
|
# module-level call (Module.func()): receiver is an imported module name
|
||||||
if ca.value.kind() == ast.ASTKind.Name:
|
if ca.value.kind() == ast.ASTKind.Name:
|
||||||
mod_nm2: str = (ast.Name*(ca.value)).id
|
mod_nm2: str = (ast.Name*(ca.value)).id
|
||||||
if mod_nm2 is not None and trans._from_imports is not None:
|
if mod_nm2 is not None and trans._from_imports is not None:
|
||||||
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(
|
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, mod_nm2)
|
||||||
pool, trans._from_imports, mod_nm2)
|
|
||||||
if mod_sha1_2 is not None:
|
if mod_sha1_2 is not None:
|
||||||
temp_dir2: str = None
|
temp_dir2: str = None
|
||||||
if self.Gen.CallGenBase.TempDir is not None:
|
if self.Gen.CallGenBase.TempDir is not None: temp_dir2 = self.Gen.CallGenBase.TempDir
|
||||||
temp_dir2 = self.Gen.CallGenBase.TempDir
|
if temp_dir2 is not None: VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
|
||||||
if temp_dir2 is not None:
|
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(mod_sha1_2, None, ca.attr)
|
||||||
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
|
|
||||||
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(
|
|
||||||
mod_sha1_2, None, ca.attr)
|
|
||||||
if annot_ret2 is not None:
|
if annot_ret2 is not None:
|
||||||
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
|
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
|
||||||
if ret_cls2 is not None:
|
if ret_cls2 is not None: return ret_cls2
|
||||||
return ret_cls2
|
|
||||||
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
|
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
|
||||||
if obj_cls is None: return None
|
if obj_cls is None: return None
|
||||||
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None
|
if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None
|
||||||
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
|
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
|
||||||
if cls_sha1 is None and trans.ModuleSha1 is not None:
|
if cls_sha1 is None and trans.ModuleSha1 is not None: cls_sha1 = trans.ModuleSha1
|
||||||
cls_sha1 = trans.ModuleSha1
|
|
||||||
ret_cls: str = None
|
ret_cls: str = None
|
||||||
if cls_sha1 is not None:
|
if cls_sha1 is not None:
|
||||||
temp_dir: str = None
|
temp_dir: str = None
|
||||||
# 跨子对象访问经 self.Gen 取 CallGenBase.TempDir(双向引用)
|
# cross sub-object access via self.Gen for CallGenBase.TempDir
|
||||||
if self.Gen.CallGenBase.TempDir is not None:
|
if self.Gen.CallGenBase.TempDir is not None: temp_dir = self.Gen.CallGenBase.TempDir
|
||||||
temp_dir = self.Gen.CallGenBase.TempDir
|
if temp_dir is not None: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
|
||||||
if temp_dir is not None:
|
|
||||||
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
|
|
||||||
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
|
annot_ret: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, obj_cls, ca.attr)
|
||||||
if annot_ret is not None:
|
if annot_ret is not None: ret_cls = self._extract_annot_class_name(pool, annot_ret)
|
||||||
ret_cls = self._extract_annot_class_name(pool, annot_ret)
|
|
||||||
return ret_cls
|
return ret_cls
|
||||||
return None
|
return None
|
||||||
# Name: 源变量注解
|
# Name: source variable annotation
|
||||||
if k == ast.ASTKind.Name:
|
if k == ast.ASTKind.Name:
|
||||||
nm: ast.Name* = ast.Name*(node)
|
nm: ast.Name* = ast.Name*(node)
|
||||||
if nm is None or nm.id is None: return None
|
if nm is None or nm.id is None: return None
|
||||||
@@ -332,16 +273,12 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
if ve is None: return None
|
if ve is None: return None
|
||||||
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
|
acn_n: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||||||
if acn_n is None: return None
|
if acn_n is None: return None
|
||||||
# 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带
|
# Strip the module prefix (json.JsonValue -> JsonValue): the lookups use
|
||||||
# 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回
|
# bare names; generic names containing '[' are not truncated.
|
||||||
# "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/
|
|
||||||
# get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名
|
|
||||||
# (list[str] 含 '[')不截断(方括号内可能有点号)。
|
|
||||||
acn_dot_n: str = string.strrchr(acn_n, 46)
|
acn_dot_n: str = string.strrchr(acn_n, 46)
|
||||||
if acn_dot_n is not None and string.strchr(acn_n, 91) is None:
|
if acn_dot_n is not None and string.strchr(acn_n, 91) is None: acn_n = acn_dot_n + 1
|
||||||
acn_n = acn_dot_n + 1
|
|
||||||
return acn_n
|
return acn_n
|
||||||
# BinOp/UnaryOp: 运算符重载返回同类对象
|
# BinOp/UnaryOp: operator overload returns the same class object
|
||||||
if k == ast.ASTKind.BinOp:
|
if k == ast.ASTKind.BinOp:
|
||||||
bop: ast.BinOp* = ast.BinOp*(node)
|
bop: ast.BinOp* = ast.BinOp*(node)
|
||||||
if bop is None or bop.left is None: return None
|
if bop is None or bop.left is None: return None
|
||||||
@@ -358,25 +295,19 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
|
opr_cls: str = self.infer_expr_class(pool, mod, uop.operand, trans)
|
||||||
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
|
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: return None
|
||||||
return opr_cls
|
return opr_cls
|
||||||
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
|
# Attribute: field annotation class name (recursive chain, e.g. p = r.origin)
|
||||||
if k == ast.ASTKind.Attribute:
|
if k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
|
||||||
return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _cls_has_dunder - whether a class (or its MRO ancestors) has the dunder/method;
|
||||||
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder/方法
|
# defined in this module or declared in a cross-module stub SHA1.
|
||||||
#
|
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, class_name: str, dunder: str) -> int:
|
||||||
# 与 try_operator_overload 的查找一致:当前模块内定义或跨模块类有
|
"""Return 1 if the class or its MRO ancestors define the dunder method."""
|
||||||
# SHA1(stub 中声明)即视为存在。仅在确实存在时采用推断类名,
|
|
||||||
# 避免把原生运算误标为类类型。
|
|
||||||
# ============================================================
|
|
||||||
def _cls_has_dunder(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, class_name: str, dunder: str) -> int:
|
|
||||||
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
|
|
||||||
if class_name is None or dunder is None or mod is None: return 0
|
if class_name is None or dunder is None or mod is None: return 0
|
||||||
lookup_name: str = pool.alloc(128)
|
lookup_name: str = pool.alloc(128)
|
||||||
if lookup_name is None: return 0
|
if lookup_name is None: return 0
|
||||||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||||||
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
|
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None: return 1
|
||||||
# 继承链祖先查找
|
# walk MRO ancestors
|
||||||
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||||||
mi: int = 1
|
mi: int = 1
|
||||||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||||||
@@ -385,22 +316,15 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
anc_lookup: str = pool.alloc(128)
|
anc_lookup: str = pool.alloc(128)
|
||||||
if anc_lookup is not None:
|
if anc_lookup is not None:
|
||||||
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
|
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
|
||||||
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None:
|
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None: return 1
|
||||||
return 1
|
|
||||||
mi += 1
|
mi += 1
|
||||||
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
|
# cross-module: a class with a SHA1 (method declared in a stub) counts as existing
|
||||||
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
|
if self.Gen.StructGen.get_struct_sha1(class_name) is not None: return 1
|
||||||
return 0
|
return 0
|
||||||
# ============================================================
|
# _extract_annot_class_name - extract a registered struct class name from a vpi
|
||||||
# _extract_annot_class_name - 从返回注解文本提取结构体类名
|
# return annotation, splitting on '|' and taking the first hit.
|
||||||
#
|
|
||||||
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
|
|
||||||
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
|
|
||||||
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
|
|
||||||
# 多候选时取首个命中段。
|
|
||||||
# ============================================================
|
|
||||||
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
|
def _extract_annot_class_name(self, pool: memhub.MemBuddy*, annot: str) -> str:
|
||||||
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
|
"""Extract a registered struct class name from return annotation text, or None."""
|
||||||
if pool is None or annot is None: return None
|
if pool is None or annot is None: return None
|
||||||
alen: sizet = string.strlen(annot)
|
alen: sizet = string.strlen(annot)
|
||||||
if alen == 0: return None
|
if alen == 0: return None
|
||||||
@@ -408,54 +332,52 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
|||||||
if seg is None: return None
|
if seg is None: return None
|
||||||
start: sizet = 0
|
start: sizet = 0
|
||||||
i: sizet
|
i: sizet
|
||||||
for i in range(alen):
|
for i in range(int(alen)):
|
||||||
if annot[i] == '|':
|
if annot[i] == '|':
|
||||||
s: sizet = start
|
s: sizet = start
|
||||||
e: sizet = i
|
e: sizet = i
|
||||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
|
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||||
s += 1
|
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||||
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
|
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||||
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
|
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||||
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
|
|
||||||
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
|
|
||||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
|
|
||||||
e -= 1
|
|
||||||
seglen: sizet = e - s
|
seglen: sizet = e - s
|
||||||
if seglen > 0 and seglen < 256:
|
if seglen > 0 and seglen < 256:
|
||||||
string.strncpy(seg, annot + s, seglen)
|
string.strncpy(seg, annot + s, seglen)
|
||||||
seg[seglen] = '\0'
|
seg[seglen] = '\0'
|
||||||
if self.Gen.StructGen.find_struct(seg) is not None:
|
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||||
return seg
|
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中
|
seg = seg + 5
|
||||||
# 时去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
seglen -= 6
|
||||||
|
seg[seglen] = '\0'
|
||||||
|
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||||
|
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||||
|
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||||
seg_dot2: str = string.strrchr(seg, 46)
|
seg_dot2: str = string.strrchr(seg, 46)
|
||||||
if seg_dot2 is not None and seg_dot2 != seg:
|
if seg_dot2 is not None and seg_dot2 != seg:
|
||||||
seg_bare2: str = seg_dot2 + 1
|
seg_bare2: str = seg_dot2 + 1
|
||||||
if self.Gen.StructGen.find_struct(seg_bare2) is not None:
|
if self.Gen.StructGen.find_struct(seg_bare2) is not None: return seg_bare2
|
||||||
return seg_bare2
|
|
||||||
start = i + 1
|
start = i + 1
|
||||||
# 最后一段(无 '|' 后缀)
|
# last segment (no '|' suffix)
|
||||||
s: sizet = start
|
s: sizet = start
|
||||||
e: sizet = alen
|
e: sizet = alen
|
||||||
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
|
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1
|
||||||
s += 1
|
# Also strip a trailing '*' (e.g. `{sha}.AST*`), matching the tail
|
||||||
# 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态
|
# stripping in ExprGenName._list_elem_obj_ty_from_struct_name.
|
||||||
# `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与
|
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1
|
||||||
# ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径;
|
|
||||||
# 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。
|
|
||||||
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'):
|
|
||||||
e -= 1
|
|
||||||
seglen: sizet = e - s
|
seglen: sizet = e - s
|
||||||
if seglen > 0 and seglen < 256:
|
if seglen > 0 and seglen < 256:
|
||||||
string.strncpy(seg, annot + s, seglen)
|
string.strncpy(seg, annot + s, seglen)
|
||||||
seg[seglen] = '\0'
|
seg[seglen] = '\0'
|
||||||
if self.Gen.StructGen.find_struct(seg) is not None:
|
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||||
return seg
|
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||||
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中时
|
seg = seg + 5
|
||||||
# 去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
|
seglen -= 6
|
||||||
|
seg[seglen] = '\0'
|
||||||
|
if self.Gen.StructGen.find_struct(seg) is not None: return seg
|
||||||
|
# On a find_struct miss, retry with the last '.'-prefix (module
|
||||||
|
# qualifier) stripped: memhub.MemBuddy -> MemBuddy.
|
||||||
seg_dot3: str = string.strrchr(seg, 46)
|
seg_dot3: str = string.strrchr(seg, 46)
|
||||||
if seg_dot3 is not None and seg_dot3 != seg:
|
if seg_dot3 is not None and seg_dot3 != seg:
|
||||||
seg_bare3: str = seg_dot3 + 1
|
seg_bare3: str = seg_dot3 + 1
|
||||||
if self.Gen.StructGen.find_struct(seg_bare3) is not None:
|
if self.Gen.StructGen.find_struct(seg_bare3) is not None: return seg_bare3
|
||||||
return seg_bare3
|
|
||||||
return None
|
return None
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdio
|
import stdio
|
||||||
@@ -13,286 +12,239 @@ import lib.core.Gen.GenTypes as GenTypes
|
|||||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ExprGenConst - split from ExprGenMixin (stage D): constants / global strings / list literals
|
||||||
# ExprGenConst - ExprGenMixin 拆分(阶段 D):常量/全局字符串/list 字面量翻译
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||||
"""常量/全局字符串/list 字面量翻译"""
|
"""Translation of constants, global strings and list literals."""
|
||||||
|
|
||||||
# ============================================================
|
# Create global string constant
|
||||||
# 创建全局字符串常量
|
def create_global_string(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, str_val: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
# ============================================================
|
"""Create a global string constant and return its i8* bitcast."""
|
||||||
def create_global_string(self, builder: llvmlite.IRBuilder*,
|
|
||||||
pool: memhub.MemBuddy*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
str_val: str,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""创建全局字符串常量并返回 i8* bitcast"""
|
|
||||||
escaped: str = self.escape_llvm_string(pool, str_val)
|
escaped: str = self.escape_llvm_string(pool, str_val)
|
||||||
if escaped is None: return None
|
if escaped is None: return None
|
||||||
|
|
||||||
slen: sizet = string.strlen(str_val)
|
slen: sizet = string.strlen(str_val)
|
||||||
count: int = slen + 1
|
count: int = int(slen + 1)
|
||||||
|
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
arr_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, count)
|
arr_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, count)
|
||||||
|
|
||||||
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
|
# Use the module-level counter (trans._label_counter) to avoid cross-function naming conflicts
|
||||||
str_idx: int = trans._label_counter
|
str_idx: int = trans._label_counter
|
||||||
trans._label_counter += 1
|
trans._label_counter += 1
|
||||||
|
|
||||||
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
|
# Prefix string names with the SHA1 (same rule as function export) to avoid cross-module collisions
|
||||||
gv_name: str = pool.alloc(48)
|
gv_name: str = pool.alloc(48)
|
||||||
if gv_name is None:
|
if gv_name is None: return None
|
||||||
return None
|
if trans.ModuleSha1 is not None: viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
||||||
if trans.ModuleSha1 is not None:
|
else: viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
|
||||||
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
|
||||||
else:
|
|
||||||
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
|
|
||||||
|
|
||||||
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, gv_name, arr_ty)
|
gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name, arr_ty)
|
||||||
if gv is None:
|
if gv is None: return None
|
||||||
return None
|
mlirlite.module_add_global(mod, gv)
|
||||||
llvmlite.module_add_global(mod, gv)
|
|
||||||
|
|
||||||
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
|
# Drop private linkage, same as funcs: declare in stub, define in text
|
||||||
llvmlite.global_set_unnamed_addr(gv, 1)
|
mlirlite.global_set_unnamed_addr(gv, 1)
|
||||||
llvmlite.global_set_constant(gv, 1)
|
mlirlite.global_set_constant(gv, 1)
|
||||||
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
|
# LLVM 22+ forbids initializers on external-linkage globals, so set internal
|
||||||
gv.Linkage = "internal"
|
gv.Linkage = "internal"
|
||||||
gv.Initializer = escaped
|
gv.Initializer = escaped
|
||||||
|
|
||||||
arr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, arr_ty)
|
arr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, arr_ty)
|
||||||
gv_ref_name: str = pool.alloc(64)
|
gv_ref_name: str = pool.alloc(64)
|
||||||
if gv_ref_name is None:
|
if gv_ref_name is None: return None
|
||||||
return None
|
|
||||||
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
|
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
|
||||||
gv_ref: llvmlite.Value* = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
|
gv_ref: mlirlite.Value* = mlirlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
|
||||||
|
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
|
|
||||||
bc: llvmlite.Value* = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
bc: mlirlite.Value* = mlirlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||||||
return bc
|
return bc
|
||||||
# ============================================================
|
# _explicit_width_int_const - 显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`):
|
||||||
# 翻译常量表达式
|
# 按声明宽度出常量;值必须落在该宽度的可表示范围(有符号 [-2^(w-1), 2^(w-1)-1];无符号 [0, 2^w-1]),
|
||||||
# ============================================================
|
# 越界编译期 fail-fast(不允许隐式截断)。w==64 时 int64 全域都合法。
|
||||||
def translate_constant(self, builder: llvmlite.IRBuilder*,
|
def _explicit_width_int_const(self, pool: memhub.MemBuddy*, node: ast.AST*, w: int, is_unsigned: int, iv: int64) -> mlirlite.Value*:
|
||||||
pool: memhub.MemBuddy*,
|
"""Explicit-width integer literal: strict range check, then a constant of exactly w bits."""
|
||||||
mod: llvmlite.LLVMModule*,
|
ok: int = 1
|
||||||
node: ast.AST*,
|
if is_unsigned != 0:
|
||||||
trans: HT.Translator* = None,
|
if w < 64 and iv < 0: ok = 0
|
||||||
target_ty: llvmlite.LLVMType* = None) -> llvmlite.Value*:
|
elif w <= 62 and iv > (int64(1) << w) - 1: ok = 0
|
||||||
"""翻译常量(int/str/bool)"""
|
else:
|
||||||
|
if w < 64:
|
||||||
|
lo: int64 = -(int64(1) << (w - 1))
|
||||||
|
hi: int64 = (int64(1) << (w - 1)) - 1
|
||||||
|
if iv < lo or iv > hi: ok = 0
|
||||||
|
if ok == 0:
|
||||||
|
wname: str = "int"
|
||||||
|
if is_unsigned != 0: wname = "uint"
|
||||||
|
self.Gen.TypeGenErr.fatal_error(node, f"integer literal {iv} does not fit the explicit width {wname}{w} (it would truncate); use a wider suffix or an explicit cast", "OverflowError")
|
||||||
|
return mlirlite.const_int(pool, w, iv)
|
||||||
|
# Translate constant expressions
|
||||||
|
def translate_constant(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||||
|
"""Translate a constant node (int/str/bool)."""
|
||||||
cn: ast.Constant* = ast.Constant*(node)
|
cn: ast.Constant* = ast.Constant*(node)
|
||||||
if cn is None:
|
if cn is None: return None
|
||||||
return None
|
|
||||||
ck: int = cn.const_kind
|
ck: int = cn.const_kind
|
||||||
if cn.const_kind == ast.CONST_INT:
|
if cn.const_kind == ast.CONST_INT:
|
||||||
# 位宽选择:
|
# Bit width: (1) if the target context is >32-bit int, use i64 directly - required
|
||||||
# 1) 目标上下文是 >32 位整数时直接取 i64 —— 必需:词法层 atoll 把
|
# since atoll wraps 0xFFFF... to -1 as signed int64; (2) else i64 if out of i32 range, otherwise i32.
|
||||||
# `0xFFFFFFFFFFFFFFFF` 按有符号 int64 回绕成 -1,仅按"是否超出
|
|
||||||
# i32 范围"判定会把它物化成 i32 -1,随后强转路径 zext 到 i64
|
|
||||||
# → 0x00000000FFFFFFFF(N1:THREAD_CREATE 失败码错)。
|
|
||||||
# const_int 按十进制值打印,故 i64 -1 即全 1,语义正确。
|
|
||||||
# 2) 否则维持旧规则:超出 i32 范围用 i64,其余 i32。
|
|
||||||
iv: int64 = cn.int_val
|
iv: int64 = cn.int_val
|
||||||
if target_ty is not None and self.is_ptr_type(target_ty) == 0 \
|
# 显式宽度后缀(_int<N>/_uint<N>/_i<N>/_u<N>):按声明宽度出常量并严格校验不截断
|
||||||
and self.get_llvm_type_bits(target_ty) > 32:
|
if cn.int_width > 0: return self._explicit_width_int_const(pool, node, cn.int_width, cn.int_unsigned, iv)
|
||||||
return llvmlite.const_int64(pool, iv)
|
if target_ty is not None and self.is_ptr_type(target_ty) == 0 and self.get_llvm_type_bits(target_ty) > 32: return mlirlite.const_int64(pool, iv)
|
||||||
if iv > 2147483647 or iv < -2147483648:
|
# 位宽按值推导:i32 放不下、但 u32 放得下的字面量归为 32 位(u32)常量,而不是一律落 64 位
|
||||||
return llvmlite.const_int64(pool, iv)
|
# (否则 `crc ^ 0xFFFFFFFF` 的异或会变成 64 位,赋值回 32 位 uint 时报“隐式窄化禁止”)。
|
||||||
return llvmlite.const_int32(pool, iv)
|
# 文本用有符号等价(LLVM/MLIR 惯例,避免越界字面量);IntVal 保留无符号真值,
|
||||||
|
# 使窄化检查按真实数值判定,不静默截断。
|
||||||
|
if iv > 2147483647 and iv <= 4294967295:
|
||||||
|
ty32: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
|
nm32: str = pool.alloc(32)
|
||||||
|
if nm32 is None: return mlirlite.const_int32(pool, iv)
|
||||||
|
viperlib.snprintf(nm32, 32, "%lld", iv - 4294967296)
|
||||||
|
return mlirlite.ConstInt(pool, ty32, iv, nm32)
|
||||||
|
if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv)
|
||||||
|
return mlirlite.const_int32(pool, iv)
|
||||||
elif cn.const_kind == ast.CONST_FLOAT:
|
elif cn.const_kind == ast.CONST_FLOAT:
|
||||||
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
|
# 目标类型是 32 位浮点(float(1.5) / x: float = 1.5 / f32 形参)→ 直接出 f32 常量。
|
||||||
double_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
|
# 旧实现无视 target_ty 一律先出 double 常量、再由 coerce_to_type fptrunc,
|
||||||
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
|
# 现场:float(2.0) / float(1.0) 生成 f64 常量 + fptrunc,与双精度常量混算时又多出 fpext。
|
||||||
|
# 其余情况(无目标类型 / double / 非浮点)保持 double 常量,由 coerce_to_type 负责转换。
|
||||||
|
if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32:
|
||||||
|
return mlirlite.ConstFloat(pool, target_ty, cn.float_val)
|
||||||
|
double_ty: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||||
|
return mlirlite.ConstFloat(pool, double_ty, cn.float_val)
|
||||||
elif cn.const_kind == ast.CONST_CHAR:
|
elif cn.const_kind == ast.CONST_CHAR:
|
||||||
# 单引号单字符字面量(CONST_CHAR):默认按字符值(i32,coerce 截断为 i8);但当目标类型是指针(str/bytes/t.CPtr)时须物化为 1 字符字符串,否则 coerce inttoptr 把字符值当指针(如 '"'=0x22)→ 运行期 strlen 访问低地址崩溃。1a 阶段(declare_only=1)依赖函数 found_func=None,target_ty 未对齐即视为垃圾跳过,避免 is_ptr_type 解引用崩溃
|
# Single-char literal (CONST_CHAR): default as char value (i32, coerce truncates to i8); if the target type is a pointer (str/bytes/t.CPtr) materialize a 1-char string, else inttoptr treats the char code as a pointer -> strlen crash. In 1a stage (declare_only=1) found_func=None so a misaligned target_ty is treated as garbage and skipped
|
||||||
if target_ty is not None and (uint64(target_ty) & 7) == 0 \
|
if target_ty is not None and (uint64(target_ty) & 7) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||||
and self.is_ptr_type(target_ty) != 0:
|
|
||||||
cbuf: bytes = stdlib.malloc(2)
|
cbuf: bytes = stdlib.malloc(2)
|
||||||
if cbuf is None:
|
if cbuf is None: return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
cbuf[0] = char(cn.int_val)
|
cbuf[0] = char(cn.int_val)
|
||||||
cbuf[1] = '\0'
|
cbuf[1] = '\0'
|
||||||
r_cs: llvmlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
|
r_cs: mlirlite.Value* = self.create_global_string(builder, pool, mod, cbuf, trans)
|
||||||
stdlib.free(cbuf)
|
stdlib.free(cbuf)
|
||||||
return r_cs
|
return r_cs
|
||||||
cv: int64 = cn.int_val
|
cv: int64 = cn.int_val
|
||||||
return llvmlite.const_int32(pool, cv)
|
return mlirlite.const_int32(pool, cv)
|
||||||
elif cn.const_kind == ast.CONST_STR:
|
elif cn.const_kind == ast.CONST_STR:
|
||||||
sv: str = cn.str_val
|
sv: str = cn.str_val
|
||||||
if sv is None:
|
if sv is None: return None
|
||||||
return None
|
r: mlirlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
|
||||||
r: llvmlite.Value* = self.create_global_string(builder, pool, mod, sv, trans)
|
|
||||||
return r
|
return r
|
||||||
elif cn.const_kind == ast.CONST_BOOL:
|
elif cn.const_kind == ast.CONST_BOOL:
|
||||||
if cn.int_val != 0:
|
if cn.int_val != 0: return mlirlite.const_int32(pool, 1)
|
||||||
return llvmlite.const_int32(pool, 1)
|
return mlirlite.const_int32(pool, 0)
|
||||||
return llvmlite.const_int32(pool, 0)
|
|
||||||
elif cn.const_kind == ast.CONST_NONE:
|
elif cn.const_kind == ast.CONST_NONE:
|
||||||
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
|
# None -> i8* null (null pointer constant) for `p is None` / `p is not None` comparisons
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
if i8_ty is None:
|
if i8_ty is None: return None
|
||||||
return None
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
if i8_ptr_ty is None: return None
|
||||||
if i8_ptr_ty is None:
|
rv_none: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||||||
return None
|
|
||||||
rv_none: llvmlite.Value* = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
|
|
||||||
return rv_none
|
return rv_none
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _infer_elem_type_from_value - infer the list element type name from an LLVM Value.
|
||||||
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
|
# Used for element type inference of list literals [a, b, c].
|
||||||
#
|
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*, value: mlirlite.Value*) -> str:
|
||||||
# 用于 list 字面量 [a, b, c] 的元素类型推断
|
"""Infer the list element type name from an LLVM Value; None if unmappable."""
|
||||||
# ============================================================
|
if value is None or value.Ty is None: return None
|
||||||
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy*,
|
ty: mlirlite.LLVMType* = value.Ty
|
||||||
value: llvmlite.Value*) -> str:
|
|
||||||
"""从 LLVM Value 的类型推断 list 元素类型名
|
|
||||||
|
|
||||||
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
|
|
||||||
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
|
|
||||||
"""
|
|
||||||
if value is None or value.Ty is None:
|
|
||||||
return None
|
|
||||||
ty: llvmlite.LLVMType* = value.Ty
|
|
||||||
bits: int = self.get_llvm_type_bits(ty)
|
bits: int = self.get_llvm_type_bits(ty)
|
||||||
if bits != 0:
|
if bits != 0:
|
||||||
if bits == 32:
|
if bits == 32: return "int"
|
||||||
return "int"
|
if bits == 64: return "CSizeT"
|
||||||
if bits == 64:
|
if bits == 8: return "CInt8T"
|
||||||
return "CSizeT"
|
if bits == 16: return "CInt16T"
|
||||||
if bits == 8:
|
|
||||||
return "CInt8T"
|
|
||||||
if bits == 16:
|
|
||||||
return "CInt16T"
|
|
||||||
return None
|
return None
|
||||||
if self.is_ptr_type(ty) != 0:
|
if self.is_ptr_type(ty) != 0: return "str"
|
||||||
return "str"
|
|
||||||
fbits: int = self.get_llvm_float_bits(ty)
|
fbits: int = self.get_llvm_float_bits(ty)
|
||||||
if fbits != 0:
|
if fbits != 0:
|
||||||
if fbits == 64:
|
if fbits == 64: return "CDouble"
|
||||||
return "CDouble"
|
if fbits == 32: return "CFloat"
|
||||||
if fbits == 32:
|
|
||||||
return "CFloat"
|
|
||||||
return None
|
return None
|
||||||
# ============================================================
|
# _find_pool_var - find the pool variable in the context.
|
||||||
# _find_pool_var — 查找上下文中的 pool 变量
|
# Tries "pool", "_mbuddy", "mbuddy", "mb" in order.
|
||||||
#
|
def _find_pool_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
|
"""Find the pool variable in context (pool/_mbuddy/mbuddy/mb), else fall back."""
|
||||||
# ============================================================
|
if trans is None: return None
|
||||||
def _find_pool_var(self, trans: HT.Translator*) -> llvmlite.Value*:
|
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
|
||||||
"""查找上下文中的 pool 变量,返回 alloca 或 None
|
if alloca is not None: return alloca
|
||||||
|
|
||||||
查找顺序: pool → _mbuddy → mbuddy → mb
|
|
||||||
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
|
|
||||||
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
|
|
||||||
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
|
|
||||||
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
|
|
||||||
"""
|
|
||||||
if trans is None:
|
|
||||||
return None
|
|
||||||
alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "pool")
|
|
||||||
if alloca is not None:
|
|
||||||
return alloca
|
|
||||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
|
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "_mbuddy")
|
||||||
if alloca is not None:
|
if alloca is not None: return alloca
|
||||||
return alloca
|
|
||||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
|
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mbuddy")
|
||||||
if alloca is not None:
|
if alloca is not None: return alloca
|
||||||
return alloca
|
|
||||||
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
|
alloca = self.Gen.VarGen.lookup_var(trans.SymTab, "mb")
|
||||||
if alloca is not None:
|
if alloca is not None: return alloca
|
||||||
return alloca
|
# Fallback: create a module-mangled @_mbuddy definition (linkonce_odr + zeroinitializer),
|
||||||
# 兜底: 创建当前模块 mangled 的 @_mbuddy 定义(linkonce_odr + zeroinitializer)
|
# matching the SHA1-prefixed global naming; a real crash exposes an uninitialized pool (fail-fast).
|
||||||
# 对齐模块级全局带 SHA1 前缀命名:裸名 @_mbuddy 依赖链接合并共享
|
|
||||||
# (--allow-multiple-definition),带前缀后每模块独立。linkonce_odr 允许
|
|
||||||
# 链接器合并同模板重复特化定义,zeroinitializer 保证编译链接通过。
|
|
||||||
# 运行时若该模块未在入口 main 赋值 _mbuddy(null pool 上动态建 list),
|
|
||||||
# 由真实崩溃暴露(fail-fast 哲学,不掩盖上游未初始化)。
|
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
if pool is None or trans.Module is None or trans.SymTab is None:
|
if pool is None or trans.Module is None or trans.SymTab is None: return None
|
||||||
return None
|
# First check whether it is already registered in the module scope (avoid duplicate globals)
|
||||||
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
|
existing: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
|
||||||
existing: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, "_mbuddy")
|
if existing is not None: return existing
|
||||||
if existing is not None:
|
# Create the i8* type (t.CPtr = i8*)
|
||||||
return existing
|
i8_ty_fb: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
# 创建 i8* 类型(t.CPtr = i8*)
|
i8_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty_fb)
|
||||||
i8_ty_fb: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
# Module-mangled name (@<module-sha1>._mbuddy)
|
||||||
i8_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_fb)
|
|
||||||
# 当前模块 mangled 名(@<模块sha1>._mbuddy)
|
|
||||||
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
|
mb_name_fb: str = self.Gen.FuncGenBase._mangle_name(trans, "_mbuddy")
|
||||||
# 创建 linkonce_odr 定义(可被链接器合并,自给自足保证链接通过)
|
# Create a linkonce_odr definition (linker-mergeable, self-contained for linking)
|
||||||
gv_ext: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
|
gv_ext: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, mb_name_fb, i8_ptr_ty_fb)
|
||||||
if gv_ext is not None:
|
if gv_ext is not None:
|
||||||
gv_ext.Linkage = "linkonce_odr"
|
gv_ext.Linkage = "linkonce_odr"
|
||||||
gv_ext.Initializer = "zeroinitializer"
|
gv_ext.Initializer = "zeroinitializer"
|
||||||
llvmlite.module_add_global(trans.Module, gv_ext)
|
mlirlite.module_add_global(trans.Module, gv_ext)
|
||||||
# 创建 Value 引用(@<模块sha1>._mbuddy, 类型为 i8**)
|
# Create the Value reference (@<module-sha1>._mbuddy, type i8**)
|
||||||
i8_ptr_ptr_ty_fb: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty_fb)
|
i8_ptr_ptr_ty_fb: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty_fb)
|
||||||
ref_name_fb: str = pool.alloc(96)
|
ref_name_fb: str = pool.alloc(96)
|
||||||
if ref_name_fb is not None:
|
if ref_name_fb is not None:
|
||||||
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(见 make_global_ref)
|
# Mangled names start with a digit (SHA1 prefix), so the reference must be quoted (see make_global_ref)
|
||||||
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0:
|
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mb_name_fb) != 0: viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
|
||||||
viperlib.snprintf(ref_name_fb, 96, "@\"%s\"", mb_name_fb)
|
else: viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
|
||||||
else:
|
gv_ref_fb: mlirlite.Value* = mlirlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||||||
viperlib.snprintf(ref_name_fb, 96, "@%s", mb_name_fb)
|
# Register in the module scope to avoid duplicate creation
|
||||||
gv_ref_fb: llvmlite.Value* = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
|
||||||
# 注册到模块作用域,避免重复创建
|
|
||||||
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
self.Gen.VarGen.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||||||
return gv_ref_fb
|
return gv_ref_fb
|
||||||
# translate_list_literal:把 list 字面量 [a, b, c] 翻译为 list[T](pool) 构造 + append 调用序列。elem_type_name 为元素类型名(如 "str"/"int"),None 表示从元素推断。返回 list 对象指针(Ptr(list[T] struct)),失败返回 None
|
# translate_list_literal - lower [a, b, c] to list[T](pool) construction + append calls; elem_type_name None means infer from elements; returns Ptr(list[T] struct) or None
|
||||||
def translate_list_literal(self, builder: llvmlite.IRBuilder*,
|
def translate_list_literal(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, elem_type_name: str, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
pool: memhub.MemBuddy*,
|
"""Lower a list literal to list[T](pool) construction plus append calls."""
|
||||||
mod: llvmlite.LLVMModule*,
|
if node is None or trans is None: return None
|
||||||
node: ast.AST*,
|
|
||||||
elem_type_name: str,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
|
|
||||||
if node is None or trans is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
list_node: ast.List* = ast.List*(node)
|
list_node: ast.List* = ast.List*(node)
|
||||||
if list_node is None:
|
if list_node is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
elts: list[ast.AST*]* = list_node.elts
|
elts: list[ast.AST*]* = list_node.elts
|
||||||
elts_count: sizet = 0
|
elts_count: sizet = 0
|
||||||
if elts is not None:
|
if elts is not None: elts_count = len(elts)
|
||||||
elts_count = len(elts)
|
|
||||||
|
|
||||||
# 推断元素类型
|
# Infer element type
|
||||||
inferred_type_name: str = elem_type_name
|
inferred_type_name: str = elem_type_name
|
||||||
first_elem_val: llvmlite.Value* = None
|
first_elem_val: mlirlite.Value* = None
|
||||||
if inferred_type_name is None:
|
if inferred_type_name is None:
|
||||||
if elts_count > 0:
|
if elts_count > 0:
|
||||||
first_elem_node: ast.AST* = elts.get(0)
|
first_elem_node: ast.AST* = elts.get(0)
|
||||||
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
first_elem_val = self.Gen.ExprGen.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
||||||
if first_elem_val is None or first_elem_val.Ty is None:
|
if first_elem_val is None or first_elem_val.Ty is None:
|
||||||
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
|
# fail-fast: first-element translation failed/Value type missing -> abort now (surface root cause)
|
||||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
|
||||||
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
||||||
if inferred_type_name is None:
|
if inferred_type_name is None:
|
||||||
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
|
# fail-fast: first-element Value type cannot map to a list element type -> abort now
|
||||||
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(first_elem_node, "list literal element type inference failed (Value type missing)", "TypeError")
|
||||||
else:
|
else:
|
||||||
# 空 list 无元素可推断,保持默认 str(list[str])
|
# Empty list has no element to infer, keep default str (list[str])
|
||||||
inferred_type_name = "str"
|
inferred_type_name = "str"
|
||||||
|
|
||||||
# 查找 pool 变量
|
# Find the pool variable
|
||||||
pool_alloca: llvmlite.Value* = self._find_pool_var(trans)
|
pool_alloca: mlirlite.Value* = self._find_pool_var(trans)
|
||||||
if pool_alloca is None:
|
if pool_alloca is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
|
self.Gen.TypeGenErr.fatal_error(node, "list literal requires pool variable in context")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# load pool 变量值(i8*)
|
# Load the pool variable value (i8*)
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
pool_val: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
pool_val: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
||||||
if pool_val is None:
|
if pool_val is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
|
# Specialize the list[T] class (elem_size=8: str is a pointer; 4 would overlap elements)
|
||||||
type_args: list[str]* = list[str](pool, 8)
|
type_args: list[str]* = list[str](pool, 8)
|
||||||
type_args.append(inferred_type_name)
|
type_args.append(inferred_type_name)
|
||||||
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
|
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, "list", type_args)
|
||||||
@@ -300,55 +252,48 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
|||||||
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
|
self.Gen.TypeGenErr.fatal_error(node, "list literal generic specialization failed")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 查找 struct entry
|
# Find the struct entry
|
||||||
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
|
struct_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
|
||||||
if struct_entry is None:
|
if struct_entry is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
|
self.Gen.TypeGenErr.fatal_error(node, "list literal specialized class not registered")
|
||||||
return None
|
return None
|
||||||
struct_ty: llvmlite.LLVMType* = struct_entry.Ty
|
struct_ty: mlirlite.LLVMType* = struct_entry.Ty
|
||||||
if struct_ty is None:
|
if struct_ty is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# alloca 临时 list 空间
|
# alloca temporary list storage; ensure the cross-module struct definition is
|
||||||
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
|
# fully available in the current module (so alloca can size it).
|
||||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
|
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||||||
tmp: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
|
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
|
||||||
if tmp is None:
|
if tmp is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
|
# Storage pointer: alloca by default, or the pointer returned by __new__ if present.
|
||||||
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
|
# __new__ is optional and only called when the generic template defines it (consistent with ctor lowering).
|
||||||
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
|
storage_ptr: mlirlite.Value* = tmp
|
||||||
# 避免为无 __new__ 的类生成过度声明)
|
|
||||||
storage_ptr: llvmlite.Value* = tmp
|
|
||||||
has_new_flag: int = 0
|
has_new_flag: int = 0
|
||||||
if struct_entry is not None:
|
if struct_entry is not None: has_new_flag = struct_entry.HasNew
|
||||||
has_new_flag = struct_entry.HasNew
|
|
||||||
if has_new_flag != 0:
|
if has_new_flag != 0:
|
||||||
new_args: sizet* = pool.alloc(8 * 32)
|
new_args: sizet* = pool.alloc(8 * 32)
|
||||||
if new_args is None:
|
if new_args is None: return None
|
||||||
return None
|
|
||||||
string.memset(new_args, 0, 8 * 32)
|
string.memset(new_args, 0, 8 * 32)
|
||||||
new_args[0] = sizet(pool_val)
|
new_args[0] = sizet(pool_val)
|
||||||
new_ptr: llvmlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
||||||
if new_ptr is not None:
|
if new_ptr is not None: storage_ptr = new_ptr
|
||||||
storage_ptr = new_ptr
|
|
||||||
|
|
||||||
# 调用 __before_init__(storage_ptr)
|
# Call __before_init__(storage_ptr)
|
||||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
||||||
|
|
||||||
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
|
# Call __init__(storage_ptr, pool_val, elem_size=0)
|
||||||
init_args: sizet* = pool.alloc(8 * 32)
|
init_args: sizet* = pool.alloc(8 * 32)
|
||||||
if init_args is None: return None
|
if init_args is None: return None
|
||||||
string.memset(init_args, 0, 8 * 32)
|
string.memset(init_args, 0, 8 * 32)
|
||||||
init_args[0] = sizet(pool_val)
|
init_args[0] = sizet(pool_val)
|
||||||
elem_size_val: llvmlite.Value* = llvmlite.const_int64(pool, 0)
|
elem_size_val: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||||||
init_args[1] = sizet(elem_size_val)
|
init_args[1] = sizet(elem_size_val)
|
||||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
||||||
|
|
||||||
# 对每个元素调用 append(storage_ptr, elem_val)
|
# Call append(storage_ptr, elem_val) for each element
|
||||||
if elts_count > 0:
|
if elts_count > 0:
|
||||||
# 第一个元素可能已经翻译过
|
# The first element may already be translated
|
||||||
if first_elem_val is not None:
|
if first_elem_val is not None:
|
||||||
append_args: sizet* = pool.alloc(8 * 32)
|
append_args: sizet* = pool.alloc(8 * 32)
|
||||||
if append_args is not None:
|
if append_args is not None:
|
||||||
@@ -356,10 +301,10 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
|||||||
append_args[0] = sizet(first_elem_val)
|
append_args[0] = sizet(first_elem_val)
|
||||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||||||
|
|
||||||
# 翻译剩余元素
|
# Translate the remaining elements
|
||||||
for i in range(1, elts_count):
|
for i in range(1, int(elts_count)):
|
||||||
elem_node_i: ast.AST* = elts.get(i)
|
elem_node_i: ast.AST* = elts.get(i)
|
||||||
elem_val_i: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
elem_val_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||||||
if elem_val_i is not None:
|
if elem_val_i is not None:
|
||||||
ap_args: sizet* = pool.alloc(8 * 32)
|
ap_args: sizet* = pool.alloc(8 * 32)
|
||||||
if ap_args is not None:
|
if ap_args is not None:
|
||||||
|
|||||||
@@ -1,254 +1,237 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import lib.core.Gen.Translator as HT
|
import lib.core.Gen.Translator as HT
|
||||||
|
|
||||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||||
|
|
||||||
# ============================================================
|
# f-string translation constants (local copy of ExprGenMixin's to avoid a cross-module
|
||||||
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
|
# CDefine lookup bug: ExprGenFstr compiles before ExprGenMixin, so the table is unregistered).
|
||||||
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
|
|
||||||
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
|
|
||||||
# ============================================================
|
|
||||||
define FSTR_BUF_SIZE = 1024
|
define FSTR_BUF_SIZE = 1024
|
||||||
define FSTR_SPEC_MAX = 64
|
define FSTR_SPEC_MAX = 64
|
||||||
define FSTR_FMT_MAX = 64
|
define FSTR_FMT_MAX = 64
|
||||||
|
|
||||||
# ============================================================
|
# ExprGenFstr - ExprGenMixin split (stage D): f-string/formatted-value translation
|
||||||
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||||
"""f-string/格式化值翻译"""
|
"""f-string and formatted-value translation."""
|
||||||
|
|
||||||
# ============================================================
|
# translate_joined_str - f-string translation (aligned with TPC _HandleJoinedStrLlvm):
|
||||||
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
|
# join literals and format specs, snprintf into a per-site i8* stack buffer (no escape).
|
||||||
#
|
def translate_joined_str(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||||
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
|
"""Lower an f-string to snprintf into a per-site i8* stack buffer."""
|
||||||
# 栈缓冲(每站点独立,线程安全);结果是**函数内临时值,禁止逃逸**
|
|
||||||
# (return / 存字段 / 存全局 由 GenApi.fail_if_fstring_escape 拦;
|
|
||||||
# 需逃逸用 GenApi.escape_fstring_copy 显式拷贝)。
|
|
||||||
# 之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
|
|
||||||
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
|
|
||||||
# ============================================================
|
|
||||||
def translate_joined_str(self, builder: llvmlite.IRBuilder*,
|
|
||||||
pool: memhub.MemBuddy*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
node: ast.AST*,
|
|
||||||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
|
||||||
"""f-string → snprintf 生成 i8* 栈缓冲(函数内临时值,禁止逃逸)"""
|
|
||||||
if node is None or builder is None or trans is None: return None
|
if node is None or builder is None or trans is None: return None
|
||||||
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](node)
|
js: ast.JoinedStr* = ast.JoinedStr*(node)
|
||||||
if js is None or js.values is None: return None
|
if js is None or js.values is None: return None
|
||||||
|
|
||||||
# 编译期格式串缓冲(pool 分配)
|
|
||||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
|
||||||
if fmt_buf is None: return None
|
|
||||||
fmt_buf[0] = '\0'
|
|
||||||
|
|
||||||
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
|
|
||||||
arg_head: llvmlite.Value* = None
|
|
||||||
arg_tail: llvmlite.Value* = None
|
|
||||||
arg_count: int = 0
|
|
||||||
|
|
||||||
parts: list[ast.AST*]* = js.values
|
parts: list[ast.AST*]* = js.values
|
||||||
pn: sizet = len(parts)
|
pn: sizet = len(parts)
|
||||||
for pi in range(pn):
|
|
||||||
|
# Size the format buffer from the actual parts: each literal may double ('%' -> '%%') and
|
||||||
|
# each conversion contributes a short spec. A fixed 1024 silently dropped trailing
|
||||||
|
# literals/converters while their arguments were still appended -> wrong runtime output.
|
||||||
|
fmt_cap: sizet = 16
|
||||||
|
for pi0 in range(int(pn)):
|
||||||
|
part0: ast.AST* = parts.get(pi0)
|
||||||
|
if part0 is None: continue
|
||||||
|
if part0.kind() == ast.ASTKind.Constant:
|
||||||
|
pc0: ast.Constant* = ast.Constant*(part0)
|
||||||
|
if pc0.const_kind == ast.CONST_STR and pc0.str_val is not None:
|
||||||
|
fmt_cap = fmt_cap + string.strlen(pc0.str_val) * 2
|
||||||
|
elif part0.kind() == ast.ASTKind.FormattedValue:
|
||||||
|
fmt_cap = fmt_cap + FSTR_SPEC_MAX + 16
|
||||||
|
|
||||||
|
# Compile-time format string buffer (pool-allocated)
|
||||||
|
fmt_buf: str = pool.alloc(fmt_cap)
|
||||||
|
if fmt_buf is None: return None
|
||||||
|
fmt_buf[0] = '\0'
|
||||||
|
|
||||||
|
# Runtime argument list (linked by Value.Next, matching the printf call)
|
||||||
|
arg_head: mlirlite.Value* = None
|
||||||
|
arg_tail: mlirlite.Value* = None
|
||||||
|
arg_count: int = 0
|
||||||
|
|
||||||
|
for pi in range(int(pn)):
|
||||||
part: ast.AST* = parts.get(pi)
|
part: ast.AST* = parts.get(pi)
|
||||||
if part is None: continue
|
if part is None: continue
|
||||||
if part.kind() == ast.ASTKind.Constant:
|
if part.kind() == ast.ASTKind.Constant:
|
||||||
pcn: ast.Constant* = ast.Constant*(part)
|
pcn: ast.Constant* = ast.Constant*(part)
|
||||||
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
||||||
self._fstr_append_literal(fmt_buf, pcn.str_val)
|
self._fstr_append_literal(fmt_buf, fmt_cap, pcn.str_val)
|
||||||
continue
|
continue
|
||||||
if part.kind() != ast.ASTKind.FormattedValue: continue
|
if part.kind() != ast.ASTKind.FormattedValue: continue
|
||||||
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part)
|
fv: ast.FormattedValue* = ast.FormattedValue*(part)
|
||||||
if fv.value is None: continue
|
if fv.value is None: continue
|
||||||
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||||
if val is None:
|
if val is None:
|
||||||
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
|
# fail-fast: a failed formatted-value translation must abort compilation
|
||||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
|
||||||
continue
|
continue
|
||||||
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
|
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||||
|
if fv.format_spec is None:
|
||||||
|
str_ov_fs: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||||
|
if str_ov_fs is not None: val = str_ov_fs
|
||||||
|
# Determine the C format specifier (format_spec first, else infer from the value type)
|
||||||
c_fmt: str = None
|
c_fmt: str = None
|
||||||
if fv.format_spec is not None:
|
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||||
if c_fmt is None:
|
if c_fmt is None:
|
||||||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||||
if c_fmt is None:
|
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
# Append the format specifier
|
||||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||||
# 追加格式说明符
|
# Convert the argument type (i8/i16->i32, bool->i32, f32->f64)
|
||||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||||
string.strcat(fmt_buf, c_fmt)
|
if cast_val is None: cast_val = val
|
||||||
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
|
|
||||||
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
|
||||||
if cast_val is None:
|
|
||||||
cast_val = val
|
|
||||||
cast_val.Next = None
|
cast_val.Next = None
|
||||||
if arg_head is None:
|
if arg_head is None: arg_head = cast_val
|
||||||
arg_head = cast_val
|
else: arg_tail.Next = cast_val
|
||||||
else:
|
|
||||||
arg_tail.Next = cast_val
|
|
||||||
arg_tail = cast_val
|
arg_tail = cast_val
|
||||||
arg_count += 1
|
arg_count += 1
|
||||||
|
|
||||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||||
def translate_formatted_value(self, builder: llvmlite.IRBuilder*,
|
def translate_formatted_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||||
pool: memhub.MemBuddy*,
|
"""Translate a bare FormattedValue without any literal parts."""
|
||||||
mod: llvmlite.LLVMModule*,
|
if node is None or builder is None or trans is None: return None
|
||||||
node: ast.AST*,
|
fv: ast.FormattedValue* = ast.FormattedValue*(node)
|
||||||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
if fv is None or fv.value is None: return None
|
||||||
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
|
# A bare FormattedValue contributes exactly one conversion spec (bounded by FSTR_SPEC_MAX
|
||||||
if node is None or builder is None or trans is None:
|
# plus the C prefix/width); size the buffer from that instead of the fixed 1024.
|
||||||
return None
|
fmt_cap: sizet = FSTR_SPEC_MAX + 64
|
||||||
fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](node)
|
fmt_buf: str = pool.alloc(fmt_cap)
|
||||||
if fv is None or fv.value is None:
|
if fmt_buf is None: return None
|
||||||
return None
|
|
||||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
|
||||||
if fmt_buf is None:
|
|
||||||
return None
|
|
||||||
fmt_buf[0] = '\0'
|
fmt_buf[0] = '\0'
|
||||||
arg_head: llvmlite.Value* = None
|
arg_head: mlirlite.Value* = None
|
||||||
arg_tail: llvmlite.Value* = None
|
arg_tail: mlirlite.Value* = None
|
||||||
arg_count: int = 0
|
arg_count: int = 0
|
||||||
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fv.value, None, 0, trans)
|
||||||
if val is None:
|
if val is None:
|
||||||
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
|
# fail-fast: a failed bare FormattedValue translation must abort compilation
|
||||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string formatted value translation failed (expression returned None)", "TypeError")
|
||||||
else:
|
else:
|
||||||
|
# Object defines __str__ and no format is given -> use its returned string (like TPC)
|
||||||
|
if fv.format_spec is None:
|
||||||
|
str_ov_sf: mlirlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
|
||||||
|
if str_ov_sf is not None: val = str_ov_sf
|
||||||
c_fmt: str = None
|
c_fmt: str = None
|
||||||
if fv.format_spec is not None:
|
if fv.format_spec is not None: c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||||||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
if c_fmt is None: c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||||||
if c_fmt is None:
|
if c_fmt is None:
|
||||||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||||
if c_fmt is None:
|
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
if cast_val is None: cast_val = val
|
||||||
string.strcat(fmt_buf, c_fmt)
|
|
||||||
cast_val: llvmlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
|
||||||
if cast_val is None:
|
|
||||||
cast_val = val
|
|
||||||
cast_val.Next = None
|
cast_val.Next = None
|
||||||
arg_head = cast_val
|
arg_head = cast_val
|
||||||
arg_tail = cast_val
|
arg_tail = cast_val
|
||||||
arg_count = 1
|
arg_count = 1
|
||||||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||||||
def _fstr_finalize(self, builder: llvmlite.IRBuilder*,
|
def _fstr_finalize(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, fmt_buf: str, arg_head: mlirlite.Value*, arg_tail: mlirlite.Value*, arg_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
pool: memhub.MemBuddy*,
|
"""Emit the final snprintf over the global format and per-site buffer."""
|
||||||
mod: llvmlite.LLVMModule*,
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
fmt_buf: str,
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
arg_head: llvmlite.Value*,
|
|
||||||
arg_tail: llvmlite.Value*,
|
|
||||||
arg_count: int,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""f-string 尾部:全局格式串 + 每站点栈缓冲 + snprintf,返回 i8*(函数内临时值)"""
|
|
||||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
|
||||||
if fmt_buf is None or fmt_buf[0] == '\0':
|
if fmt_buf is None or fmt_buf[0] == '\0':
|
||||||
# 空 f-string(f"")→ 空字符串常量
|
# Empty f-string (f"") -> empty string constant
|
||||||
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
|
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
|
||||||
# 格式串 → 全局字符串常量(i8*)
|
# Format string -> global string constant (i8*)
|
||||||
fmt_ptr: llvmlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
|
fmt_ptr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans)
|
||||||
if fmt_ptr is None: return None
|
if fmt_ptr is None: return None
|
||||||
# 结果缓冲:**每站点独立栈缓冲**(entry 块 alloca;每次翻译本站点各建一个,
|
# Result buffer: a per-site entry alloca (thread-safe, no runtime dependency), so the
|
||||||
# 站点之间不共享 ⇒ 同函数多个 f-string 不再互相覆盖)。
|
# f-string result is a function-local temporary that must not escape; copy to escape.
|
||||||
#
|
buf_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
||||||
# 语义(用户 2026-09-27 决定):f-string 结果是**当前函数内的临时值**。
|
buf_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
|
||||||
# · 栈缓冲 ⇒ 线程安全(每个线程各自一份栈帧,同站点并发也不串味)
|
|
||||||
# + 零运行时依赖(内核没有 C-ABI malloc,也不能假设任何分配器);
|
|
||||||
# · 函数返回即失效 ⇒ **禁止逃逸**:return / 存字段 / 存全局 / 存容器一律
|
|
||||||
# 由 GenApi.fail_if_fstring_escape 在翻译期 fail-fast 拦下。历史事故:
|
|
||||||
# 旧实现让调用方把该栈指针 return 出去,再作 %s 喂 snprintf ⇒
|
|
||||||
# ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。
|
|
||||||
# · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy
|
|
||||||
# (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法
|
|
||||||
# (AssignGenMixin.const_assign_msg)。
|
|
||||||
buf_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
|
||||||
buf_alloca: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
|
|
||||||
if buf_alloca is None: return None
|
if buf_alloca is None: return None
|
||||||
zero_i32: llvmlite.Value* = llvmlite.const_int32(pool, 0)
|
zero_i32: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||||
buf_ptr: llvmlite.Value* = llvmlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
buf_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
||||||
if buf_ptr is None: return None
|
if buf_ptr is None: return None
|
||||||
|
# f-string lowers to snprintf, declared in includes/stdio.vp. If the source imports no
|
||||||
|
# module declaring it, add the declare here (implicit-provide); i8*, i64, i8* + varargs.
|
||||||
|
if self.Gen.CallGenBase.find_func_in_module(mod, "snprintf") is None:
|
||||||
|
snprintf_decl: mlirlite.Function* = mlirlite.create_declare(pool, mod, "snprintf", i32_ty)
|
||||||
|
if snprintf_decl is not None:
|
||||||
|
snprintf_decl.IsVarArg = 1
|
||||||
|
# buf/fmt are pointer params (i8*), not the element type i8: a bare i8 declare
|
||||||
|
# would cause an MLIR 'llvm.call' operand type mismatch ('!llvm.ptr' != 'i8').
|
||||||
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
|
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "buf")
|
||||||
|
mlirlite.add_param(pool, snprintf_decl, mlirlite.Int64(pool), "size")
|
||||||
|
mlirlite.add_param(pool, snprintf_decl, i8_ptr_ty, "fmt")
|
||||||
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
|
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
|
||||||
size_val: llvmlite.Value* = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
|
size_val: mlirlite.Value* = mlirlite.const_int64(pool, FSTR_BUF_SIZE)
|
||||||
buf_ptr.Next = size_val
|
buf_ptr.Next = size_val
|
||||||
size_val.Next = fmt_ptr
|
size_val.Next = fmt_ptr
|
||||||
fmt_ptr.Next = arg_head
|
fmt_ptr.Next = arg_head
|
||||||
if arg_tail is not None:
|
if arg_tail is not None: arg_tail.Next = None
|
||||||
arg_tail.Next = None
|
mlirlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||||||
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
|
||||||
return buf_ptr
|
return buf_ptr
|
||||||
def _fstr_append_literal(self, fmt_buf: str,
|
def _fstr_append_literal(self, fmt_buf: str, cap: sizet, lit: str) -> None:
|
||||||
lit: str) -> None:
|
"""Append a literal to the format string, escaping percent signs; fail-fast on overflow."""
|
||||||
"""追加字面量到格式串(% 转义为 %%)"""
|
|
||||||
if fmt_buf is None or lit is None: return
|
if fmt_buf is None or lit is None: return
|
||||||
cur_len: sizet = string.strlen(fmt_buf)
|
cur_len: sizet = string.strlen(fmt_buf)
|
||||||
rem: sizet = FSTR_BUF_SIZE - 1 - cur_len
|
|
||||||
if rem == 0: return
|
|
||||||
src_len: sizet = string.strlen(lit)
|
src_len: sizet = string.strlen(lit)
|
||||||
|
# Worst case: every character expands to "%%".
|
||||||
|
if cur_len + src_len * 2 + 1 > cap:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + up to {src_len * 2} escaped + 1 NUL); shorten the literal", "OverflowError")
|
||||||
si: sizet = 0
|
si: sizet = 0
|
||||||
while si < src_len and rem > 0:
|
while si < src_len:
|
||||||
ch: char = lit[si]
|
ch: char = lit[si]
|
||||||
if ch == '%':
|
if ch == '%':
|
||||||
if rem < 2:
|
|
||||||
break
|
|
||||||
fmt_buf[cur_len] = '%'
|
fmt_buf[cur_len] = '%'
|
||||||
fmt_buf[cur_len + 1] = '%'
|
fmt_buf[cur_len + 1] = '%'
|
||||||
cur_len += 2
|
cur_len += 2
|
||||||
rem -= 2
|
|
||||||
else:
|
else:
|
||||||
fmt_buf[cur_len] = ch
|
fmt_buf[cur_len] = ch
|
||||||
cur_len += 1
|
cur_len += 1
|
||||||
rem -= 1
|
|
||||||
si += 1
|
si += 1
|
||||||
fmt_buf[cur_len] = '\0'
|
fmt_buf[cur_len] = '\0'
|
||||||
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
|
def _fstr_append_raw(self, fmt_buf: str, cap: sizet, s: str) -> None:
|
||||||
"""追加原始字符串(不做 % 转义)"""
|
"""Append a raw string without percent escaping; fail-fast on overflow."""
|
||||||
if fmt_buf is None or s is None:
|
if fmt_buf is None or s is None: return
|
||||||
return
|
|
||||||
cur_len: sizet = string.strlen(fmt_buf)
|
cur_len: sizet = string.strlen(fmt_buf)
|
||||||
src_len: sizet = string.strlen(s)
|
src_len: sizet = string.strlen(s)
|
||||||
if cur_len + src_len < FSTR_BUF_SIZE:
|
if cur_len + src_len + 1 > cap:
|
||||||
string.strcpy(fmt_buf + cur_len, s)
|
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + {src_len} to append + 1 NUL); shorten the format spec", "OverflowError")
|
||||||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*,
|
i: sizet = 0
|
||||||
spec_node: ast.AST*,
|
while i < src_len:
|
||||||
val: llvmlite.Value*,
|
fmt_buf[cur_len + i] = s[i]
|
||||||
arg_node: ast.AST*) -> str:
|
i += 1
|
||||||
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
|
fmt_buf[cur_len + src_len] = '\0'
|
||||||
if spec_node is None or pool is None or val is None:
|
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*, spec_node: ast.AST*, val: mlirlite.Value*, arg_node: ast.AST*) -> str:
|
||||||
return None
|
"""Convert a Python format_spec into a C printf format string."""
|
||||||
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
|
if spec_node is None or pool is None or val is None: return None
|
||||||
|
# 1. Extract the spec string (JoinedStr with constants only)
|
||||||
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
||||||
if spec is None:
|
if spec is None: return None
|
||||||
return None
|
|
||||||
spec[0] = '\0'
|
spec[0] = '\0'
|
||||||
sk: int = spec_node.kind()
|
sk: int = spec_node.kind()
|
||||||
if sk == ast.ASTKind.JoinedStr:
|
if sk == ast.ASTKind.JoinedStr:
|
||||||
jss: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](spec_node)
|
jss: ast.JoinedStr* = ast.JoinedStr*(spec_node)
|
||||||
if jss.values is None: return None
|
if jss.values is None: return None
|
||||||
vn: sizet = len(jss.values)
|
vn: sizet = len(jss.values)
|
||||||
for vi in range(vn):
|
for vi in range(int(vn)):
|
||||||
vpart: ast.AST* = jss.values.get(vi)
|
vpart: ast.AST* = jss.values.get(vi)
|
||||||
if vpart is None: continue
|
if vpart is None: continue
|
||||||
if vpart.kind() != ast.ASTKind.Constant: return None
|
if vpart.kind() != ast.ASTKind.Constant: return None
|
||||||
vcn: ast.Constant* = ast.Constant*(vpart)
|
vcn: ast.Constant* = ast.Constant*(vpart)
|
||||||
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
|
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
|
||||||
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
|
# fail-fast: a truncated spec can still look valid (e.g. ">10.5f" cut to ">10."),
|
||||||
string.strcat(spec, vcn.str_val)
|
# which silently changes the output format.
|
||||||
|
if string.strlen(spec) + string.strlen(vcn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||||
|
string.strcat(spec, vcn.str_val)
|
||||||
elif sk == ast.ASTKind.Constant:
|
elif sk == ast.ASTKind.Constant:
|
||||||
cn: ast.Constant* = ast.Constant*(spec_node)
|
cn: ast.Constant* = ast.Constant*(spec_node)
|
||||||
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
|
if cn.const_kind != ast.CONST_STR or cn.str_val is None: return None
|
||||||
return None
|
if string.strlen(cn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||||
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
||||||
else: return None
|
else: return None
|
||||||
slen: sizet = string.strlen(spec)
|
slen: sizet = string.strlen(spec)
|
||||||
if slen == 0: return None
|
if slen == 0: return None
|
||||||
|
|
||||||
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
|
# 2. Parse segment by segment: [fill]align / sign / # / 0 / width / , / .precision / type
|
||||||
pos: sizet = 0
|
pos: sizet = 0
|
||||||
align_char: char = 0
|
align_char: char = 0
|
||||||
if pos < slen:
|
if pos < slen:
|
||||||
@@ -270,10 +253,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
c_prec[0] = '\0'
|
c_prec[0] = '\0'
|
||||||
# sign
|
# sign
|
||||||
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
|
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
|
||||||
if spec[pos] == '+':
|
if spec[pos] == '+': string.strcat(c_flags, "+")
|
||||||
string.strcat(c_flags, "+")
|
elif spec[pos] == ' ': string.strcat(c_flags, " ")
|
||||||
elif spec[pos] == ' ':
|
|
||||||
string.strcat(c_flags, " ")
|
|
||||||
pos += 1
|
pos += 1
|
||||||
# alt '#'
|
# alt '#'
|
||||||
if pos < slen and spec[pos] == '#':
|
if pos < slen and spec[pos] == '#':
|
||||||
@@ -283,9 +264,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
if pos < slen and spec[pos] == '0':
|
if pos < slen and spec[pos] == '0':
|
||||||
string.strcat(c_flags, "0")
|
string.strcat(c_flags, "0")
|
||||||
pos += 1
|
pos += 1
|
||||||
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
|
# Left align '<'/'^' -> '-' (C semantics give '-' priority over '0')
|
||||||
if align_char == '<' or align_char == '^':
|
if align_char == '<' or align_char == '^': string.strcat(c_flags, "-")
|
||||||
string.strcat(c_flags, "-")
|
|
||||||
# width digits
|
# width digits
|
||||||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||||||
wl: sizet = string.strlen(c_width)
|
wl: sizet = string.strlen(c_width)
|
||||||
@@ -293,9 +273,8 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
c_width[wl] = spec[pos]
|
c_width[wl] = spec[pos]
|
||||||
c_width[wl + 1] = '\0'
|
c_width[wl + 1] = '\0'
|
||||||
pos += 1
|
pos += 1
|
||||||
# comma ',' → C printf 不支持千分位,忽略
|
# comma ',' -> C printf has no thousands separator, ignore
|
||||||
if pos < slen and spec[pos] == ',':
|
if pos < slen and spec[pos] == ',': pos += 1
|
||||||
pos += 1
|
|
||||||
# .precision
|
# .precision
|
||||||
if pos < slen and spec[pos] == '.':
|
if pos < slen and spec[pos] == '.':
|
||||||
string.strcat(c_prec, ".")
|
string.strcat(c_prec, ".")
|
||||||
@@ -307,28 +286,26 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
c_prec[pl + 1] = '\0'
|
c_prec[pl + 1] = '\0'
|
||||||
pos += 1
|
pos += 1
|
||||||
if string.strlen(c_prec) == 1:
|
if string.strlen(c_prec) == 1:
|
||||||
return None # 只有 '.' 无效
|
return None # a lone '.' is invalid
|
||||||
# type char
|
# type char
|
||||||
fmt_type: char = 0
|
fmt_type: char = 0
|
||||||
if pos < slen:
|
if pos < slen:
|
||||||
fmt_type = spec[pos]
|
fmt_type = spec[pos]
|
||||||
pos += 1
|
pos += 1
|
||||||
if pos != slen: return None # 多余字符无法解析
|
if pos != slen: return None # extra characters cannot be parsed
|
||||||
|
|
||||||
# 3. 长度修饰(按值类型位宽)
|
# 3. Length modifier (by value type bit width)
|
||||||
len_mod: str = pool.alloc(4)
|
len_mod: str = pool.alloc(4)
|
||||||
if len_mod is None: return None
|
if len_mod is None: return None
|
||||||
len_mod[0] = '\0'
|
len_mod[0] = '\0'
|
||||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||||
# 32 位**不写**长度修饰符(N14):`{x:08X}` 的实参是 i32,此前推导出
|
# Do not write a length modifier for 32-bit (N14): `{x:08X}` passes an i32, but `l`
|
||||||
# `l` → 格式串 `%08lX`(long)与实参宽度不符(LP64 下 printf 按 64 位
|
# would make %08lX (long) mismatch on LP64 and misread the varargs.
|
||||||
# 读参 → 变参错位)。与同文件无 spec 路径 `_fstr_infer_c_fmt`(32 位用
|
|
||||||
# `%u/%d`)以及 TPC 侧 HandlesExprFormat.py 保持一致。
|
|
||||||
if ibits == 64: string.strcpy(len_mod, "ll")
|
if ibits == 64: string.strcpy(len_mod, "ll")
|
||||||
elif ibits == 16: string.strcpy(len_mod, "h")
|
elif ibits == 16: string.strcpy(len_mod, "h")
|
||||||
elif ibits == 8: string.strcpy(len_mod, "hh")
|
elif ibits == 8: string.strcpy(len_mod, "hh")
|
||||||
|
|
||||||
# 4. 组装 C 格式串
|
# 4. Assemble the C format string
|
||||||
out: str = pool.alloc(FSTR_FMT_MAX)
|
out: str = pool.alloc(FSTR_FMT_MAX)
|
||||||
if out is None: return None
|
if out is None: return None
|
||||||
out[0] = '\0'
|
out[0] = '\0'
|
||||||
@@ -337,58 +314,43 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
string.strcat(out, c_width)
|
string.strcat(out, c_width)
|
||||||
string.strcat(out, c_prec)
|
string.strcat(out, c_prec)
|
||||||
if fmt_type == 0:
|
if fmt_type == 0:
|
||||||
# 无 type:按值类型推断
|
# No type: infer from the value type
|
||||||
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||||
if ibits != 0:
|
if ibits != 0:
|
||||||
if ibits == 8:
|
if ibits == 8: string.strcat(out, "c")
|
||||||
string.strcat(out, "c")
|
elif ibits == 1: string.strcat(out, "d")
|
||||||
elif ibits == 1:
|
|
||||||
string.strcat(out, "d")
|
|
||||||
else:
|
else:
|
||||||
string.strcat(out, len_mod)
|
string.strcat(out, len_mod)
|
||||||
string.strcat(out, "d")
|
string.strcat(out, "d")
|
||||||
elif fbits2 != 0:
|
elif fbits2 != 0: string.strcat(out, "f")
|
||||||
string.strcat(out, "f")
|
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: string.strcat(out, "s")
|
||||||
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
|
else: string.strcat(out, "d")
|
||||||
string.strcat(out, "s")
|
elif fmt_type == 'c': string.strcat(out, "c")
|
||||||
else:
|
elif fmt_type == 'f' or fmt_type == 'F': string.strcat(out, "f")
|
||||||
string.strcat(out, "d")
|
|
||||||
elif fmt_type == 'c':
|
|
||||||
string.strcat(out, "c")
|
|
||||||
elif fmt_type == 'f' or fmt_type == 'F':
|
|
||||||
string.strcat(out, "f")
|
|
||||||
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
|
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
|
||||||
tmp_ch: str = pool.alloc(2)
|
tmp_ch: str = pool.alloc(2)
|
||||||
if tmp_ch is None:
|
if tmp_ch is None: return None
|
||||||
return None
|
|
||||||
tmp_ch[0] = fmt_type
|
tmp_ch[0] = fmt_type
|
||||||
tmp_ch[1] = '\0'
|
tmp_ch[1] = '\0'
|
||||||
string.strcat(out, tmp_ch)
|
string.strcat(out, tmp_ch)
|
||||||
elif fmt_type == 's':
|
elif fmt_type == 's': string.strcat(out, "s")
|
||||||
string.strcat(out, "s")
|
elif fmt_type == '%': string.strcat(out, "%")
|
||||||
elif fmt_type == '%':
|
|
||||||
string.strcat(out, "%")
|
|
||||||
elif fmt_type == 'b':
|
elif fmt_type == 'b':
|
||||||
return None # 二进制不支持
|
return None # binary is unsupported
|
||||||
elif fmt_type == 'd' or fmt_type == 'i':
|
elif fmt_type == 'd' or fmt_type == 'i':
|
||||||
string.strcat(out, len_mod)
|
string.strcat(out, len_mod)
|
||||||
string.strcat(out, "d")
|
string.strcat(out, "d")
|
||||||
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
|
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
|
||||||
string.strcat(out, len_mod)
|
string.strcat(out, len_mod)
|
||||||
tmp_ch2: str = pool.alloc(2)
|
tmp_ch2: str = pool.alloc(2)
|
||||||
if tmp_ch2 is None:
|
if tmp_ch2 is None: return None
|
||||||
return None
|
|
||||||
tmp_ch2[0] = fmt_type
|
tmp_ch2[0] = fmt_type
|
||||||
tmp_ch2[1] = '\0'
|
tmp_ch2[1] = '\0'
|
||||||
string.strcat(out, tmp_ch2)
|
string.strcat(out, tmp_ch2)
|
||||||
else:
|
else: return None
|
||||||
return None
|
|
||||||
return out
|
return out
|
||||||
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*,
|
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy*, val: mlirlite.Value*, arg_node: ast.AST*, trans: HT.Translator*) -> str:
|
||||||
val: llvmlite.Value*,
|
"""Infer the C printf format from the value type when no format_spec is given."""
|
||||||
arg_node: ast.AST*,
|
|
||||||
trans: HT.Translator*) -> str:
|
|
||||||
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
|
|
||||||
if val is None or pool is None: return None
|
if val is None or pool is None: return None
|
||||||
out: str = pool.alloc(16)
|
out: str = pool.alloc(16)
|
||||||
if out is None: return None
|
if out is None: return None
|
||||||
@@ -396,70 +358,50 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
|||||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||||
if ibits == 1: return "%d" # bool
|
if ibits == 1: return "%d" # bool
|
||||||
if ibits == 8: return "%c" # 字符
|
if ibits == 8: return "%c" # character
|
||||||
if ibits != 0:
|
if ibits != 0:
|
||||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
||||||
if ibits == 64:
|
if ibits == 64: string.strcpy(out, "%llu")
|
||||||
string.strcpy(out, "%llu")
|
else: string.strcpy(out, "%u")
|
||||||
else:
|
|
||||||
string.strcpy(out, "%u")
|
|
||||||
else:
|
else:
|
||||||
if ibits == 64:
|
if ibits == 64: string.strcpy(out, "%lld")
|
||||||
string.strcpy(out, "%lld")
|
else: string.strcpy(out, "%d")
|
||||||
else:
|
|
||||||
string.strcpy(out, "%d")
|
|
||||||
return out
|
return out
|
||||||
if fbits != 0:
|
if fbits != 0: return "%f"
|
||||||
return "%f"
|
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0: return "%s"
|
||||||
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
|
# fail-fast: missing value type (not int/float/pointer); cannot infer the C format
|
||||||
return "%s"
|
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||||
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
|
|
||||||
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
|
||||||
return None
|
return None
|
||||||
def _fstr_cast_value(self, builder: llvmlite.IRBuilder*,
|
def _fstr_cast_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, val: mlirlite.Value*, c_fmt: str, arg_node: ast.AST*, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
pool: memhub.MemBuddy*,
|
"""Cast the value to the type required by the C format specifier."""
|
||||||
val: llvmlite.Value*,
|
if val is None or c_fmt is None: return None
|
||||||
c_fmt: str,
|
# Find the format type char (last letter, ignoring l/h length modifiers)
|
||||||
arg_node: ast.AST*,
|
|
||||||
trans: HT.Translator*) -> llvmlite.Value*:
|
|
||||||
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
|
|
||||||
if val is None or c_fmt is None:
|
|
||||||
return None
|
|
||||||
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
|
|
||||||
fmt_char: char = 0
|
fmt_char: char = 0
|
||||||
flen: sizet = string.strlen(c_fmt)
|
flen: sizet = string.strlen(c_fmt)
|
||||||
fi: sizet = flen
|
fi: sizet = flen
|
||||||
while fi > 0:
|
while fi > 0:
|
||||||
fi -= 1
|
fi -= 1
|
||||||
ch: char = c_fmt[fi]
|
ch: char = c_fmt[fi]
|
||||||
if ch == '%':
|
if ch == '%': break
|
||||||
break
|
|
||||||
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
|
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
|
||||||
if ch != 'l' and ch != 'h':
|
if ch != 'l' and ch != 'h':
|
||||||
fmt_char = ch
|
fmt_char = ch
|
||||||
break
|
break
|
||||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
if fmt_char == 's':
|
if fmt_char == 's': return val
|
||||||
return val
|
|
||||||
if fmt_char == 'c':
|
if fmt_char == 'c':
|
||||||
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||||
if ibits != 0 and ibits < 32:
|
if ibits != 0 and ibits < 32: return mlirlite.build_zext(builder, val, i32_ty)
|
||||||
return llvmlite.build_zext(builder, val, i32_ty)
|
|
||||||
return val
|
return val
|
||||||
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
|
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
|
||||||
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
|
|
||||||
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
|
||||||
if ibits == 1:
|
if ibits == 1: return mlirlite.build_zext(builder, val, i32_ty)
|
||||||
return llvmlite.build_zext(builder, val, i32_ty)
|
|
||||||
if ibits == 8 or ibits == 16:
|
if ibits == 8 or ibits == 16:
|
||||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
|
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0: return mlirlite.build_zext(builder, val, i32_ty)
|
||||||
return llvmlite.build_zext(builder, val, i32_ty)
|
return mlirlite.build_sext(builder, val, i32_ty)
|
||||||
return llvmlite.build_sext(builder, val, i32_ty)
|
|
||||||
return val
|
return val
|
||||||
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
|
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' or fmt_char == 'g' or fmt_char == 'G':
|
||||||
or fmt_char == 'g' or fmt_char == 'G':
|
|
||||||
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
|
||||||
if fbits == 32:
|
if fbits == 32: return mlirlite.build_fpext(builder, val, mlirlite.Double(pool))
|
||||||
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
|
|
||||||
return val
|
return val
|
||||||
return val
|
return val
|
||||||
@@ -1,127 +1,81 @@
|
|||||||
import t, c
|
from stdint import *
|
||||||
from stdint import *
|
import ast
|
||||||
import ast
|
import memhub
|
||||||
import memhub
|
import mlirlite
|
||||||
import llvmlite
|
import lib.core.Gen.Translator as HT
|
||||||
import lib.core.Gen.Translator as HT
|
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||||||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
|
||||||
|
|
||||||
|
# docstring registry capacities (__doc__ magic attribute support).
|
||||||
# ============================================================
|
# DOC_MAX: max entries; DOC_KEY_SZ: key "{module_sha1}.{symbol}" width; DOC_VAL_SZ: content width.
|
||||||
# docstring 登记表容量(__doc__ 魔法属性支持)
|
define DOC_MAX = 512
|
||||||
#
|
define DOC_KEY_SZ = 192
|
||||||
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
|
define DOC_VAL_SZ = 4096
|
||||||
# 函数/类/方法 docstring,幂等查重)
|
|
||||||
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
|
# f-string translation constants (aligned with TPC HandlesExprFormat).
|
||||||
# DOC_VAL_SZ: docstring 内容缓冲宽度
|
# FSTR_BUF_SIZE: runtime stack buffer; FSTR_SPEC_MAX: format_spec parse width; FSTR_FMT_MAX: printf format width.
|
||||||
# ============================================================
|
define FSTR_BUF_SIZE = 1024
|
||||||
define DOC_MAX = 512
|
define FSTR_SPEC_MAX = 64
|
||||||
define DOC_KEY_SZ = 192
|
define FSTR_FMT_MAX = 64
|
||||||
define DOC_VAL_SZ = 4096
|
|
||||||
|
|
||||||
# ============================================================
|
# ExprGenMixin - expression handling mixin (objectified P3).
|
||||||
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
|
|
||||||
#
|
|
||||||
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
|
# ExprGenMixin - expressions / constants / subscripts / attributes / operators.
|
||||||
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
|
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||||
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
|
"""Expression-handling mixin (pure methods, no state fields)."""
|
||||||
# ============================================================
|
|
||||||
define FSTR_BUF_SIZE = 1024
|
# Translate a value expression (RHS dispatch) - module-level version.
|
||||||
define FSTR_SPEC_MAX = 64
|
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||||
define FSTR_FMT_MAX = 64
|
"""Translate a value expression to an LLVM Value (depth-guard wrapper; see _translate_value_impl).
|
||||||
|
target_ty = assignment/parameter type context; use_ctx = real call site for CDefine width checks."""
|
||||||
|
if trans is not None:
|
||||||
# ============================================================
|
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "expression translation nested too deep (suspected AST self-reference cycle or translation bug)", "RecursionError")
|
||||||
# ExprGenMixin - 表达式处理 Mixin(对象化 P3)
|
trans.TranslateDepth += 1
|
||||||
# ============================================================
|
rv: mlirlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
|
||||||
|
if trans is not None: trans.TranslateDepth -= 1
|
||||||
|
return rv
|
||||||
# ============================================================
|
|
||||||
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
|
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
|
||||||
# ============================================================
|
"""Translate a value expression to an LLVM Value."""
|
||||||
novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
if node is None: return None
|
||||||
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
|
k: int = node.kind()
|
||||||
|
if k == ast.ASTKind.Constant:
|
||||||
# ============================================================
|
rv: mlirlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
|
||||||
# 翻译值表达式(RHS 分派)— 模块级版本
|
return rv
|
||||||
# ============================================================
|
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
|
||||||
def translate_value(self, builder: llvmlite.IRBuilder*,
|
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans, target_ty)
|
||||||
pool: memhub.MemBuddy*,
|
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
|
||||||
mod: llvmlite.LLVMModule*,
|
# M2.4 `new stack|heap|memgr(x) [slife|hlife] T(args)`:只换"取存储",其余复用构造序列
|
||||||
node: ast.AST*,
|
elif k == ast.ASTKind.New: return self.Gen.CallGenCtor._translate_new_expr(pool, builder, mod, ast.New*(node), trans)
|
||||||
funcs_ptr: t.CPtr = None,
|
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
|
||||||
func_count: int = 0,
|
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans, target_ty)
|
||||||
trans: HT.Translator* = None,
|
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
|
||||||
target_ty: llvmlite.LLVMType* = None,
|
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
|
||||||
use_ctx: str = None) -> llvmlite.Value*:
|
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
|
||||||
"""翻译值表达式为 LLVM Value(深度守卫包装器,原逻辑见 _translate_value_impl)
|
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
|
||||||
|
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
|
||||||
target_ty: 目标类型上下文(如调用形参类型/赋值目标类型),用于单引号
|
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
|
||||||
单字符字面量(CONST_CHAR)在字符串目标下物化为 1 字符字符串(对齐 TPC)。
|
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
|
||||||
use_ctx: 使用处**真实站点种类**标签("局部变量注解" / "模块级全局" /
|
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
|
||||||
"形参" / "返回注解" …),只被 CDefine 使用处宽度不足报文读取;不参与判定。
|
elif k == ast.ASTKind.Await:
|
||||||
"""
|
# Coroutine await (VPC-M1): inline-translate when the await target is an async call.
|
||||||
if trans is not None:
|
# Returns the child coroutine's i32 ret; top-level await is handled by AsyncGen.
|
||||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
|
aw: ast.Await* = ast.Await*(node)
|
||||||
self.Gen.TypeGenErr.fatal_error(node, "表达式翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
|
||||||
trans.TranslateDepth += 1
|
aw_cl: ast.Call* = ast.Call*(aw.value)
|
||||||
rv: llvmlite.Value* = self._translate_value_impl(builder, pool, mod, node, funcs_ptr, func_count, trans, target_ty, use_ctx)
|
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
|
||||||
if trans is not None:
|
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
|
||||||
trans.TranslateDepth -= 1
|
if aw_nm.id is not None and trans is not None:
|
||||||
return rv
|
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0: return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
|
||||||
|
return None
|
||||||
def _translate_value_impl(self, builder: llvmlite.IRBuilder*,
|
elif k == ast.ASTKind.Starred:
|
||||||
pool: memhub.MemBuddy*,
|
# *x in a value context: the parser tags '*' as Starred, but Viper has no unpacking.
|
||||||
mod: llvmlite.LLVMModule*,
|
# Treat it as C unary deref (*ptr -> load pointee) by building a Deref UnaryOp.
|
||||||
node: ast.AST*,
|
st: ast.Starred* = ast.Starred*(node)
|
||||||
funcs_ptr: t.CPtr = None,
|
if st is None or st.value is None: return None
|
||||||
func_count: int = 0,
|
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
|
||||||
trans: HT.Translator* = None,
|
if deref_node is None: return None
|
||||||
target_ty: llvmlite.LLVMType* = None,
|
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
|
||||||
use_ctx: str = None) -> llvmlite.Value*:
|
return None
|
||||||
"""翻译值表达式为 LLVM Value"""
|
|
||||||
if node is None:
|
|
||||||
return None
|
|
||||||
k: int = node.kind()
|
|
||||||
if k == ast.ASTKind.Constant:
|
|
||||||
rv: llvmlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
|
|
||||||
return rv
|
|
||||||
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
|
|
||||||
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
|
|
||||||
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
|
|
||||||
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.List: return self.Gen.ExprGenConst.translate_list_literal(builder, pool, mod, node, None, trans)
|
|
||||||
elif k == ast.ASTKind.IfExp: return self.Gen.ExprGenOps.translate_ifexp(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.Lambda: return self.Gen.FuncGen.translate_lambda(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.JoinedStr: return self.Gen.ExprGenFstr.translate_joined_str(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.FormattedValue: return self.Gen.ExprGenFstr.translate_formatted_value(builder, pool, mod, node, trans)
|
|
||||||
elif k == ast.ASTKind.Await:
|
|
||||||
# 协程 await(TPV-M1):await 目标是 async 函数调用时内联翻译
|
|
||||||
# (子帧创建 + resume 到 DONE + 取子帧 ret 存父帧 await 槽 + 父挂起),
|
|
||||||
# 返回子协程 ret 值(i32)。顶层 await(AnnAssign/Assign 的 value 直接
|
|
||||||
# 是 Await)由 AsyncGen 段翻译特判处理,本分支覆盖表达式上下文中的
|
|
||||||
# await(M1 覆盖有限场景,详见 AsyncGenMixin._emit_await)。
|
|
||||||
aw: rawptr[ast.Await] = rawptr[ast.Await](node)
|
|
||||||
if aw is not None and aw.value is not None and aw.value.kind() == ast.ASTKind.Call:
|
|
||||||
aw_cl: ast.Call* = ast.Call*(aw.value)
|
|
||||||
if aw_cl.func is not None and aw_cl.func.kind() == ast.ASTKind.Name:
|
|
||||||
aw_nm: ast.Name* = ast.Name*(aw_cl.func)
|
|
||||||
if aw_nm.id is not None and trans is not None:
|
|
||||||
if self.Gen.AsyncGen.is_async_func(trans, aw_nm.id) != 0:
|
|
||||||
return self.Gen.AsyncGen._emit_await(builder, pool, mod, trans, aw, aw_nm.id)
|
|
||||||
return None
|
|
||||||
elif k == ast.ASTKind.Starred:
|
|
||||||
# *x 在值上下文(如 printf("%d", *k) 实参):解析器把调用参数位的
|
|
||||||
# '*' 按 Python *args 解包语义归类为 Starred,但 Viper 未实现解包,
|
|
||||||
# 全代码库无调用点使用;按 C 语义视为单目解引用(*ptr → load pointee,
|
|
||||||
# 对齐 UnaryOp(Deref))。构造 Deref UnaryOp 复用既有翻译/错误路径。
|
|
||||||
st: rawptr[ast.Starred] = rawptr[ast.Starred](node)
|
|
||||||
if st is None or st.value is None: return None
|
|
||||||
deref_node: ast.AST* = ast.UnaryOp(pool, ast.OpKind.Deref, st.value)
|
|
||||||
if deref_node is None: return None
|
|
||||||
return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans)
|
|
||||||
return None
|
|
||||||
File diff suppressed because it is too large.
Load diff
+1509
-1527
File diff suppressed because it is too large.
Load diff
@@ -1,371 +1,282 @@
|
|||||||
import t, c
|
from stdint import *
|
||||||
from stdint import *
|
import ast
|
||||||
import ast
|
import mlirlite
|
||||||
import llvmlite
|
import memhub
|
||||||
import memhub
|
import string
|
||||||
import string
|
import viperlib
|
||||||
import viperlib
|
import stdlib
|
||||||
import stdio
|
import lib.core.VLogger as VLogger
|
||||||
import stdlib
|
import lib.core.Gen.Translator as HT
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.Gen.GenApi as GenApi
|
||||||
import lib.core.Gen.Translator as HT
|
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||||
import lib.core.Gen.GenApi as GenApi
|
|
||||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
|
||||||
|
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||||
# ============================================================
|
"""Shared helpers for function attributes, naming, mangling and creation."""
|
||||||
# FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具
|
|
||||||
# ============================================================
|
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
|
||||||
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
|
||||||
"""函数属性 / 命名 / mangle / create_function 基础工具"""
|
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||||
|
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
|
||||||
# ============================================================
|
if decorator_list is None: return None
|
||||||
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
|
dn: sizet = len(decorator_list)
|
||||||
#
|
if dn == 0: return None
|
||||||
# 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式
|
|
||||||
# 也支持 @c.Attribute(t.attr.llvm.xxx) 形式
|
attrs_buf: str = pool.alloc(256)
|
||||||
#
|
if attrs_buf is None: return None
|
||||||
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
|
attrs_buf[0] = '\0'
|
||||||
# ============================================================
|
found_any: int = 0
|
||||||
def extract_func_attrs(self, pool: memhub.MemBuddy*,
|
|
||||||
decorator_list: list[ast.AST*]*,
|
for di in range(int(dn)):
|
||||||
imported_modules: str) -> str:
|
deco: ast.AST* = decorator_list.get(di)
|
||||||
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
|
if deco is None: continue
|
||||||
if decorator_list is None: return None
|
dk: int = deco.kind()
|
||||||
dn: sizet = len(decorator_list)
|
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||||
if dn == 0: return None
|
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||||
|
if dk == ast.ASTKind.Attribute:
|
||||||
attrs_buf: str = pool.alloc(256)
|
dan: str = self._get_attr_name_from_node(deco)
|
||||||
if attrs_buf is None: return None
|
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||||
attrs_buf[0] = '\0'
|
if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1
|
||||||
found_any: int = 0
|
continue
|
||||||
|
if dk != ast.ASTKind.Call: continue
|
||||||
for di in range(dn):
|
call_node: ast.Call* = ast.Call*(deco)
|
||||||
deco: ast.AST* = decorator_list.get(di)
|
if call_node.func is None: continue
|
||||||
if deco is None: continue
|
cfk: int = call_node.func.kind()
|
||||||
dk: int = deco.kind()
|
|
||||||
# 直接形式 `@t.attr.<name>`(与类侧 ClassGenBase._has_decorator 同口径,
|
# Detect the builtin Attribute(...) wrapper emitted by @__attribute__(...).
|
||||||
# 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态,
|
if cfk != ast.ASTKind.Name: continue
|
||||||
# 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件
|
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||||
# 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略
|
if func_nm.id is None: continue
|
||||||
# (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。
|
if string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||||
if dk == ast.ASTKind.Attribute:
|
|
||||||
dan: str = self._get_attr_name_from_node(deco)
|
# Iterate args and extract attrs
|
||||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
if call_node.args is None: continue
|
||||||
if self._append_llvm_attr(attrs_buf, dan) != 0:
|
args_list: list[ast.AST*]* = call_node.args
|
||||||
found_any = 1
|
an: sizet = len(args_list)
|
||||||
continue
|
for ai in range(int(an)):
|
||||||
if dk != ast.ASTKind.Call: continue
|
arg_node: ast.AST* = args_list.get(ai)
|
||||||
call_node: ast.Call* = ast.Call*(deco)
|
if arg_node is None: continue
|
||||||
if call_node.func is None: continue
|
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||||
cfk: int = call_node.func.kind()
|
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||||
if cfk != ast.ASTKind.Attribute: continue
|
if attr_name is None: continue
|
||||||
|
# section string attr cannot be inlined on the signature line (llc: expected
|
||||||
# 检测 c.Attribute
|
# string constant); it must go through attr group #N, handled by FuncGenDef.
|
||||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
if string.strcmp(attr_name, "section") == 0: continue
|
||||||
if func_attr.attr is None: continue
|
# Map to LLVM attr name and append
|
||||||
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||||
if func_attr.value is None: continue
|
if ar != 0: found_any = 1
|
||||||
fvk: int = func_attr.value.kind()
|
|
||||||
if fvk != ast.ASTKind.Name: continue
|
if found_any == 0: return None
|
||||||
c_name: ast.Name* = ast.Name*(func_attr.value)
|
return attrs_buf
|
||||||
if c_name.id is None: continue
|
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||||
if string.strcmp(c_name.id, "c") != 0: continue
|
"""Extract @Attribute(attr.section(...)) as the attr-group text."""
|
||||||
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
|
if pool is None or decorator_list is None: return None
|
||||||
if imp_c == 0: continue
|
dn: sizet = len(decorator_list)
|
||||||
|
if dn == 0: return None
|
||||||
# 遍历参数提取属性
|
for di in range(int(dn)):
|
||||||
if call_node.args is None: continue
|
deco: ast.AST* = decorator_list.get(di)
|
||||||
args_list: list[ast.AST*]* = call_node.args
|
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||||
an: sizet = len(args_list)
|
call_node: ast.Call* = ast.Call*(deco)
|
||||||
for ai in range(an):
|
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Name: continue
|
||||||
arg_node: ast.AST* = args_list.get(ai)
|
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||||
if arg_node is None: continue
|
if func_nm.id is None or string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||||
# 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
if call_node.args is None: continue
|
||||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
args_list: list[ast.AST*]* = call_node.args
|
||||||
if attr_name is None: continue
|
an: sizet = len(args_list)
|
||||||
# B5:section 字符串属性(t.attr.section(".text.startup"))不能内联
|
for ai in range(int(an)):
|
||||||
# 在函数签名行(llc: expected string constant),必须走属性组
|
arg_node: ast.AST* = args_list.get(ai)
|
||||||
# #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。
|
if arg_node is None: continue
|
||||||
if string.strcmp(attr_name, "section") == 0: continue
|
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||||
# 映射到 LLVM 属性名并追加
|
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
|
||||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
sec_val: str = self._get_attr_call_arg_str(arg_node)
|
||||||
if ar != 0:
|
if sec_val is None: continue
|
||||||
found_any = 1
|
sec_buf: str = pool.alloc(128)
|
||||||
|
if sec_buf is None: return None
|
||||||
if found_any == 0: return None
|
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
|
||||||
return attrs_buf
|
return sec_buf
|
||||||
def extract_func_section(self, pool: memhub.MemBuddy*,
|
return None
|
||||||
decorator_list: list[ast.AST*]*,
|
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
||||||
imported_modules: str) -> str:
|
"""Extract the attribute name from a Call or Attribute AST node."""
|
||||||
"""提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。
|
if node is None: return None
|
||||||
|
k: int = node.kind()
|
||||||
LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾
|
|
||||||
attributes #N = { ... }),不能内联(llc: expected string constant)。
|
# Call node: t.attr.always_inline()
|
||||||
main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。
|
if k == ast.ASTKind.Call:
|
||||||
"""
|
call: ast.Call* = ast.Call*(node)
|
||||||
if pool is None or decorator_list is None: return None
|
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
||||||
dn: sizet = len(decorator_list)
|
at: ast.Attribute* = ast.Attribute*(call.func)
|
||||||
if dn == 0: return None
|
return at.attr
|
||||||
for di in range(dn):
|
|
||||||
deco: ast.AST* = decorator_list.get(di)
|
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
|
||||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
if k == ast.ASTKind.Attribute:
|
||||||
call_node: ast.Call* = ast.Call*(deco)
|
at2: ast.Attribute* = ast.Attribute*(node)
|
||||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
|
return at2.attr
|
||||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
|
||||||
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
return None
|
||||||
if call_node.args is None: continue
|
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
||||||
args_list: list[ast.AST*]* = call_node.args
|
"""Return the first positional string constant of a t.attr.xxx(...) call."""
|
||||||
an: sizet = len(args_list)
|
if node is None or node.kind() != ast.ASTKind.Call: return None
|
||||||
for ai in range(an):
|
cl: ast.Call* = ast.Call*(node)
|
||||||
arg_node: ast.AST* = args_list.get(ai)
|
if cl.args is None or len(cl.args) == 0: return None
|
||||||
if arg_node is None: continue
|
a0: ast.AST* = cl.args.get(0)
|
||||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
|
||||||
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
|
cn: ast.Constant* = ast.Constant*(a0)
|
||||||
sec_val: str = self._get_attr_call_arg_str(arg_node)
|
if cn.const_kind != ast.CONST_STR: return None
|
||||||
if sec_val is None: continue
|
return cn.str_val
|
||||||
sec_buf: str = pool.alloc(128)
|
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||||
if sec_buf is None: return None
|
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
|
||||||
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
|
if attrs_buf is None or attr_name is None: return 0
|
||||||
return sec_buf
|
|
||||||
return None
|
# First determine the mapped LLVM attr name
|
||||||
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
llvm_name: str = None
|
||||||
"""从 AST 节点提取属性名
|
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
||||||
|
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
||||||
支持:
|
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
|
||||||
- Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline"
|
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
|
||||||
- Attribute: t.attr.packed -> "packed"
|
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
|
||||||
- Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind"
|
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
|
||||||
"""
|
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
|
||||||
if node is None: return None
|
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
|
||||||
k: int = node.kind()
|
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
||||||
|
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
||||||
# Call 节点: t.attr.always_inline()
|
else:
|
||||||
if k == ast.ASTKind.Call:
|
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
|
||||||
call: ast.Call* = ast.Call*(node)
|
return 0
|
||||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
|
||||||
at: ast.Attribute* = ast.Attribute*(call.func)
|
# Supported attr: append a space separator if the buffer is non-empty
|
||||||
return at.attr
|
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
|
||||||
|
self._append_str(attrs_buf, llvm_name, 256)
|
||||||
# Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind
|
return 1
|
||||||
if k == ast.ASTKind.Attribute:
|
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
|
||||||
at2: ast.Attribute* = ast.Attribute*(node)
|
"""Append a string to buf with a bounded write to avoid overflow."""
|
||||||
return at2.attr
|
if buf is None or s is None: return
|
||||||
|
# Defensive: VPC default-arg registration is buggy (missing max_len falls to 0);
|
||||||
return None
|
# fall back to 256 so the bounded-write check does not drop all attr text.
|
||||||
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
if max_len == 0: max_len = 256
|
||||||
"""取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))"""
|
cur_len: sizet = string.strlen(buf)
|
||||||
if node is None or node.kind() != ast.ASTKind.Call: return None
|
s_len: sizet = string.strlen(s)
|
||||||
cl: ast.Call* = ast.Call*(node)
|
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
|
||||||
if cl.args is None or len(cl.args) == 0: return None
|
string.strcpy(buf + cur_len, s)
|
||||||
a0: ast.AST* = cl.args.get(0)
|
# _mangle_name - prepend a SHA1 prefix to a name.
|
||||||
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
|
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
|
||||||
cn: ast.Constant* = ast.Constant*(a0)
|
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
||||||
if cn.const_kind != ast.CONST_STR: return None
|
"""Prepend the SHA1 prefix to a name (no main/export check)."""
|
||||||
return cn.str_val
|
if trans is None or name is None: return name
|
||||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
if trans.ModuleSha1 is None: return name
|
||||||
"""将属性名映射到 LLVM 属性名并追加到缓冲区
|
name_len: sizet = string.strlen(name)
|
||||||
|
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||||
返回 1 表示已追加,0 表示不支持该属性
|
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
|
||||||
"""
|
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||||
if attrs_buf is None or attr_name is None: return 0
|
sha1: str = trans.ModuleSha1
|
||||||
|
sha1_len: sizet = string.strlen(sha1)
|
||||||
# 先确定映射的 LLVM 属性名
|
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||||
llvm_name: str = None
|
if mangled is None: return name
|
||||||
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
string.strcpy(mangled, sha1)
|
||||||
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
mangled[sha1_len] = '.'
|
||||||
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
|
string.strcpy(mangled + sha1_len + 1, name)
|
||||||
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
|
return mangled
|
||||||
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
|
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
|
||||||
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
|
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
|
||||||
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
|
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||||
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
|
"""Prepend the given SHA1 to a name (no main/export check)."""
|
||||||
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
if sha1 is None or name is None: return name
|
||||||
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
name_len: sizet = string.strlen(name)
|
||||||
else:
|
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||||
# packed/aligned/section/visibility/weak 等不支持作为函数内联属性
|
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
|
||||||
return 0
|
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||||
|
sha1_len: sizet = string.strlen(sha1)
|
||||||
# 支持的属性:如果缓冲区非空,先追加空格分隔
|
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||||
if attrs_buf[0] != '\0':
|
if mangled is None: return name
|
||||||
self._append_str(attrs_buf, " ", 256)
|
string.strcpy(mangled, sha1)
|
||||||
self._append_str(attrs_buf, llvm_name, 256)
|
mangled[sha1_len] = '.'
|
||||||
return 1
|
string.strcpy(mangled + sha1_len + 1, name)
|
||||||
def _append_str(self, buf: str, s: str,
|
return mangled
|
||||||
max_len: sizet = 256) -> None:
|
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
|
||||||
"""将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)"""
|
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
|
||||||
if buf is None or s is None: return
|
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
|
||||||
# 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0),
|
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
|
||||||
# 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。
|
if trans is None or name is None: return name
|
||||||
if max_len == 0:
|
if has_export != 0: return name
|
||||||
max_len = 256
|
if string.strcmp(name, "main") == 0: return name
|
||||||
cur_len: sizet = string.strlen(buf)
|
return self._mangle_name(trans, name)
|
||||||
s_len: sizet = string.strlen(s)
|
# _body_has_raise - whether the body contains raise (non-StopIteration).
|
||||||
if cur_len + s_len + 1 > max_len: return # 无剩余容量:丢弃本次追加,避免越界写 存疑
|
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
|
||||||
string.strcpy(buf + cur_len, s)
|
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
||||||
# ============================================================
|
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
|
||||||
# _mangle_name - 为名称添加 SHA1 前缀
|
if fd is None or fd.children is None: return 0
|
||||||
#
|
body_f: list[ast.AST*]* = fd.children
|
||||||
# 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带
|
bn_f: sizet = len(body_f)
|
||||||
# SHA1 前缀(16 hex + '.'),则原样返回。
|
for bi_f in range(int(bn_f)):
|
||||||
# ============================================================
|
stmt_f: ast.AST* = body_f.get(bi_f)
|
||||||
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
if stmt_f is None: continue
|
||||||
"""为任意名称添加 SHA1 前缀(不做 main/export 检查)"""
|
if stmt_f.kind() != ast.ASTKind.Raise: continue
|
||||||
if trans is None or name is None: return name
|
rn_f: ast.Raise* = ast.Raise*(stmt_f)
|
||||||
if trans.ModuleSha1 is None: return name
|
if rn_f is None or rn_f.exc is None: continue
|
||||||
name_len: sizet = string.strlen(name)
|
ek_f: int = rn_f.exc.kind()
|
||||||
# 已带 SHA1 前缀("{16hex}.")则跳过
|
is_stop_f: int = 0
|
||||||
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`。旧判据是 `name_len > 17`,
|
if ek_f == ast.ASTKind.Name:
|
||||||
# 与真源的 `>= 17` 只差"前缀之后为空串"这一退化形态(无实际意义),一并对齐。
|
enm_f: ast.Name* = ast.Name*(rn_f.exc)
|
||||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||||
sha1: str = trans.ModuleSha1
|
elif ek_f == ast.ASTKind.Call:
|
||||||
sha1_len: sizet = string.strlen(sha1)
|
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
|
||||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
||||||
if mangled is None: return name
|
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
|
||||||
string.strcpy(mangled, sha1)
|
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||||
mangled[sha1_len] = '.'
|
if is_stop_f == 0: return 1
|
||||||
string.strcpy(mangled + sha1_len + 1, name)
|
return 0
|
||||||
return mangled
|
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||||
# ============================================================
|
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||||
# _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀
|
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||||
#
|
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||||
# 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling,
|
if decorators is None: return 0
|
||||||
# 而非当前模块的 SHA1。
|
rcr_dn: sizet = len(decorators)
|
||||||
# ============================================================
|
for rcr_i in range(int(rcr_dn)):
|
||||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||||
"""用指定 SHA1 为名称添加前缀(不做 main/export 检查)"""
|
if rcr_deco is None: continue
|
||||||
if sha1 is None or name is None: return name
|
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||||
name_len: sizet = string.strlen(name)
|
rcr_attr: ast.Attribute* = None
|
||||||
# 已带 SHA1 前缀("{16hex}.")则跳过
|
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||||
# [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`(口径对齐同 _mangle_name)。
|
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
|
||||||
sha1_len: sizet = string.strlen(sha1)
|
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
|
||||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
if rcr_attr is None or rcr_attr.attr is None: continue
|
||||||
if mangled is None: return name
|
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
||||||
string.strcpy(mangled, sha1)
|
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
|
||||||
mangled[sha1_len] = '.'
|
return 1
|
||||||
string.strcpy(mangled + sha1_len + 1, name)
|
return 0
|
||||||
return mangled
|
# Create LLVM function (simple version, declaration only)
|
||||||
# ============================================================
|
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||||
# _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查)
|
"""Create an LLVM function and add its parameters."""
|
||||||
#
|
if name is None or mod is None: return None
|
||||||
# 规则:
|
|
||||||
# - main 函数不加前缀(程序入口点)
|
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
|
||||||
# - has_export 非 0 时不加前缀(t.CExport 导出函数)
|
if func is None: return None
|
||||||
# - 已带 SHA1 前缀的不再加
|
|
||||||
# - ModuleSha1 为 None 时不加前缀
|
if args_node is not None:
|
||||||
# ============================================================
|
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||||
def _mangle_func_name(self, trans: HT.Translator*, name: str,
|
if ags.args is not None:
|
||||||
has_export: int) -> str:
|
alist: list[ast.AST*]* = ags.args
|
||||||
"""为函数名添加 SHA1 前缀(检查 main/export)"""
|
an: sizet = len(alist)
|
||||||
if trans is None or name is None: return name
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
if has_export != 0: return name
|
for ai in range(int(an)):
|
||||||
if string.strcmp(name, "main") == 0: return name
|
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||||
return self._mangle_name(trans, name)
|
if arg is not None and arg.arg is not None:
|
||||||
# ============================================================
|
# CVoid / void means no args: skip
|
||||||
# _body_has_raise - 函数体是否含 raise(非 StopIteration)
|
if arg.annotation is not None:
|
||||||
#
|
if arg.annotation.kind() == ast.ASTKind.Name:
|
||||||
# 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名
|
at_cn: ast.Name* = ast.Name*(arg.annotation)
|
||||||
# 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体
|
if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
|
||||||
# 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。
|
param_ty: mlirlite.LLVMType* = i32_ty
|
||||||
# ============================================================
|
if arg.annotation is not None:
|
||||||
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||||
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
|
if resolved is None:
|
||||||
if fd is None or fd.children is None:
|
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
|
||||||
return 0
|
# This fn lacks trans/imports context, so failure surfaces upstream.
|
||||||
body_f: list[ast.AST*]* = fd.children
|
self.Gen.TypeGenErr.fatal_error(arg, f"function '{name}' param '{arg.arg}' type annotation failed to resolve", "NameError")
|
||||||
bn_f: sizet = len(body_f)
|
return None
|
||||||
for bi_f in range(bn_f):
|
param_ty = resolved
|
||||||
stmt_f: ast.AST* = body_f.get(bi_f)
|
pname: str = pool.alloc(32)
|
||||||
if stmt_f is None: continue
|
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||||
if stmt_f.kind() != ast.ASTKind.Raise: continue
|
mlirlite.add_param(pool, func, param_ty, pname)
|
||||||
rn_f: rawptr[ast.Raise] = rawptr[ast.Raise](stmt_f)
|
|
||||||
if rn_f is None or rn_f.exc is None: continue
|
|
||||||
ek_f: int = rn_f.exc.kind()
|
|
||||||
is_stop_f: int = 0
|
|
||||||
if ek_f == ast.ASTKind.Name:
|
|
||||||
enm_f: ast.Name* = ast.Name*(rn_f.exc)
|
|
||||||
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0:
|
|
||||||
is_stop_f = 1
|
|
||||||
elif ek_f == ast.ASTKind.Call:
|
|
||||||
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
|
|
||||||
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
|
||||||
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
|
|
||||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0:
|
|
||||||
is_stop_f = 1
|
|
||||||
if is_stop_f == 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# RejectCReturnDecorator - @c.CReturn 装饰器已移除,遇到即编译期报错
|
|
||||||
#
|
|
||||||
# 多返回值唯一入口是返回注解 `-> tuple[ty1, ty2, ...]`(tuple → struct 通路
|
|
||||||
# 逐字保留)。任何装饰器形态出现 CReturn 一律 fail-fast,不静默忽略。
|
|
||||||
# 供 FuncGenDef(pre_scan 前向声明 / translate_function_def 定义两阶段)复用;
|
|
||||||
# Converter 为模块级函数,同报文另写一份(见 StubGen/Converter.vp)。
|
|
||||||
# 无 CReturn 返回 0。
|
|
||||||
# ============================================================
|
|
||||||
def RejectCReturnDecorator(self, node: ast.AST*,
|
|
||||||
decorators: list[ast.AST*]*) -> int:
|
|
||||||
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
|
|
||||||
if decorators is None: return 0
|
|
||||||
rcr_dn: sizet = len(decorators)
|
|
||||||
for rcr_i in range(rcr_dn):
|
|
||||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
|
||||||
if rcr_deco is None: continue
|
|
||||||
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
|
|
||||||
rcr_attr: ast.Attribute* = None
|
|
||||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
|
||||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
|
||||||
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute:
|
|
||||||
rcr_attr = ast.Attribute*(rcr_call.func)
|
|
||||||
elif rcr_deco.kind() == ast.ASTKind.Attribute:
|
|
||||||
rcr_attr = ast.Attribute*(rcr_deco)
|
|
||||||
if rcr_attr is None or rcr_attr.attr is None: continue
|
|
||||||
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
|
||||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct 返回)", "TypeError")
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
# ============================================================
|
|
||||||
# 创建 LLVM 函数(简单版本,仅声明)
|
|
||||||
# ============================================================
|
|
||||||
def create_function(self, pool: memhub.MemBuddy*,
|
|
||||||
mod: llvmlite.LLVMModule*,
|
|
||||||
name: str,
|
|
||||||
args_node: ast.AST*,
|
|
||||||
ret_ty: llvmlite.LLVMType*) -> llvmlite.Function*:
|
|
||||||
"""创建 LLVM 函数并添加参数"""
|
|
||||||
if name is None or mod is None: return None
|
|
||||||
|
|
||||||
func: llvmlite.Function* = llvmlite.create_function(pool, mod, name, ret_ty)
|
|
||||||
if func is None: return None
|
|
||||||
|
|
||||||
if args_node is not None:
|
|
||||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
|
||||||
if ags.args is not None:
|
|
||||||
alist: list[ast.AST*]* = ags.args
|
|
||||||
an: sizet = len(alist)
|
|
||||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
|
||||||
for ai in range(an):
|
|
||||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
|
||||||
if arg is not None and arg.arg is not None:
|
|
||||||
# t\.CVoid 表示空参:跳过
|
|
||||||
if arg.annotation is not None:
|
|
||||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
|
||||||
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
|
|
||||||
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue
|
|
||||||
param_ty: llvmlite.LLVMType* = i32_ty
|
|
||||||
if arg.annotation is not None:
|
|
||||||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
|
||||||
if resolved is None:
|
|
||||||
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32。
|
|
||||||
# 本函数不传 trans/imports 上下文,解析失败即调用方未补齐
|
|
||||||
# 上下文(暴露上游,不由本函数兜底)。
|
|
||||||
self.Gen.TypeGenErr.fatal_error(arg, f"函数 '{name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
|
|
||||||
return None
|
|
||||||
param_ty = resolved
|
|
||||||
pname: str = pool.alloc(32)
|
|
||||||
if pname is not None:
|
|
||||||
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
|
||||||
llvmlite.add_param(pool, func, param_ty, pname)
|
|
||||||
|
|
||||||
return func
|
return func
|
||||||
+1188
-1386
File diff suppressed because it is too large.
Load diff
+1247
-1445
File diff suppressed because it is too large.
Load diff
+813
-483
File diff suppressed because it is too large.
Load diff
+226
-328
@@ -1,434 +1,332 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
|
# TypeSpec: unified type descriptor (Debugger/_Type.md section 6).
|
||||||
# 别名用 TSpec:模块内类名同为 TypeSpec,别名同名会被解析器误判为类静态方法。
|
# Alias TSpec because the in-module class name is also TypeSpec (same name would be a static method).
|
||||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# GenTypes - Gen 侧共享数据类型与常量(去除 Handles 层迁移目标)
|
# GenTypes - Gen-side shared data types and constants (migrated out of the Handles layer)
|
||||||
#
|
|
||||||
# 原分布于 Handles 目录的数据类与常量集中于此,供 Gen 子对象、
|
|
||||||
# 管线与保留实体文件引用。删除 Handles 目录后统一经本模块访问。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 类编译期元数据(原 HandlesType.CLASS_META_*)
|
# Class compile-time metadata (formerly HandlesType.CLASS_META_*)
|
||||||
# ============================================================
|
define CLASS_META_PROVIDES = 1
|
||||||
CLASS_META_PROVIDES: t.CDefine = 1
|
define CLASS_META_REQUIRES = 2
|
||||||
CLASS_META_REQUIRES: t.CDefine = 2
|
define CLASS_META_REQUIRE_MUST = 3
|
||||||
CLASS_META_REQUIRE_MUST: t.CDefine = 3
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 属性/静态/类方法元数据(原 HandlesClassDef.META_*)
|
# Attribute/static/class-method metadata (formerly HandlesClassDef.META_*)
|
||||||
# ============================================================
|
define META_STATIC = 1
|
||||||
META_STATIC: t.CDefine = 1
|
define META_CLASS = 2
|
||||||
META_CLASS: t.CDefine = 2
|
define META_PROP_GETTER = 4
|
||||||
META_PROP_GETTER: t.CDefine = 4
|
define META_PROP_SETTER = 8
|
||||||
META_PROP_SETTER: t.CDefine = 8
|
define META_PROP_DELETER = 16
|
||||||
META_PROP_DELETER: t.CDefine = 16
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 结构体容量(原 HandlesStruct.FIELD_MAX/FIELD_NAME_MAX/PARENT_MAX/MRO_MAX)
|
# Struct capacities (formerly HandlesStruct.FIELD_MAX/...)
|
||||||
# ============================================================
|
define FIELD_MAX = 256
|
||||||
FIELD_MAX: t.CDefine = 256
|
define FIELD_NAME_MAX = 64
|
||||||
FIELD_NAME_MAX: t.CDefine = 64
|
define PARENT_MAX = 64
|
||||||
PARENT_MAX: t.CDefine = 64
|
define MRO_MAX = 64
|
||||||
MRO_MAX: t.CDefine = 64
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# for 迭代序列种类(原 HandlesFor.SEQ_KIND_*)
|
# for-iteration sequence kinds (formerly HandlesFor.SEQ_KIND_*)
|
||||||
# ============================================================
|
define SEQ_KIND_RANGE = 1
|
||||||
SEQ_KIND_RANGE: t.CDefine = 1
|
define SEQ_KIND_STR = 2
|
||||||
SEQ_KIND_STR: t.CDefine = 2
|
define SEQ_KIND_LIST = 3
|
||||||
SEQ_KIND_LIST: t.CDefine = 3
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# TraceFrame - 仿 Python Traceback 调用栈帧(原 HandlesType.TraceFrame)
|
# TraceFrame - Python-Traceback-like call frame (formerly HandlesType.TraceFrame)
|
||||||
# ============================================================
|
|
||||||
class TraceFrame:
|
class TraceFrame:
|
||||||
"""仿 Python Traceback 调用栈帧(push_trace/pop_trace 使用)"""
|
"""Python-Traceback-like call frame used by push_trace/pop_trace"""
|
||||||
file_name: str
|
file_name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
col_start: int
|
col_start: int
|
||||||
col_end: int
|
col_end: int
|
||||||
|
|
||||||
|
|
||||||
|
# ---- 生命期标签(LifeTag):有序,越小越"短命" ----
|
||||||
|
# 逃逸检查只在 LIFE_FRAME 上触发;控制流汇合取更短者(Meet = min)。
|
||||||
|
# 详见 Debugger/ownership-and-new.md §11。
|
||||||
|
define LIFE_VALUE = 0 # 值语义(标量/聚合按值拷贝),不参与逃逸
|
||||||
|
define LIFE_FRAME = 1 # 本帧栈存储:局部变量地址、数组退让、形参副本地址
|
||||||
|
define LIFE_HEAP = 2 # 堆,hlife(释放靠手动/区域)
|
||||||
|
define LIFE_STATIC = 3 # 模块级全局 / 字符串常量 / 函数地址
|
||||||
|
define LIFE_UNKNOWN = 4 # 未知(extern 返回等),保守放行
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# VarEntry - 变量表条目(原 HandlesVar.VarEntry)
|
# VarEntry - variable table entry (formerly HandlesVar.VarEntry)
|
||||||
# ============================================================
|
|
||||||
class VarEntry:
|
class VarEntry:
|
||||||
"""变量表条目"""
|
"""Variable table entry"""
|
||||||
Name: str
|
Name: str
|
||||||
Alloca: llvmlite.Value*
|
Alloca: mlirlite.Value*
|
||||||
Used: int
|
Used: int
|
||||||
# TypeSpec(S3 起为**唯一类型承载**):由 add_var 用 SpecFromLLVMType 从
|
# TypeSpec (sole type carrier since S3): filled by add_var via SpecFromLLVMType from
|
||||||
# alloca.Ty 的 pointee 填充(PermPool 分配 ⇒ 跨文件安全),注解侧再由
|
# alloca.Ty's pointee (PermPool, cross-file safe); annotation side augments sign/source/elem.
|
||||||
# set_var_annot_class_name 经 SpecWithSign / SpecWithSource 补整数符号性与
|
|
||||||
# 注解来源名(Spec.SourceName,原 AnnotClassName 字段已并入),由
|
|
||||||
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
|
|
||||||
# IsPtrElement 字段已并入 Spec.ElemBits)。
|
|
||||||
Spec: TSpec.TypeSpec*
|
Spec: TSpec.TypeSpec*
|
||||||
# 1=该变量的 load/store 带 LLVM volatile 限定符(注解联合含 t.CVolatile)。
|
# 1=load/store of this variable carries LLVM volatile (annotation union has t.CVolatile).
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
# Appended at the end so existing field offsets are unchanged.
|
||||||
IsVolatile: int
|
IsVolatile: int
|
||||||
# 1=该变量是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
# 1=this variable is rawptr[t.CVolatile | T] (volatile pointee, like C's volatile T*):
|
||||||
# volatile T*):解引用(p[i] / p.field / c.Deref(p))的 load/store 带 LLVM
|
# its dereferences carry volatile. Unlike IsVolatile, which limits the pointer variable itself.
|
||||||
# volatile,禁止优化器重排序/消除对所指对象的每次访问。注意与 IsVolatile
|
|
||||||
# 的区别:IsVolatile 限定"指针变量自身"的读写,本字段限定"指针所指物"。
|
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
|
||||||
PointeeVolatile: int
|
PointeeVolatile: int
|
||||||
# 1=该变量是 **const 对象**(注解含 t.CConst 且声明类型**不是指针**):
|
# 1=this variable is a const object (annotation has t.CConst and the type is not a
|
||||||
# 整个作用域内禁止对该变量名赋值 / 增量赋值 / 元素赋值(fail-fast)。
|
# pointer): assignment to it anywhere in scope fails fast. Pointer CConst only limits the pointee.
|
||||||
# 与 TPC 的 Gen.var_const_flags / global_const_flags 同判据、同报文,见
|
|
||||||
# Debugger/type-descriptors.md。指针类型(如 `p: t.CConst | T | t.CPtr`)的
|
|
||||||
# CConst 只约束 pointee,不使指针变量自身不可写,故不置 1。
|
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
|
|
||||||
IsConst: int
|
IsConst: int
|
||||||
|
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||||
|
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||||
|
Life: int
|
||||||
|
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
|
||||||
|
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
|
||||||
|
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
|
||||||
|
ByteOrder: int
|
||||||
|
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
|
||||||
|
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
|
||||||
|
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Scope - 作用域节点(原 HandlesVar.Scope)
|
# Scope - scope node (formerly HandlesVar.Scope)
|
||||||
# ============================================================
|
|
||||||
novirtual class Scope:
|
novirtual class Scope:
|
||||||
"""作用域节点"""
|
"""Scope node"""
|
||||||
Parent: Scope | t.CPtr
|
Parent: Scope*
|
||||||
Vars: VarEntry | t.CPtr
|
Vars: VarEntry*
|
||||||
VarCount: int
|
VarCount: int
|
||||||
Kind: int
|
Kind: int
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# SymbolTable - 嵌套作用域符号表(原 HandlesVar.SymbolTable)
|
# SymbolTable - nested-scope symbol table (formerly HandlesVar.SymbolTable)
|
||||||
# ============================================================
|
|
||||||
novirtual class SymbolTable:
|
novirtual class SymbolTable:
|
||||||
"""嵌套作用域符号表"""
|
"""Nested-scope symbol table"""
|
||||||
Pool: memhub.MemBuddy*
|
Pool: memhub.MemBuddy*
|
||||||
Root: Scope | t.CPtr
|
Root: Scope*
|
||||||
Current: Scope | t.CPtr
|
Current: Scope*
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# FieldEntry - 字段信息条目(原 HandlesStruct.FieldEntry)
|
# FieldEntry - field info entry (formerly HandlesStruct.FieldEntry)
|
||||||
# ============================================================
|
|
||||||
class FieldEntry:
|
class FieldEntry:
|
||||||
Name: str # 字段名(字符串)
|
Name: str # Field name (string)
|
||||||
Index: int # 字段在结构体中的索引
|
Index: int # Field index within the struct
|
||||||
Ty: llvmlite.LLVMType* # 字段的 LLVM 类型
|
Ty: mlirlite.LLVMType* # Field LLVM type
|
||||||
DefaultVal: ast.AST* # 默认值 AST 节点(None=无默认值)
|
DefaultVal: ast.AST* # Default-value AST node (None=no default)
|
||||||
# TypeSpec(S3 起为**唯一类型承载**):由 add_field 用 SpecFromLLVMType 从
|
# TypeSpec (sole type carrier since S3): filled by add_field via SpecFromLLVMType from
|
||||||
# field_ty 填充(PermPool 分配 ⇒ 跨 1b reset 安全,无需 DeepCopyType),
|
# field_ty (PermPool, safe across 1b reset); annotation side adds sign/source.
|
||||||
# 注解侧再经 SpecWithSign / SpecWithSource 补符号性与注解来源名
|
|
||||||
# (Spec.SourceName,原 AnnotClassName 字段已并入)。
|
|
||||||
Spec: TSpec.TypeSpec*
|
Spec: TSpec.TypeSpec*
|
||||||
# 1=该字段经对象访问的 load/store 带 LLVM volatile 限定符
|
# 1=load/store of this field via an object carries LLVM volatile (field annotation union
|
||||||
# (字段注解联合含 t.CVolatile,如 Spinlock.locked: t.CVolatile | t.CInt)。
|
# has t.CVolatile, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
|
||||||
IsVolatile: int
|
IsVolatile: int
|
||||||
# 1=该字段是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
# 1=this field is rawptr[t.CVolatile | T] (volatile pointee): dereferences via it
|
||||||
# volatile T*):经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
|
# (self.ptr[i] / self.ptr.field) carry volatile. Same distinction as VarEntry.PointeeVolatile.
|
||||||
# 带 LLVM volatile。与 IsVolatile 的区别同 VarEntry.PointeeVolatile。
|
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
|
||||||
PointeeVolatile: int
|
PointeeVolatile: int
|
||||||
# 1=该字段是 **const 对象**(注解含 t.CConst 且字段类型**不是指针**):
|
# 1=this field is a const object (annotation has t.CConst and the type is not a pointer):
|
||||||
# 经对象给该字段赋值(obj.f = ... / self.f = ...)即 fail-fast,与
|
# assigning it via an object fails fast, same as VarEntry.IsConst.
|
||||||
# VarEntry.IsConst 同判据、同报文(TPC 侧对应 Gen.field_const_flags)。
|
|
||||||
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
|
|
||||||
IsConst: int
|
IsConst: int
|
||||||
|
# Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation
|
||||||
|
# union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets
|
||||||
|
# are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only).
|
||||||
|
ByteOrder: int
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# StructEntry - 结构体注册条目(原 HandlesStruct.StructEntry)
|
# StructEntry - struct registration entry (formerly HandlesStruct.StructEntry)
|
||||||
# ============================================================
|
|
||||||
novirtual class StructEntry:
|
novirtual class StructEntry:
|
||||||
Name: str # 类名
|
Name: str # Class name
|
||||||
Ty: llvmlite.LLVMType* # LLVM StructType
|
Ty: mlirlite.LLVMType* # LLVM StructType
|
||||||
FieldCount: int # 字段数量
|
FieldCount: int # Field count
|
||||||
Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位)
|
Fields: FieldEntry* # Field array (FIELD_MAX slots)
|
||||||
IsUnion: int # 1=联合体 / 0=普通结构体
|
IsUnion: int # 1=union / 0=normal struct
|
||||||
IsPacked: int # 1=@c.Attribute(t.attr.packed) 打包结构体(无字段填充)
|
IsPacked: int # 1=@c.Attribute(t.attr.packed) packed struct (no padding)
|
||||||
Align: int # @c.Attribute(t.attr.aligned(N)) 对齐字节数(0=默认,如 fpu_context=16)
|
Align: int # @c.Attribute(t.attr.aligned(N)) alignment in bytes (0=default)
|
||||||
IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体
|
IsOOP: int # 1=OOP struct (has methods) / 0=plain memory struct
|
||||||
HasInit: int # 1=有__init__方法 / 0=无
|
HasInit: int # 1=has __init__ / 0=no
|
||||||
HasNew: int # 1=有__new__方法 / 0=无
|
HasNew: int # 1=has __new__ / 0=no
|
||||||
HasVTable: int # 1=有虚表 / 0=无虚表
|
HasVTable: int # 1=has vtable / 0=no vtable
|
||||||
IsNoVTable: int # 1=明确标记 @t.NoVTable / 0=未标记
|
IsNoVTable: int # 1=explicitly @t.NoVTable / 0=not marked
|
||||||
ParentName: str # 父类名(None=无父类,多继承时为第一个父类,兼容旧调用)
|
ParentName: str # Parent name (None=none; first parent for multiple inheritance)
|
||||||
ParentNames: sizet* # 多继承父类名数组(每元素 str 指针,ParentCount 个;stdlib 深拷贝)
|
ParentNames: sizet* # Multi-inheritance parent name array (str pointers; stdlib-deep-copied)
|
||||||
ParentCount: int # 父类数量(0=无父类,1=单继承,>1=多继承)
|
ParentCount: int # Parent count (0=none, 1=single, >1=multiple)
|
||||||
VTableMethodCount: int # 虚表中的方法数量
|
VTableMethodCount: int # Number of methods in the vtable
|
||||||
VTableMethods: str # 虚表方法名数组(每个方法名 str,VTableMethodCount 个)
|
VTableMethods: str # Vtable method name array (VTableMethodCount str)
|
||||||
ModuleSha1: str # 定义该类的模块 SHA1(None=无 SHA1)
|
ModuleSha1: str # Module SHA1 defining this class (None=no SHA1)
|
||||||
MethodsEmitted: int # 1=方法体已翻译 / 0=未译(Phase 1a mark_only 未译,Phase B 据此补译)
|
MethodsEmitted: int # 1=method bodies translated / 0=not (Phase 1a mark_only; Phase B fills in)
|
||||||
SpecTriggerSha1: str # 泛型特化类的触发文件 SHA1(None=非特化类)。特化类 ModuleSha1=模板
|
SpecTriggerSha1: str # Trigger-file SHA1 for a generic specialization (None=not specialized).
|
||||||
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
|
# ModuleSha1 is the template sha1; reset_module_methods_emitted resets
|
||||||
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
|
# MethodsEmitted by trigger sha1 to avoid leftover 1 / undefined symbol.
|
||||||
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
|
IsForwardDecl: int # 1=forward-declaration placeholder (Ty is opaque named struct, FieldCount=0).
|
||||||
IsForwardDecl: int # 1=前向声明占位条目(Ty=不透明具名结构体 {sha}.Class,FieldCount=0;
|
# The definition side later fills the same Ty in place and clears this flag.
|
||||||
# 定义侧随后**原位填充**同一 Ty 对象并清零本标记)。
|
|
||||||
# 语义对齐 TPC:未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充。
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# EnumMember - 枚举成员(原 HandlesEnum.EnumMember)
|
# EnumMember - enum member (formerly HandlesEnum.EnumMember)
|
||||||
# ============================================================
|
|
||||||
class EnumMember:
|
class EnumMember:
|
||||||
Name: str # 成员名(字符串)
|
Name: str # Member name (string)
|
||||||
Value: int64 # 成员的整数值
|
Value: int64 # Member integer value
|
||||||
Ty: llvmlite.LLVMType* # 成员的 LLVM 类型(用于混用类型场景)
|
Ty: mlirlite.LLVMType* # Member LLVM type (for mixed-type cases)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# EnumEntry - 枚举类型条目(原 HandlesEnum.EnumEntry)
|
# EnumEntry - enum type entry (formerly HandlesEnum.EnumEntry)
|
||||||
# ============================================================
|
|
||||||
novirtual class EnumEntry:
|
novirtual class EnumEntry:
|
||||||
Name: str # 枚举类名
|
Name: str # Enum class name
|
||||||
BaseTy: llvmlite.LLVMType* # 基准类型(所有成员类型中最大的)
|
BaseTy: mlirlite.LLVMType* # Base type (largest among member types)
|
||||||
MemberCount: int # 成员数量
|
MemberCount: int # Member count
|
||||||
Members: EnumMember | t.CPtr # 成员数组(ENUM_MEMBER_MAX 个槽位)
|
Members: EnumMember* # Member array (ENUM_MEMBER_MAX slots)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# AsmOperand - 内联汇编操作数条目(原 HandlesExprCall.AsmOperand)
|
# AsmOperand - inline-asm operand entry (formerly HandlesExprCall.AsmOperand)
|
||||||
# ============================================================
|
|
||||||
class AsmOperand:
|
class AsmOperand:
|
||||||
"""内联汇编操作数条目"""
|
"""Inline-asm operand entry"""
|
||||||
Value: llvmlite.Value* # 输入值或输出 alloca
|
Value: mlirlite.Value* # Input value or output alloca
|
||||||
Constraint: str # 约束字符串 (如 "r", "=r")
|
Constraint: str # Constraint string (e.g. "r", "=r")
|
||||||
IsOutput: int # 1=输出, 0=输入
|
IsOutput: int # 1=output, 0=input
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# FuncEntry - 函数表条目(原 HandlesExprCall.FuncEntry)
|
# FuncEntry - function table entry (formerly HandlesExprCall.FuncEntry)
|
||||||
# ============================================================
|
|
||||||
novirtual class FuncEntry:
|
novirtual class FuncEntry:
|
||||||
"""函数表条目"""
|
"""Function table entry"""
|
||||||
Name: str
|
Name: str
|
||||||
Func: llvmlite.Function*
|
Func: mlirlite.Function*
|
||||||
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr]),None=无默认值
|
Defaults: t.CPtr # Defaults list node pointer (list[AST*]); None=no defaults
|
||||||
DefaultCount: int # 默认值数量
|
DefaultCount: int # Default count
|
||||||
ParamCount: int # 形参数量(不含 self)
|
ParamCount: int # Param count (excluding self)
|
||||||
# TypeSpec(S3 条目双写):Kind=TS_FUNC 的**整签名**(Ret/Params/ParamCount/IsVarArg
|
# TypeSpec (dual-write since S3): the full signature of Kind=TS_FUNC (Ret/Params/...)
|
||||||
# 都在其内),由 update_global_func_spec 在形参 add_param 完成后用 NewTypeFunc 构造。
|
# built by update_global_func_spec after add_param; PermPool-resident, safe across 1b reset.
|
||||||
# PermPool 驻留 ⇒ 跨 1b reset 安全。单字段即可完整表达签名,与 VarEntry/FieldEntry
|
|
||||||
# 的 Spec 命名一致,避免参数数组重复。
|
|
||||||
Spec: TSpec.TypeSpec*
|
Spec: TSpec.TypeSpec*
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# FuncDefaultsEntry - 默认参数表条目(原 HandlesExprCall.FuncDefaultsEntry)
|
# FuncDefaultsEntry - default-argument entry (formerly HandlesExprCall.FuncDefaultsEntry)
|
||||||
# ============================================================
|
|
||||||
novirtual class FuncDefaultsEntry:
|
novirtual class FuncDefaultsEntry:
|
||||||
"""默认参数表条目"""
|
"""Default-argument table entry"""
|
||||||
Name: str
|
Name: str
|
||||||
ModuleSha1: str # 定义该函数的模块 SHA1(限定名去重,避免跨模块同名串味)
|
ModuleSha1: str # Module SHA1 defining this function (dedupes qualified names)
|
||||||
Defaults: t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
|
Defaults: t.CPtr # Defaults list node pointer (list[AST*]) (in pool, may be reset)
|
||||||
DefaultCount: int
|
DefaultCount: int
|
||||||
ParamCount: int # 形参数量(不含 self)
|
ParamCount: int # Param count (excluding self)
|
||||||
# 常量快照(stdlib 分配,跨文件永久存活):
|
# Constant snapshot (stdlib-allocated, permanent): extracted at registration so
|
||||||
# 注册时提取每个默认值 Constant 的值,跨模块调用时优先用快照重建常量 IR,
|
# cross-module calls rebuild constant IR without touching a reset AST.
|
||||||
# 避免读取已被 1b reset_to(mark1) 回收的 pool 内 AST。
|
ConstKinds: int* # const_kind array (DefaultCount i32)
|
||||||
ConstKinds: int | t.CPtr # const_kind 数组(DefaultCount 个 i32)
|
ConstInts: int64* # int_val array (DefaultCount i64)
|
||||||
ConstInts: int64 | t.CPtr # int_val 数组(DefaultCount 个 i64)
|
ConstStrs: sizet* # str_val pointer array (DefaultCount i8*)
|
||||||
ConstStrs: sizet* # str_val 指针数组(DefaultCount 个 i8*)
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# REPL 会话(M2/M3):ctx 缓冲区布局 + 会话表条目 + 结果区头
|
# REPL session (M2/M3): ctx buffer layout, session table entries, and result header.
|
||||||
#
|
define REPL_CTX_MARKER = 1397571916 # Host-side object marker (self-comparison; not a ctx header field)
|
||||||
# ctx 缓冲区由宿主(lib/core/Repl.vp)在 PermPool 一次性分配,片段函数以首形参
|
define REPL_CTX_MAGIC = 0x5245504C # 'REPL' (ctx header +0, little-endian bytes R E P L)
|
||||||
# 接收(注解 bytes ⇒ i8*),同一进程内宿主与翻译器共享同一块内存。
|
define REPL_CTX_VERSION = 2 # ctx header +4 (1 -> 2: function pointer slot area appended at the tail)
|
||||||
#
|
define REPL_SLOT_SIZE = 16 # Bytes per session variable (stride)
|
||||||
# 布局(用户拍板子约定;字节全小端,无符号/有符号按位模式):
|
define REPL_SLOT_BASE = 64 # Slot area start = fixed header bytes (after the +40..63 reserved area)
|
||||||
#
|
define REPL_CTX_PARAM = "__repl_ctx__" # Wrapper-template ctx parameter name (same source as Repl.vp)
|
||||||
# 偏移 0 magic(u32 = 'REPL' = 0x5245504C) 4 version(u32 = 2)
|
define REPL_VAR_CAP = 64 # Max session variables (var area = 64 + 64*16 = 1088 bytes)
|
||||||
# 偏移 8 result_kind(i32) 12 保留(0)
|
# Session function pointer slot area (M3): starts right after the var area, 16B stride (low 8 bytes hold the address)
|
||||||
# 偏移 16 result_a(u64) 24 result_b(u64)
|
define REPL_FUNC_BASE = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
|
||||||
# 偏移 32 var_count(i32) 36 last_frag(i32)
|
define REPL_FUNC_SLOT_SIZE = 16 # Bytes per session function (stride)
|
||||||
# 偏移 40..63 保留(0)
|
define REPL_FUNC_CAP = 32 # Max session functions (func area = 32*16 = 512 bytes)
|
||||||
# 偏移 64 + i*16 第 i 个会话变量的槽(步长 16 字节),i < REPL_VAR_CAP
|
# Total ctx bytes = 64 + 64*16 + 32*16 = 1600 (host Repl._REPL_CTX_SIZE must match)
|
||||||
# 偏移 1088 + j*16 第 j 个会话函数的指针槽(步长 16 字节),j < REPL_FUNC_CAP
|
define REPL_CTX_SIZE = 1600
|
||||||
#
|
# Max session function params (fixed-size signature token array; overflow errors at registration)
|
||||||
# 版本与向后兼容(version 1 → 2,**纯尾部追加**):
|
define REPL_FUNC_PARAM_MAX = 8
|
||||||
# v1 = 头 64B + 变量槽区 1024B(共 1088B);v2 在 1088 之后追加函数指针槽区
|
# Fragment entry function name prefix (Repl.vp generates "__repl_frag_%d"); distinguishes
|
||||||
# (REPL_FUNC_CAP*REPL_FUNC_SLOT_SIZE)。前 1088 字节的字段偏移/宽度/语义逐字
|
# the fragment entry function (writes session vars) from the hoisted session function body.
|
||||||
# 未变 ⇒ 按 v1 生成的旧片段 DLL 与 v2 宿主混用仍然正确(旧片段只读写
|
define REPL_ENTRY_PREFIX = "__repl_frag_"
|
||||||
# < 1088 的字节);新片段读函数指针槽时宿主必为 v2 ⇒ 无版本协商需求。
|
# Session module SHA1 (M4): fragments use this stable value so type names are byte-identical
|
||||||
# 头 +4 写 2 仅作"含函数指针槽区"的显式标记(片段侧不校验该字段)。
|
# across fragments, which is the key for classes defined earlier to be visible later.
|
||||||
#
|
define REPL_SESSION_SHA1 = "71f67b7cb405f0e4"
|
||||||
# result_kind:
|
|
||||||
# 0 = none 本片段无结果(声明类片段)
|
|
||||||
# 1 = int/uint result_a = 值(补码位模式,宿主按有符号 64 位回显)
|
|
||||||
# 2 = float64 result_a = IEEE754 double 位模式(宿主重解释后回显)
|
|
||||||
# 3 = str/bytes result_a = 指针;result_b = 长度(0 = 以 NUL 结尾,宿主按
|
|
||||||
# C 字符串处理——片段侧不额外调 strlen,故长度未知时写 0)
|
|
||||||
# 4 = struct/其它 result_a = 值地址(结构体值装箱后的盒子地址 / 非字符串指针)
|
|
||||||
#
|
|
||||||
# 槽内存放形态(ReplVarEntry.Kind):
|
|
||||||
# SCALAR — 标量/指针(≤8 字节)的值直接存槽;
|
|
||||||
# BOX — 结构体值:片段侧 malloc 盒子(值存盒内),槽内存盒子指针。
|
|
||||||
# 槽步长 16 字节(结构体/字符串统一走指针 ⇒ 宿主无需知道类型);负载一律
|
|
||||||
# ≤8 字节且 8 字节对齐即合法(MemBuddy 用户指针实测 8 字节对齐)。
|
|
||||||
# 槽数上限 REPL_VAR_CAP ⇒ 变量区尺寸 = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE。
|
|
||||||
#
|
|
||||||
# 函数指针槽(M3):槽内是会话函数在**定义片段 DLL** 里的入口地址(void*,8 字节;
|
|
||||||
# DLL 常驻不卸载 ⇒ 地址在会话期内有效)。宿主在片段 DLL 加载成功后
|
|
||||||
# GetProcAddress(裸名) 取得地址并写入槽;后续片段的调用点从槽 load 地址后
|
|
||||||
# 间接调用 ⇒ 函数代码只有一份(在定义片段里),调用点不复制代码。
|
|
||||||
# ============================================================
|
|
||||||
REPL_CTX_MARKER: t.CDefine = 1397571916 # 宿主侧对象标记(自比对;非 ctx 头字段)
|
|
||||||
REPL_CTX_MAGIC: t.CDefine = 0x5245504C # 'REPL'(ctx 头 +0,小端字节序为 R E P L)
|
|
||||||
REPL_CTX_VERSION: t.CDefine = 2 # ctx 头 +4(1 → 2:尾部追加函数指针槽区)
|
|
||||||
REPL_SLOT_SIZE: t.CDefine = 16 # 每个会话变量占用字节数(步长)
|
|
||||||
REPL_SLOT_BASE: t.CDefine = 64 # 槽区起点 = 固定头字节数(= +40..63 保留之后)
|
|
||||||
REPL_CTX_PARAM: t.CDefine = "__repl_ctx__" # 包裹模板里 ctx 形参名(与 Repl.vp 同源)
|
|
||||||
REPL_VAR_CAP: t.CDefine = 64 # 会话变量上限(变量区 = 64 + 64*16 = 1088 字节)
|
|
||||||
# 会话函数指针槽区(M3):起点紧跟变量区,步长与变量槽一致(16B,低 8 字节存地址)
|
|
||||||
REPL_FUNC_BASE: t.CDefine = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
|
|
||||||
REPL_FUNC_SLOT_SIZE: t.CDefine = 16 # 每个会话函数占用字节数(步长)
|
|
||||||
REPL_FUNC_CAP: t.CDefine = 32 # 会话函数上限(函数区 = 32*16 = 512 字节)
|
|
||||||
# ctx 总字节数 = 64 + 64*16 + 32*16 = 1600(宿主 Repl._REPL_CTX_SIZE 必须一致)
|
|
||||||
REPL_CTX_SIZE: t.CDefine = 1600
|
|
||||||
# 会话函数的形参上限(签名 token 数组定长;超出 ⇒ 注册期明确报错)
|
|
||||||
REPL_FUNC_PARAM_MAX: t.CDefine = 8
|
|
||||||
# 片段入口函数名前缀(Repl.vp 生成 "__repl_frag_%d";用于区分"片段入口函数"与
|
|
||||||
# 被提升的会话函数本体——前者是会话变量写入的作用域,后者只读会话槽)
|
|
||||||
REPL_ENTRY_PREFIX: t.CDefine = "__repl_frag_"
|
|
||||||
# 会话模块 SHA1(M4):片段翻译器的 ModuleSha1 取此**稳定**值 ⇒ 片段内定义的类型
|
|
||||||
# 名形如 "<16位hex>.<Type>",且**跨片段逐字一致**——这是"先前片段定义的类在后续
|
|
||||||
# 片段可见/可用"的基础(类型名是跨片段查找的唯一键)。
|
|
||||||
# 派生:sha1("TransPyV-REPL-Session").hexdigest()[:16]
|
|
||||||
# 得 71f67b7cb405f0e4(一次性计算,见 repl-design.md §M4 的可复算命令)。
|
|
||||||
# 形态:16 位小写 hex 与 includes 模块 SHA1 同形 ⇒ 被既有判定
|
|
||||||
# GenApi.is_sha1_hex16 / StructGen.find_struct 的"模块段即 SHA1"分支
|
|
||||||
# 直接当作模块限定名处理(无需新增解析规则)。
|
|
||||||
# 反例(为什么不给每个片段各设一个 sha1):类型名会随片段漂移,先前片段注册的
|
|
||||||
# 结构体/方法在后续片段的 find_struct_by_module 全部落空。
|
|
||||||
# 与裸名约定的关系:`export def`(含片段入口与会话函数)走 has_export 分支取裸名
|
|
||||||
# (FuncGenDef.vp:482-485),**不受**该值影响;受影响的只有类/方法/模块级全局
|
|
||||||
# 等经 _mangle_name/_mangle_name_with_sha1 命名的符号。
|
|
||||||
REPL_SESSION_SHA1: t.CDefine = "71f67b7cb405f0e4"
|
|
||||||
|
|
||||||
# ctx 头字段偏移(与 ReplResult 字段顺序/宽度一一对应)
|
# ctx header field offsets (one-to-one with ReplResult field order/width)
|
||||||
REPL_RES_KIND_OFF: t.CDefine = 8 # i32
|
define REPL_RES_KIND_OFF = 8 # i32
|
||||||
REPL_RES_A_OFF: t.CDefine = 16 # u64
|
define REPL_RES_A_OFF = 16 # u64
|
||||||
REPL_RES_B_OFF: t.CDefine = 24 # u64
|
define REPL_RES_B_OFF = 24 # u64
|
||||||
REPL_RES_VARCOUNT_OFF: t.CDefine = 32 # i32
|
define REPL_RES_VARCOUNT_OFF = 32 # i32
|
||||||
REPL_RES_LASTFRAG_OFF: t.CDefine = 36 # i32
|
define REPL_RES_LASTFRAG_OFF = 36 # i32
|
||||||
|
|
||||||
# 结果 Kind(宿主按此回显)
|
# Result kind (the host echoes accordingly)
|
||||||
REPL_RES_NONE: t.CDefine = 0 # 本片段无结果(如仅声明)
|
define REPL_RES_NONE = 0 # No result for this fragment (e.g. declaration only)
|
||||||
REPL_RES_INT: t.CDefine = 1 # 整数(读 result_a 位模式)
|
define REPL_RES_INT = 1 # Integer (read result_a bit pattern)
|
||||||
REPL_RES_FLOAT: t.CDefine = 2 # float64(读 result_a 的 double 位模式)
|
define REPL_RES_FLOAT = 2 # float64 (read result_a's double bit pattern)
|
||||||
REPL_RES_STR: t.CDefine = 3 # str/bytes(读 result_a 指针 + result_b 长度)
|
define REPL_RES_STR = 3 # str/bytes (read result_a pointer + result_b length)
|
||||||
REPL_RES_OBJ: t.CDefine = 4 # 结构体/其它指针(读 result_a 地址)
|
define REPL_RES_OBJ = 4 # Struct/other pointer (read result_a address)
|
||||||
|
|
||||||
# 会话变量类型标签(ReplVarEntry.TyTag)
|
# Session variable type tags (ReplVarEntry.TyTag)
|
||||||
REPL_TY_INT: t.CDefine = 1
|
define REPL_TY_INT = 1
|
||||||
REPL_TY_FLOAT: t.CDefine = 2
|
define REPL_TY_FLOAT = 2
|
||||||
REPL_TY_STR: t.CDefine = 3 # i8*(str/bytes)
|
define REPL_TY_STR = 3 # i8* (str/bytes)
|
||||||
REPL_TY_PTR: t.CDefine = 4 # 其他指针(Bits=pointee 位宽 / TyName=结构体名)
|
define REPL_TY_PTR = 4 # Other pointer (Bits=pointee width / TyName=struct name)
|
||||||
REPL_TY_STRUCT: t.CDefine = 5 # 结构体值(按声明类型内联在槽里;≤ 槽容量)
|
define REPL_TY_STRUCT = 5 # Struct value (inlined in the slot per declared type; <= slot capacity)
|
||||||
|
|
||||||
# 会话变量存放形态(ReplVarEntry.Kind)
|
# Session variable storage kind (ReplVarEntry.Kind)
|
||||||
REPL_KIND_UNTYPED: t.CDefine = 0 # 已登记槽位但尚未定型(未赋值/无注解)
|
define REPL_KIND_UNTYPED = 0 # Slot registered but not yet typed (unassigned / no annotation)
|
||||||
REPL_KIND_SCALAR: t.CDefine = 1 # 值内联存槽(标量/指针/≤16B 结构体值)
|
define REPL_KIND_SCALAR = 1 # Value inlined in the slot (scalar/pointer/<=16B struct value)
|
||||||
|
|
||||||
# 会话函数签名的返回类型 token(M3;只用于 ReplFuncEntry.RetTag)
|
# Session function signature return-type token (M3; used only for ReplFuncEntry.RetTag)
|
||||||
REPL_TY_VOID: t.CDefine = 0 # 无返回值(void;不作为形参 token)
|
define REPL_TY_VOID = 0 # No return value (void; not used as a param token)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# ReplVarEntry - REPL 会话变量条目(跨片段存活,表本体在 PermPool)
|
# ReplVarEntry - REPL session variable entry (survives across fragments; table in PermPool)
|
||||||
# ============================================================
|
|
||||||
novirtual class ReplVarEntry:
|
novirtual class ReplVarEntry:
|
||||||
"""REPL 会话变量条目:名字 + ctx 槽偏移 + 定型信息
|
"""REPL session variable entry: name + ctx slot offset + type info"""
|
||||||
|
Name: str # Variable name (PermPool deep copy, NUL-terminated)
|
||||||
Name/TyName 必须是 PermPool 深拷贝——宿主的 AST 名字串属于片段 pool,
|
TyName: str # Struct name (TyTag=PTR with struct pointee / TyTag=STRUCT); else None
|
||||||
行末 reset_to 后即失效,直接存指针会读到已回收内存。
|
Offset: int # Slot offset within ctx (REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
|
||||||
"""
|
|
||||||
Name: str # 变量名(PermPool 深拷贝,NUL 结尾)
|
|
||||||
TyName: str # 结构体名(TyTag=PTR 且 pointee 为结构体 / TyTag=STRUCT);否则 None
|
|
||||||
Offset: int # ctx 内槽偏移(REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
|
|
||||||
Kind: int # REPL_KIND_*
|
Kind: int # REPL_KIND_*
|
||||||
TyTag: int # REPL_TY_*
|
TyTag: int # REPL_TY_*
|
||||||
Bits: int # 标量位宽;TyTag=PTR 时是 pointee 位宽(0=未知)
|
Bits: int # Scalar bit width; for TyTag=PTR the pointee width (0=unknown)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ReplFuncEntry - REPL session function entry (M3; survives fragments, in PermPool).
|
||||||
# ReplFuncEntry - REPL 会话函数条目(M3;跨片段存活,表本体在 PermPool)
|
# Maps name -> symbol + fragment no + slot offset + signature token for indirect calls.
|
||||||
#
|
|
||||||
# 语义:片段 N 里 `def f(...)` 被提升为片段模块里的**顶层 export def**(裸名导出),
|
|
||||||
# 该 DLL 加载后宿主 GetProcAddress 取到地址并写入 ctx 的函数指针槽;本条目记录
|
|
||||||
# "名字 → 符号名 + 定义片段号 + 槽偏移 + 签名 token",后续片段的调用点据此
|
|
||||||
# 从槽 load 地址并间接调用(与 M2 的会话变量槽同理:单一真相 + 间接寻址)。
|
|
||||||
#
|
|
||||||
# 签名 token(ParamTags/ParamBits 每形参 1 字节、RetTag/RetBits)与来源:
|
|
||||||
# 定义片段翻译出的 IR 里 `define <ret> @f(i8* %__repl_ctx__, <P1> %p1, ...)`
|
|
||||||
# 是 ABI 的唯一真相(注解 → LLVM 类型由 codegen 解析),宿主在注册前解析该
|
|
||||||
# define 行并**只接受**可往返重建的 token 集合:
|
|
||||||
# REPL_TY_INT(bits=1/8/16/32/64) / REPL_TY_FLOAT(bits=32/64) / REPL_TY_STR(i8*)
|
|
||||||
# 其余类型(结构体值、数组、多级指针等)⇒ 注册期明确报错,绝不静默按 i32 兜底。
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ReplFuncEntry:
|
novirtual class ReplFuncEntry:
|
||||||
"""REPL 会话函数条目:名字 + 导出符号名 + ctx 函数指针槽 + 签名 token"""
|
"""REPL session function entry: name + export symbol + ctx function-pointer slot + signature token"""
|
||||||
Name: str # 会话函数名(= 用户写的裸名;PermPool 深拷贝,NUL 结尾)
|
Name: str # Session function name (= the bare name the user wrote; PermPool copy)
|
||||||
SymName: str # 片段 DLL 里的导出符号名(PermPool 深拷贝;M3 = 裸名)
|
SymName: str # Export symbol name in the fragment DLL (PermPool copy; M3 = bare name)
|
||||||
FragNo: int # 定义该函数的片段号(重定义时更新为最新片段号)
|
FragNo: int # Fragment number defining this function (updated on redefinition)
|
||||||
SlotOff: int # ctx 内函数指针槽偏移(REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
|
SlotOff: int # Function pointer slot offset within ctx (REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
|
||||||
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
|
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
|
||||||
RetBits: int # 对齐 ReplVarEntry.Bits 的口径(VOID 时为 0)
|
RetBits: int # Same measure as ReplVarEntry.Bits (0 for VOID)
|
||||||
ParamCount: int # 形参数(**不含**注入的 ctx 首形参)
|
ParamCount: int # Param count (NOT including the injected ctx first param)
|
||||||
ParamTags: str # PermPool 字节数组(每形参 1 字节 tag,REPL_FUNC_PARAM_MAX 个)
|
ParamTags: str # PermPool byte array (1 tag per param, REPL_FUNC_PARAM_MAX entries)
|
||||||
ParamBits: str # PermPool 字节数组(每形参 1 字节 bits,REPL_FUNC_PARAM_MAX 个)
|
ParamBits: str # PermPool byte array (1 bits byte per param, REPL_FUNC_PARAM_MAX entries)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ReplCtx - REPL session context (host-built, translator-read). trans.ReplCtx != None
|
||||||
# ReplCtx - REPL 会话上下文(宿主建、翻译器读)
|
# enables REPL mode and guards all Gen-side interception points; otherwise the old path is used.
|
||||||
#
|
|
||||||
# 开关语义:trans.ReplCtx != None ⇒ REPL 模式(仅 Repl.vp 翻译片段前置位,
|
|
||||||
# 翻译后清空)。Gen 侧全部拦截点都以此字段为唯一守卫——普通编译路径下该字段
|
|
||||||
# 恒为 None,逐字走原路径。
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ReplCtx:
|
novirtual class ReplCtx:
|
||||||
"""REPL 模式上下文(宿主持有;翻译器只读)"""
|
"""REPL mode context (host-owned, translator-read-only)"""
|
||||||
Marker: int # REPL_CTX_MARKER(防误传非 ReplCtx 对象)
|
Marker: int # REPL_CTX_MARKER (guards against a non-ReplCtx object)
|
||||||
Table: rawptr[ReplVarEntry] # 会话表数组(PermPool,容量 Cap)
|
Table: ReplVarEntry* # Session table array (PermPool, capacity Cap)
|
||||||
Count: int # 已登记条目数(= 已分配槽数)
|
Count: int # Registered entry count (= allocated slots)
|
||||||
Cap: int # 容量(REPL_VAR_CAP)
|
Cap: int # Capacity (REPL_VAR_CAP)
|
||||||
CtxParam: str # 片段函数 ctx 形参名(PermPool 深拷贝)
|
CtxParam: str # Fragment function ctx param name (PermPool copy)
|
||||||
SlotSize: int # 槽步长(REPL_SLOT_SIZE)
|
SlotSize: int # Slot stride (REPL_SLOT_SIZE)
|
||||||
# 影子条目(宿主建、PermPool):仅供"裸表达式结果"做类型分类用,不占会话槽、
|
# Shadow entry (host-built, PermPool): only classifies bare-expression results; it
|
||||||
# 不进表(Count 不计入),Name 固定为 "<expr>" 以便错误消息可读。
|
# occupies no session slot and is not counted; Name is fixed to "<expr>".
|
||||||
Scratch: rawptr[ReplVarEntry]
|
Scratch: ReplVarEntry*
|
||||||
# 宿主提供的永久池:会话表条目名/结构体名等**跨片段存活**的数据必须在此池
|
# Permanent pool provided by the host: cross-fragment data (session names, struct
|
||||||
# 深拷贝(片段的 trans.Pool 行末 reset_to 即回收,直接存其指针会悬垂)。
|
# names) must be deep-copied here, since a fragment's trans.Pool is reset each line.
|
||||||
Perm: memhub.MemBuddy*
|
Perm: memhub.MemBuddy*
|
||||||
# 会话函数表(M3;宿主写、Gen 只读):数组容量 FuncCap,已登记 FuncCount 条。
|
# Session function table (M3; host-writes, Gen-reads): capacity FuncCap, FuncCount
|
||||||
# 条目只在"定义片段 DLL 加载成功且 GetProcAddress 命中"后由宿主追加 ⇒ 片段
|
# registered. Entries are appended only after a fragment DLL loads and GetProcAddress hits.
|
||||||
# 翻译期看到的表恒为"先前片段的已就绪函数"(本片段新定义的同名函数走直接调用)。
|
FuncTable: ReplFuncEntry*
|
||||||
FuncTable: rawptr[ReplFuncEntry]
|
|
||||||
FuncCount: int
|
FuncCount: int
|
||||||
FuncCap: int # 容量(REPL_FUNC_CAP)
|
FuncCap: int # Capacity (REPL_FUNC_CAP)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ReplResult - ctx header (host reads by field; a fragment writes by REPL_RES_*_OFF).
|
||||||
# ReplResult - ctx 头(宿主按字段读;片段按 REPL_RES_*_OFF 字节偏移写)
|
# Field order/width is the ctx layout: Magic, Version, Kind, Reserved0, A, B, VarCount, LastFrag.
|
||||||
#
|
|
||||||
# 字段顺序/宽度即 ctx 布局:Magic(+0,i32) Version(+4,i32) Kind(+8,i32)
|
|
||||||
# Reserved0(+12,i32) A(+16,i64) B(+24,i64) VarCount(+32,i32) LastFrag(+36,i32)。
|
|
||||||
# 宿主另有"同一头的浮点视图"(把 +16 的 8 字节按 double 读回,见 Repl.vp)。
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ReplResult:
|
novirtual class ReplResult:
|
||||||
"""ctx 固定头(前 64 字节中的前 40 字节;+40..63 为保留)"""
|
"""Fixed ctx header (first 40 of the 64 header bytes; +40..63 reserved)"""
|
||||||
Magic: int # REPL_CTX_MAGIC('REPL')
|
Magic: int # REPL_CTX_MAGIC ('REPL')
|
||||||
Version: int # REPL_CTX_VERSION
|
Version: int # REPL_CTX_VERSION
|
||||||
Kind: int # REPL_RES_*
|
Kind: int # REPL_RES_*
|
||||||
Reserved0: int # 保留(0)
|
Reserved0: int # Reserved (0)
|
||||||
A: t.CInt64T # result_a
|
A: int64 # result_a
|
||||||
B: t.CInt64T # result_b
|
B: int64 # result_b
|
||||||
VarCount: int # 已分配槽数(宿主写,供检视)
|
VarCount: int # Allocated slot count (host-written, for inspection)
|
||||||
LastFrag: int # 最近执行的片段号(宿主写,供检视)
|
LastFrag: int # Most recent fragment number (host-written, for inspection)
|
||||||
+102
-108
@@ -1,11 +1,10 @@
|
|||||||
import string
|
import string
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
import lib.core.CompilerFactory as CompilerFactory
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import stdio
|
import stdio
|
||||||
import stdlib
|
import stdlib
|
||||||
import memhub
|
import memhub
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import lib.core.Gen.BaseGenMixin as BaseGenMixin
|
import lib.core.Gen.BaseGenMixin as BaseGenMixin
|
||||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||||
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
||||||
@@ -55,388 +54,385 @@ import lib.core.Gen.VarGenMixin as VarGenMixin
|
|||||||
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
|
import lib.core.Gen.TranslatorGenMixin as TranslatorGenMixin
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Generator - generator (bidirectional-reference composition architecture)
|
||||||
# Generator - 生成器(双向引用组合架构)
|
|
||||||
# ============================================================
|
|
||||||
novirtual class Generator:
|
novirtual class Generator:
|
||||||
"""生成器:持所有 GenMixin 子对象指针(双向引用组合)"""
|
"""Generator holding pointers to all GenMixin sub-objects (bidirectional composition)."""
|
||||||
|
|
||||||
# ---- 子对象指针(46 个,永久池) ----
|
# ---- sub-object pointers (46, permanent pool) ----
|
||||||
BaseGen: rawptr[BaseGenMixin.BaseGenMixin]
|
BaseGen: BaseGenMixin.BaseGenMixin*
|
||||||
TypeGenBase: rawptr[TypeGenBase.TypeGenBase]
|
TypeGenBase: TypeGenBase.TypeGenBase*
|
||||||
TypeGen: rawptr[TypeGenMixin.TypeGenMixin]
|
TypeGen: TypeGenMixin.TypeGenMixin*
|
||||||
TypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef]
|
TypeGenCDef: TypeGenCDef.TypeGenCDef*
|
||||||
TypeGenCType: rawptr[TypeGenCType.TypeGenCType]
|
TypeGenCType: TypeGenCType.TypeGenCType*
|
||||||
TypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve]
|
TypeGenResolve: TypeGenResolve.TypeGenResolve*
|
||||||
TypeGenErr: rawptr[TypeGenErr.TypeGenErr]
|
TypeGenErr: TypeGenErr.TypeGenErr*
|
||||||
StructGen: rawptr[StructGenMixin.StructGenMixin]
|
StructGen: StructGenMixin.StructGenMixin*
|
||||||
ExprGenBase: rawptr[ExprGenBase.ExprGenBase]
|
ExprGenBase: ExprGenBase.ExprGenBase*
|
||||||
ExprGen: rawptr[ExprGenMixin.ExprGenMixin]
|
ExprGen: ExprGenMixin.ExprGenMixin*
|
||||||
ExprGenConst: rawptr[ExprGenConst.ExprGenConst]
|
ExprGenConst: ExprGenConst.ExprGenConst*
|
||||||
ExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr]
|
ExprGenFstr: ExprGenFstr.ExprGenFstr*
|
||||||
ExprGenOps: rawptr[ExprGenOps.ExprGenOps]
|
ExprGenOps: ExprGenOps.ExprGenOps*
|
||||||
ExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef]
|
ExprGenCdef: ExprGenCdef.ExprGenCdef*
|
||||||
ExprGenName: rawptr[ExprGenName.ExprGenName]
|
ExprGenName: ExprGenName.ExprGenName*
|
||||||
ExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr]
|
ExprGenAttr: ExprGenAttr.ExprGenAttr*
|
||||||
ExprGenCls: rawptr[ExprGenCls.ExprGenCls]
|
ExprGenCls: ExprGenCls.ExprGenCls*
|
||||||
CallGenBase: rawptr[CallGenBase.CallGenBase]
|
CallGenBase: CallGenBase.CallGenBase*
|
||||||
CallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin]
|
CallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin*
|
||||||
CallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit]
|
CallGenLeafKit: CallGenLeafKit.CallGenLeafKit*
|
||||||
CallGenCtor: rawptr[CallGenCtor.CallGenCtor]
|
CallGenCtor: CallGenCtor.CallGenCtor*
|
||||||
CallGenCall: rawptr[CallGenCall.CallGenCall]
|
CallGenCall: CallGenCall.CallGenCall*
|
||||||
CallGen: rawptr[CallGenMixin.CallGenMixin]
|
CallGen: CallGenMixin.CallGenMixin*
|
||||||
CallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch]
|
CallGenDispatch: CallGenDispatch.CallGenDispatch*
|
||||||
CallGenLib: rawptr[CallGenLib.CallGenLib]
|
CallGenLib: CallGenLib.CallGenLib*
|
||||||
CallGenVt: rawptr[CallGenVt.CallGenVt]
|
CallGenVt: CallGenVt.CallGenVt*
|
||||||
CallGenVa: rawptr[CallGenVa.CallGenVa]
|
CallGenVa: CallGenVa.CallGenVa*
|
||||||
CallGenProp: rawptr[CallGenProp.CallGenProp]
|
CallGenProp: CallGenProp.CallGenProp*
|
||||||
CallGenFd: rawptr[CallGenFd.CallGenFd]
|
CallGenFd: CallGenFd.CallGenFd*
|
||||||
FuncGenBase: rawptr[FuncGenBase.FuncGenBase]
|
FuncGenBase: FuncGenBase.FuncGenBase*
|
||||||
FuncGenDef: rawptr[FuncGenDef.FuncGenDef]
|
FuncGenDef: FuncGenDef.FuncGenDef*
|
||||||
FuncGen: rawptr[FuncGenMixin.FuncGenMixin]
|
FuncGen: FuncGenMixin.FuncGenMixin*
|
||||||
ClassGenBase: rawptr[ClassGenBase.ClassGenBase]
|
ClassGenBase: ClassGenBase.ClassGenBase*
|
||||||
ClassGen: rawptr[ClassGenMixin.ClassGenMixin]
|
ClassGen: ClassGenMixin.ClassGenMixin*
|
||||||
ClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum]
|
ClassGenEnum: ClassGenEnum.ClassGenEnum*
|
||||||
ClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric]
|
ClassGenGeneric: ClassGenGeneric.ClassGenGeneric*
|
||||||
ClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable]
|
ClassGenVTable: ClassGenVTable.ClassGenVTable*
|
||||||
ClassGenDef: rawptr[ClassGenDef.ClassGenDef]
|
ClassGenDef: ClassGenDef.ClassGenDef*
|
||||||
ImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin]
|
ImportsGen: ImportsGenMixin.ImportsGenMixin*
|
||||||
StmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin]
|
StmtGenCif: StmtGenCifMixin.StmtGenCifMixin*
|
||||||
StmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin]
|
StmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin*
|
||||||
StmtGen: rawptr[StmtGenMixin.StmtGenMixin]
|
StmtGen: StmtGenMixin.StmtGenMixin*
|
||||||
MatchGen: rawptr[MatchGenMixin.MatchGenMixin]
|
MatchGen: MatchGenMixin.MatchGenMixin*
|
||||||
AssignGen: rawptr[AssignGenMixin.AssignGenMixin]
|
AssignGen: AssignGenMixin.AssignGenMixin*
|
||||||
AsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin]
|
AsyncGen: AsyncGenMixin.AsyncGenMixin*
|
||||||
VarGen: rawptr[VarGenMixin.VarGenMixin]
|
VarGen: VarGenMixin.VarGenMixin*
|
||||||
TranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin]
|
TranslatorGen: TranslatorGenMixin.TranslatorGenMixin*
|
||||||
|
|
||||||
def __init__(self, pool: memhub.MemBuddy*):
|
def __init__(self, pool: memhub.MemBuddy*):
|
||||||
if self is None:
|
if self is None: return
|
||||||
return
|
# ---- alloc 46 sub-objects + back-pointers (permanent pool, fail-fast) ----
|
||||||
# ---- alloc 46 个子对象 + 回指(永久池,fail-fast) ----
|
SubBaseGen: BaseGenMixin.BaseGenMixin* = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||||
SubBaseGen: rawptr[BaseGenMixin.BaseGenMixin] = CompilerFactory.factory.PermPool.alloc(BaseGenMixin.BaseGenMixin.__sizeof__())
|
|
||||||
if SubBaseGen is None:
|
if SubBaseGen is None:
|
||||||
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
|
stdio.printf("FAIL: BaseGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
|
string.memset(SubBaseGen, 0, BaseGenMixin.BaseGenMixin.__sizeof__())
|
||||||
SubBaseGen.Gen = self
|
SubBaseGen.Gen = self
|
||||||
self.BaseGen = SubBaseGen
|
self.BaseGen = SubBaseGen
|
||||||
SubTypeGenBase: rawptr[TypeGenBase.TypeGenBase] = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
|
SubTypeGenBase: TypeGenBase.TypeGenBase* = CompilerFactory.factory.PermPool.alloc(TypeGenBase.TypeGenBase.__sizeof__())
|
||||||
if SubTypeGenBase is None:
|
if SubTypeGenBase is None:
|
||||||
stdio.printf("FAIL: TypeGenBase alloc failed\n")
|
stdio.printf("FAIL: TypeGenBase alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
|
string.memset(SubTypeGenBase, 0, TypeGenBase.TypeGenBase.__sizeof__())
|
||||||
SubTypeGenBase.Gen = self
|
SubTypeGenBase.Gen = self
|
||||||
self.TypeGenBase = SubTypeGenBase
|
self.TypeGenBase = SubTypeGenBase
|
||||||
SubTypeGen: rawptr[TypeGenMixin.TypeGenMixin] = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
|
SubTypeGen: TypeGenMixin.TypeGenMixin* = CompilerFactory.factory.PermPool.alloc(TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||||
if SubTypeGen is None:
|
if SubTypeGen is None:
|
||||||
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
|
stdio.printf("FAIL: TypeGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
|
string.memset(SubTypeGen, 0, TypeGenMixin.TypeGenMixin.__sizeof__())
|
||||||
SubTypeGen.Gen = self
|
SubTypeGen.Gen = self
|
||||||
self.TypeGen = SubTypeGen
|
self.TypeGen = SubTypeGen
|
||||||
SubTypeGenCDef: rawptr[TypeGenCDef.TypeGenCDef] = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
|
SubTypeGenCDef: TypeGenCDef.TypeGenCDef* = CompilerFactory.factory.PermPool.alloc(TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||||
if SubTypeGenCDef is None:
|
if SubTypeGenCDef is None:
|
||||||
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
|
stdio.printf("FAIL: TypeGenCDef alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
|
string.memset(SubTypeGenCDef, 0, TypeGenCDef.TypeGenCDef.__sizeof__())
|
||||||
SubTypeGenCDef.Gen = self
|
SubTypeGenCDef.Gen = self
|
||||||
self.TypeGenCDef = SubTypeGenCDef
|
self.TypeGenCDef = SubTypeGenCDef
|
||||||
SubTypeGenCType: rawptr[TypeGenCType.TypeGenCType] = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
|
SubTypeGenCType: TypeGenCType.TypeGenCType* = CompilerFactory.factory.PermPool.alloc(TypeGenCType.TypeGenCType.__sizeof__())
|
||||||
if SubTypeGenCType is None:
|
if SubTypeGenCType is None:
|
||||||
stdio.printf("FAIL: TypeGenCType alloc failed\n")
|
stdio.printf("FAIL: TypeGenCType alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
|
string.memset(SubTypeGenCType, 0, TypeGenCType.TypeGenCType.__sizeof__())
|
||||||
SubTypeGenCType.Gen = self
|
SubTypeGenCType.Gen = self
|
||||||
self.TypeGenCType = SubTypeGenCType
|
self.TypeGenCType = SubTypeGenCType
|
||||||
SubTypeGenResolve: rawptr[TypeGenResolve.TypeGenResolve] = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
|
SubTypeGenResolve: TypeGenResolve.TypeGenResolve* = CompilerFactory.factory.PermPool.alloc(TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||||
if SubTypeGenResolve is None:
|
if SubTypeGenResolve is None:
|
||||||
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
|
stdio.printf("FAIL: TypeGenResolve alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
|
string.memset(SubTypeGenResolve, 0, TypeGenResolve.TypeGenResolve.__sizeof__())
|
||||||
SubTypeGenResolve.Gen = self
|
SubTypeGenResolve.Gen = self
|
||||||
self.TypeGenResolve = SubTypeGenResolve
|
self.TypeGenResolve = SubTypeGenResolve
|
||||||
SubTypeGenErr: rawptr[TypeGenErr.TypeGenErr] = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
|
SubTypeGenErr: TypeGenErr.TypeGenErr* = CompilerFactory.factory.PermPool.alloc(TypeGenErr.TypeGenErr.__sizeof__())
|
||||||
if SubTypeGenErr is None:
|
if SubTypeGenErr is None:
|
||||||
stdio.printf("FAIL: TypeGenErr alloc failed\n")
|
stdio.printf("FAIL: TypeGenErr alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
|
string.memset(SubTypeGenErr, 0, TypeGenErr.TypeGenErr.__sizeof__())
|
||||||
SubTypeGenErr.Gen = self
|
SubTypeGenErr.Gen = self
|
||||||
self.TypeGenErr = SubTypeGenErr
|
self.TypeGenErr = SubTypeGenErr
|
||||||
SubStructGen: rawptr[StructGenMixin.StructGenMixin] = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
|
SubStructGen: StructGenMixin.StructGenMixin* = CompilerFactory.factory.PermPool.alloc(StructGenMixin.StructGenMixin.__sizeof__())
|
||||||
if SubStructGen is None:
|
if SubStructGen is None:
|
||||||
stdio.printf("FAIL: StructGenMixin alloc failed\n")
|
stdio.printf("FAIL: StructGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
|
string.memset(SubStructGen, 0, StructGenMixin.StructGenMixin.__sizeof__())
|
||||||
SubStructGen.Gen = self
|
SubStructGen.Gen = self
|
||||||
self.StructGen = SubStructGen
|
self.StructGen = SubStructGen
|
||||||
SubExprGenBase: rawptr[ExprGenBase.ExprGenBase] = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
|
SubExprGenBase: ExprGenBase.ExprGenBase* = CompilerFactory.factory.PermPool.alloc(ExprGenBase.ExprGenBase.__sizeof__())
|
||||||
if SubExprGenBase is None:
|
if SubExprGenBase is None:
|
||||||
stdio.printf("FAIL: ExprGenBase alloc failed\n")
|
stdio.printf("FAIL: ExprGenBase alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
|
string.memset(SubExprGenBase, 0, ExprGenBase.ExprGenBase.__sizeof__())
|
||||||
SubExprGenBase.Gen = self
|
SubExprGenBase.Gen = self
|
||||||
self.ExprGenBase = SubExprGenBase
|
self.ExprGenBase = SubExprGenBase
|
||||||
SubExprGen: rawptr[ExprGenMixin.ExprGenMixin] = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
|
SubExprGen: ExprGenMixin.ExprGenMixin* = CompilerFactory.factory.PermPool.alloc(ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||||
if SubExprGen is None:
|
if SubExprGen is None:
|
||||||
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
|
stdio.printf("FAIL: ExprGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
|
string.memset(SubExprGen, 0, ExprGenMixin.ExprGenMixin.__sizeof__())
|
||||||
SubExprGen.Gen = self
|
SubExprGen.Gen = self
|
||||||
self.ExprGen = SubExprGen
|
self.ExprGen = SubExprGen
|
||||||
SubExprGenConst: rawptr[ExprGenConst.ExprGenConst] = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
|
SubExprGenConst: ExprGenConst.ExprGenConst* = CompilerFactory.factory.PermPool.alloc(ExprGenConst.ExprGenConst.__sizeof__())
|
||||||
if SubExprGenConst is None:
|
if SubExprGenConst is None:
|
||||||
stdio.printf("FAIL: ExprGenConst alloc failed\n")
|
stdio.printf("FAIL: ExprGenConst alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
|
string.memset(SubExprGenConst, 0, ExprGenConst.ExprGenConst.__sizeof__())
|
||||||
SubExprGenConst.Gen = self
|
SubExprGenConst.Gen = self
|
||||||
self.ExprGenConst = SubExprGenConst
|
self.ExprGenConst = SubExprGenConst
|
||||||
SubExprGenFstr: rawptr[ExprGenFstr.ExprGenFstr] = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
|
SubExprGenFstr: ExprGenFstr.ExprGenFstr* = CompilerFactory.factory.PermPool.alloc(ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||||
if SubExprGenFstr is None:
|
if SubExprGenFstr is None:
|
||||||
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
|
stdio.printf("FAIL: ExprGenFstr alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
|
string.memset(SubExprGenFstr, 0, ExprGenFstr.ExprGenFstr.__sizeof__())
|
||||||
SubExprGenFstr.Gen = self
|
SubExprGenFstr.Gen = self
|
||||||
self.ExprGenFstr = SubExprGenFstr
|
self.ExprGenFstr = SubExprGenFstr
|
||||||
SubExprGenOps: rawptr[ExprGenOps.ExprGenOps] = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
|
SubExprGenOps: ExprGenOps.ExprGenOps* = CompilerFactory.factory.PermPool.alloc(ExprGenOps.ExprGenOps.__sizeof__())
|
||||||
if SubExprGenOps is None:
|
if SubExprGenOps is None:
|
||||||
stdio.printf("FAIL: ExprGenOps alloc failed\n")
|
stdio.printf("FAIL: ExprGenOps alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
|
string.memset(SubExprGenOps, 0, ExprGenOps.ExprGenOps.__sizeof__())
|
||||||
SubExprGenOps.Gen = self
|
SubExprGenOps.Gen = self
|
||||||
self.ExprGenOps = SubExprGenOps
|
self.ExprGenOps = SubExprGenOps
|
||||||
SubExprGenCdef: rawptr[ExprGenCdef.ExprGenCdef] = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
|
SubExprGenCdef: ExprGenCdef.ExprGenCdef* = CompilerFactory.factory.PermPool.alloc(ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||||
if SubExprGenCdef is None:
|
if SubExprGenCdef is None:
|
||||||
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
|
stdio.printf("FAIL: ExprGenCdef alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
|
string.memset(SubExprGenCdef, 0, ExprGenCdef.ExprGenCdef.__sizeof__())
|
||||||
SubExprGenCdef.Gen = self
|
SubExprGenCdef.Gen = self
|
||||||
self.ExprGenCdef = SubExprGenCdef
|
self.ExprGenCdef = SubExprGenCdef
|
||||||
SubExprGenName: rawptr[ExprGenName.ExprGenName] = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
|
SubExprGenName: ExprGenName.ExprGenName* = CompilerFactory.factory.PermPool.alloc(ExprGenName.ExprGenName.__sizeof__())
|
||||||
if SubExprGenName is None:
|
if SubExprGenName is None:
|
||||||
stdio.printf("FAIL: ExprGenName alloc failed\n")
|
stdio.printf("FAIL: ExprGenName alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
|
string.memset(SubExprGenName, 0, ExprGenName.ExprGenName.__sizeof__())
|
||||||
SubExprGenName.Gen = self
|
SubExprGenName.Gen = self
|
||||||
self.ExprGenName = SubExprGenName
|
self.ExprGenName = SubExprGenName
|
||||||
SubExprGenAttr: rawptr[ExprGenAttr.ExprGenAttr] = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
|
SubExprGenAttr: ExprGenAttr.ExprGenAttr* = CompilerFactory.factory.PermPool.alloc(ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||||
if SubExprGenAttr is None:
|
if SubExprGenAttr is None:
|
||||||
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
|
stdio.printf("FAIL: ExprGenAttr alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
|
string.memset(SubExprGenAttr, 0, ExprGenAttr.ExprGenAttr.__sizeof__())
|
||||||
SubExprGenAttr.Gen = self
|
SubExprGenAttr.Gen = self
|
||||||
self.ExprGenAttr = SubExprGenAttr
|
self.ExprGenAttr = SubExprGenAttr
|
||||||
SubExprGenCls: rawptr[ExprGenCls.ExprGenCls] = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
|
SubExprGenCls: ExprGenCls.ExprGenCls* = CompilerFactory.factory.PermPool.alloc(ExprGenCls.ExprGenCls.__sizeof__())
|
||||||
if SubExprGenCls is None:
|
if SubExprGenCls is None:
|
||||||
stdio.printf("FAIL: ExprGenCls alloc failed\n")
|
stdio.printf("FAIL: ExprGenCls alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
|
string.memset(SubExprGenCls, 0, ExprGenCls.ExprGenCls.__sizeof__())
|
||||||
SubExprGenCls.Gen = self
|
SubExprGenCls.Gen = self
|
||||||
self.ExprGenCls = SubExprGenCls
|
self.ExprGenCls = SubExprGenCls
|
||||||
SubCallGenBase: rawptr[CallGenBase.CallGenBase] = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
|
SubCallGenBase: CallGenBase.CallGenBase* = CompilerFactory.factory.PermPool.alloc(CallGenBase.CallGenBase.__sizeof__())
|
||||||
if SubCallGenBase is None:
|
if SubCallGenBase is None:
|
||||||
stdio.printf("FAIL: CallGenBase alloc failed\n")
|
stdio.printf("FAIL: CallGenBase alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
|
string.memset(SubCallGenBase, 0, CallGenBase.CallGenBase.__sizeof__())
|
||||||
SubCallGenBase.Gen = self
|
SubCallGenBase.Gen = self
|
||||||
self.CallGenBase = SubCallGenBase
|
self.CallGenBase = SubCallGenBase
|
||||||
SubCallGenCBuiltin: rawptr[CallGenCBuiltin.CallGenCBuiltin] = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
SubCallGenCBuiltin: CallGenCBuiltin.CallGenCBuiltin* = CompilerFactory.factory.PermPool.alloc(CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||||
if SubCallGenCBuiltin is None:
|
if SubCallGenCBuiltin is None:
|
||||||
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
|
stdio.printf("FAIL: CallGenCBuiltin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
string.memset(SubCallGenCBuiltin, 0, CallGenCBuiltin.CallGenCBuiltin.__sizeof__())
|
||||||
SubCallGenCBuiltin.Gen = self
|
SubCallGenCBuiltin.Gen = self
|
||||||
self.CallGenCBuiltin = SubCallGenCBuiltin
|
self.CallGenCBuiltin = SubCallGenCBuiltin
|
||||||
SubCallGenLeafKit: rawptr[CallGenLeafKit.CallGenLeafKit] = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
SubCallGenLeafKit: CallGenLeafKit.CallGenLeafKit* = CompilerFactory.factory.PermPool.alloc(CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||||
if SubCallGenLeafKit is None:
|
if SubCallGenLeafKit is None:
|
||||||
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
|
stdio.printf("FAIL: CallGenLeafKit alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
string.memset(SubCallGenLeafKit, 0, CallGenLeafKit.CallGenLeafKit.__sizeof__())
|
||||||
SubCallGenLeafKit.Gen = self
|
SubCallGenLeafKit.Gen = self
|
||||||
self.CallGenLeafKit = SubCallGenLeafKit
|
self.CallGenLeafKit = SubCallGenLeafKit
|
||||||
SubCallGenCtor: rawptr[CallGenCtor.CallGenCtor] = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
|
SubCallGenCtor: CallGenCtor.CallGenCtor* = CompilerFactory.factory.PermPool.alloc(CallGenCtor.CallGenCtor.__sizeof__())
|
||||||
if SubCallGenCtor is None:
|
if SubCallGenCtor is None:
|
||||||
stdio.printf("FAIL: CallGenCtor alloc failed\n")
|
stdio.printf("FAIL: CallGenCtor alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
|
string.memset(SubCallGenCtor, 0, CallGenCtor.CallGenCtor.__sizeof__())
|
||||||
SubCallGenCtor.Gen = self
|
SubCallGenCtor.Gen = self
|
||||||
self.CallGenCtor = SubCallGenCtor
|
self.CallGenCtor = SubCallGenCtor
|
||||||
SubCallGenCall: rawptr[CallGenCall.CallGenCall] = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
|
SubCallGenCall: CallGenCall.CallGenCall* = CompilerFactory.factory.PermPool.alloc(CallGenCall.CallGenCall.__sizeof__())
|
||||||
if SubCallGenCall is None:
|
if SubCallGenCall is None:
|
||||||
stdio.printf("FAIL: CallGenCall alloc failed\n")
|
stdio.printf("FAIL: CallGenCall alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
|
string.memset(SubCallGenCall, 0, CallGenCall.CallGenCall.__sizeof__())
|
||||||
SubCallGenCall.Gen = self
|
SubCallGenCall.Gen = self
|
||||||
self.CallGenCall = SubCallGenCall
|
self.CallGenCall = SubCallGenCall
|
||||||
SubCallGen: rawptr[CallGenMixin.CallGenMixin] = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
|
SubCallGen: CallGenMixin.CallGenMixin* = CompilerFactory.factory.PermPool.alloc(CallGenMixin.CallGenMixin.__sizeof__())
|
||||||
if SubCallGen is None:
|
if SubCallGen is None:
|
||||||
stdio.printf("FAIL: CallGenMixin alloc failed\n")
|
stdio.printf("FAIL: CallGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
|
string.memset(SubCallGen, 0, CallGenMixin.CallGenMixin.__sizeof__())
|
||||||
SubCallGen.Gen = self
|
SubCallGen.Gen = self
|
||||||
self.CallGen = SubCallGen
|
self.CallGen = SubCallGen
|
||||||
SubCallGenDispatch: rawptr[CallGenDispatch.CallGenDispatch] = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
|
SubCallGenDispatch: CallGenDispatch.CallGenDispatch* = CompilerFactory.factory.PermPool.alloc(CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||||
if SubCallGenDispatch is None:
|
if SubCallGenDispatch is None:
|
||||||
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
|
stdio.printf("FAIL: CallGenDispatch alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
|
string.memset(SubCallGenDispatch, 0, CallGenDispatch.CallGenDispatch.__sizeof__())
|
||||||
SubCallGenDispatch.Gen = self
|
SubCallGenDispatch.Gen = self
|
||||||
self.CallGenDispatch = SubCallGenDispatch
|
self.CallGenDispatch = SubCallGenDispatch
|
||||||
SubCallGenLib: rawptr[CallGenLib.CallGenLib] = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
|
SubCallGenLib: CallGenLib.CallGenLib* = CompilerFactory.factory.PermPool.alloc(CallGenLib.CallGenLib.__sizeof__())
|
||||||
if SubCallGenLib is None:
|
if SubCallGenLib is None:
|
||||||
stdio.printf("FAIL: CallGenLib alloc failed\n")
|
stdio.printf("FAIL: CallGenLib alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
|
string.memset(SubCallGenLib, 0, CallGenLib.CallGenLib.__sizeof__())
|
||||||
SubCallGenLib.Gen = self
|
SubCallGenLib.Gen = self
|
||||||
self.CallGenLib = SubCallGenLib
|
self.CallGenLib = SubCallGenLib
|
||||||
SubCallGenVt: rawptr[CallGenVt.CallGenVt] = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
|
SubCallGenVt: CallGenVt.CallGenVt* = CompilerFactory.factory.PermPool.alloc(CallGenVt.CallGenVt.__sizeof__())
|
||||||
if SubCallGenVt is None:
|
if SubCallGenVt is None:
|
||||||
stdio.printf("FAIL: CallGenVt alloc failed\n")
|
stdio.printf("FAIL: CallGenVt alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
|
string.memset(SubCallGenVt, 0, CallGenVt.CallGenVt.__sizeof__())
|
||||||
SubCallGenVt.Gen = self
|
SubCallGenVt.Gen = self
|
||||||
self.CallGenVt = SubCallGenVt
|
self.CallGenVt = SubCallGenVt
|
||||||
SubCallGenVa: rawptr[CallGenVa.CallGenVa] = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
|
SubCallGenVa: CallGenVa.CallGenVa* = CompilerFactory.factory.PermPool.alloc(CallGenVa.CallGenVa.__sizeof__())
|
||||||
if SubCallGenVa is None:
|
if SubCallGenVa is None:
|
||||||
stdio.printf("FAIL: CallGenVa alloc failed\n")
|
stdio.printf("FAIL: CallGenVa alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
|
string.memset(SubCallGenVa, 0, CallGenVa.CallGenVa.__sizeof__())
|
||||||
SubCallGenVa.Gen = self
|
SubCallGenVa.Gen = self
|
||||||
self.CallGenVa = SubCallGenVa
|
self.CallGenVa = SubCallGenVa
|
||||||
SubCallGenProp: rawptr[CallGenProp.CallGenProp] = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
|
SubCallGenProp: CallGenProp.CallGenProp* = CompilerFactory.factory.PermPool.alloc(CallGenProp.CallGenProp.__sizeof__())
|
||||||
if SubCallGenProp is None:
|
if SubCallGenProp is None:
|
||||||
stdio.printf("FAIL: CallGenProp alloc failed\n")
|
stdio.printf("FAIL: CallGenProp alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
|
string.memset(SubCallGenProp, 0, CallGenProp.CallGenProp.__sizeof__())
|
||||||
SubCallGenProp.Gen = self
|
SubCallGenProp.Gen = self
|
||||||
self.CallGenProp = SubCallGenProp
|
self.CallGenProp = SubCallGenProp
|
||||||
SubCallGenFd: rawptr[CallGenFd.CallGenFd] = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
|
SubCallGenFd: CallGenFd.CallGenFd* = CompilerFactory.factory.PermPool.alloc(CallGenFd.CallGenFd.__sizeof__())
|
||||||
if SubCallGenFd is None:
|
if SubCallGenFd is None:
|
||||||
stdio.printf("FAIL: CallGenFd alloc failed\n")
|
stdio.printf("FAIL: CallGenFd alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
|
string.memset(SubCallGenFd, 0, CallGenFd.CallGenFd.__sizeof__())
|
||||||
SubCallGenFd.Gen = self
|
SubCallGenFd.Gen = self
|
||||||
self.CallGenFd = SubCallGenFd
|
self.CallGenFd = SubCallGenFd
|
||||||
SubFuncGenBase: rawptr[FuncGenBase.FuncGenBase] = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
|
SubFuncGenBase: FuncGenBase.FuncGenBase* = CompilerFactory.factory.PermPool.alloc(FuncGenBase.FuncGenBase.__sizeof__())
|
||||||
if SubFuncGenBase is None:
|
if SubFuncGenBase is None:
|
||||||
stdio.printf("FAIL: FuncGenBase alloc failed\n")
|
stdio.printf("FAIL: FuncGenBase alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
|
string.memset(SubFuncGenBase, 0, FuncGenBase.FuncGenBase.__sizeof__())
|
||||||
SubFuncGenBase.Gen = self
|
SubFuncGenBase.Gen = self
|
||||||
self.FuncGenBase = SubFuncGenBase
|
self.FuncGenBase = SubFuncGenBase
|
||||||
SubFuncGenDef: rawptr[FuncGenDef.FuncGenDef] = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
|
SubFuncGenDef: FuncGenDef.FuncGenDef* = CompilerFactory.factory.PermPool.alloc(FuncGenDef.FuncGenDef.__sizeof__())
|
||||||
if SubFuncGenDef is None:
|
if SubFuncGenDef is None:
|
||||||
stdio.printf("FAIL: FuncGenDef alloc failed\n")
|
stdio.printf("FAIL: FuncGenDef alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
|
string.memset(SubFuncGenDef, 0, FuncGenDef.FuncGenDef.__sizeof__())
|
||||||
SubFuncGenDef.Gen = self
|
SubFuncGenDef.Gen = self
|
||||||
self.FuncGenDef = SubFuncGenDef
|
self.FuncGenDef = SubFuncGenDef
|
||||||
SubFuncGen: rawptr[FuncGenMixin.FuncGenMixin] = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
|
SubFuncGen: FuncGenMixin.FuncGenMixin* = CompilerFactory.factory.PermPool.alloc(FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||||
if SubFuncGen is None:
|
if SubFuncGen is None:
|
||||||
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
|
stdio.printf("FAIL: FuncGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
|
string.memset(SubFuncGen, 0, FuncGenMixin.FuncGenMixin.__sizeof__())
|
||||||
SubFuncGen.Gen = self
|
SubFuncGen.Gen = self
|
||||||
self.FuncGen = SubFuncGen
|
self.FuncGen = SubFuncGen
|
||||||
SubClassGenBase: rawptr[ClassGenBase.ClassGenBase] = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
|
SubClassGenBase: ClassGenBase.ClassGenBase* = CompilerFactory.factory.PermPool.alloc(ClassGenBase.ClassGenBase.__sizeof__())
|
||||||
if SubClassGenBase is None:
|
if SubClassGenBase is None:
|
||||||
stdio.printf("FAIL: ClassGenBase alloc failed\n")
|
stdio.printf("FAIL: ClassGenBase alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
|
string.memset(SubClassGenBase, 0, ClassGenBase.ClassGenBase.__sizeof__())
|
||||||
SubClassGenBase.Gen = self
|
SubClassGenBase.Gen = self
|
||||||
self.ClassGenBase = SubClassGenBase
|
self.ClassGenBase = SubClassGenBase
|
||||||
SubClassGen: rawptr[ClassGenMixin.ClassGenMixin] = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
|
SubClassGen: ClassGenMixin.ClassGenMixin* = CompilerFactory.factory.PermPool.alloc(ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||||
if SubClassGen is None:
|
if SubClassGen is None:
|
||||||
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
|
stdio.printf("FAIL: ClassGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
|
string.memset(SubClassGen, 0, ClassGenMixin.ClassGenMixin.__sizeof__())
|
||||||
SubClassGen.Gen = self
|
SubClassGen.Gen = self
|
||||||
self.ClassGen = SubClassGen
|
self.ClassGen = SubClassGen
|
||||||
SubClassGenEnum: rawptr[ClassGenEnum.ClassGenEnum] = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
|
SubClassGenEnum: ClassGenEnum.ClassGenEnum* = CompilerFactory.factory.PermPool.alloc(ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||||
if SubClassGenEnum is None:
|
if SubClassGenEnum is None:
|
||||||
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
|
stdio.printf("FAIL: ClassGenEnum alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
|
string.memset(SubClassGenEnum, 0, ClassGenEnum.ClassGenEnum.__sizeof__())
|
||||||
SubClassGenEnum.Gen = self
|
SubClassGenEnum.Gen = self
|
||||||
self.ClassGenEnum = SubClassGenEnum
|
self.ClassGenEnum = SubClassGenEnum
|
||||||
SubClassGenGeneric: rawptr[ClassGenGeneric.ClassGenGeneric] = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
SubClassGenGeneric: ClassGenGeneric.ClassGenGeneric* = CompilerFactory.factory.PermPool.alloc(ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||||
if SubClassGenGeneric is None:
|
if SubClassGenGeneric is None:
|
||||||
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
|
stdio.printf("FAIL: ClassGenGeneric alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
string.memset(SubClassGenGeneric, 0, ClassGenGeneric.ClassGenGeneric.__sizeof__())
|
||||||
SubClassGenGeneric.Gen = self
|
SubClassGenGeneric.Gen = self
|
||||||
self.ClassGenGeneric = SubClassGenGeneric
|
self.ClassGenGeneric = SubClassGenGeneric
|
||||||
SubClassGenVTable: rawptr[ClassGenVTable.ClassGenVTable] = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
|
SubClassGenVTable: ClassGenVTable.ClassGenVTable* = CompilerFactory.factory.PermPool.alloc(ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||||
if SubClassGenVTable is None:
|
if SubClassGenVTable is None:
|
||||||
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
|
stdio.printf("FAIL: ClassGenVTable alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
|
string.memset(SubClassGenVTable, 0, ClassGenVTable.ClassGenVTable.__sizeof__())
|
||||||
SubClassGenVTable.Gen = self
|
SubClassGenVTable.Gen = self
|
||||||
self.ClassGenVTable = SubClassGenVTable
|
self.ClassGenVTable = SubClassGenVTable
|
||||||
SubClassGenDef: rawptr[ClassGenDef.ClassGenDef] = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
|
SubClassGenDef: ClassGenDef.ClassGenDef* = CompilerFactory.factory.PermPool.alloc(ClassGenDef.ClassGenDef.__sizeof__())
|
||||||
if SubClassGenDef is None:
|
if SubClassGenDef is None:
|
||||||
stdio.printf("FAIL: ClassGenDef alloc failed\n")
|
stdio.printf("FAIL: ClassGenDef alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
|
string.memset(SubClassGenDef, 0, ClassGenDef.ClassGenDef.__sizeof__())
|
||||||
SubClassGenDef.Gen = self
|
SubClassGenDef.Gen = self
|
||||||
self.ClassGenDef = SubClassGenDef
|
self.ClassGenDef = SubClassGenDef
|
||||||
SubImportsGen: rawptr[ImportsGenMixin.ImportsGenMixin] = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
SubImportsGen: ImportsGenMixin.ImportsGenMixin* = CompilerFactory.factory.PermPool.alloc(ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||||
if SubImportsGen is None:
|
if SubImportsGen is None:
|
||||||
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
|
stdio.printf("FAIL: ImportsGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
string.memset(SubImportsGen, 0, ImportsGenMixin.ImportsGenMixin.__sizeof__())
|
||||||
SubImportsGen.Gen = self
|
SubImportsGen.Gen = self
|
||||||
self.ImportsGen = SubImportsGen
|
self.ImportsGen = SubImportsGen
|
||||||
SubStmtGenCif: rawptr[StmtGenCifMixin.StmtGenCifMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
SubStmtGenCif: StmtGenCifMixin.StmtGenCifMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||||
if SubStmtGenCif is None:
|
if SubStmtGenCif is None:
|
||||||
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
|
stdio.printf("FAIL: StmtGenCifMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
string.memset(SubStmtGenCif, 0, StmtGenCifMixin.StmtGenCifMixin.__sizeof__())
|
||||||
SubStmtGenCif.Gen = self
|
SubStmtGenCif.Gen = self
|
||||||
self.StmtGenCif = SubStmtGenCif
|
self.StmtGenCif = SubStmtGenCif
|
||||||
SubStmtGenDecl: rawptr[StmtGenDeclMixin.StmtGenDeclMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
SubStmtGenDecl: StmtGenDeclMixin.StmtGenDeclMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||||
if SubStmtGenDecl is None:
|
if SubStmtGenDecl is None:
|
||||||
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
|
stdio.printf("FAIL: StmtGenDeclMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
string.memset(SubStmtGenDecl, 0, StmtGenDeclMixin.StmtGenDeclMixin.__sizeof__())
|
||||||
SubStmtGenDecl.Gen = self
|
SubStmtGenDecl.Gen = self
|
||||||
self.StmtGenDecl = SubStmtGenDecl
|
self.StmtGenDecl = SubStmtGenDecl
|
||||||
SubStmtGen: rawptr[StmtGenMixin.StmtGenMixin] = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
|
SubStmtGen: StmtGenMixin.StmtGenMixin* = CompilerFactory.factory.PermPool.alloc(StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||||
if SubStmtGen is None:
|
if SubStmtGen is None:
|
||||||
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
|
stdio.printf("FAIL: StmtGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
|
string.memset(SubStmtGen, 0, StmtGenMixin.StmtGenMixin.__sizeof__())
|
||||||
SubStmtGen.Gen = self
|
SubStmtGen.Gen = self
|
||||||
self.StmtGen = SubStmtGen
|
self.StmtGen = SubStmtGen
|
||||||
SubMatchGen: rawptr[MatchGenMixin.MatchGenMixin] = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
|
SubMatchGen: MatchGenMixin.MatchGenMixin* = CompilerFactory.factory.PermPool.alloc(MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||||
if SubMatchGen is None:
|
if SubMatchGen is None:
|
||||||
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
|
stdio.printf("FAIL: MatchGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
|
string.memset(SubMatchGen, 0, MatchGenMixin.MatchGenMixin.__sizeof__())
|
||||||
SubMatchGen.Gen = self
|
SubMatchGen.Gen = self
|
||||||
self.MatchGen = SubMatchGen
|
self.MatchGen = SubMatchGen
|
||||||
SubAssignGen: rawptr[AssignGenMixin.AssignGenMixin] = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
|
SubAssignGen: AssignGenMixin.AssignGenMixin* = CompilerFactory.factory.PermPool.alloc(AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||||
if SubAssignGen is None:
|
if SubAssignGen is None:
|
||||||
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
|
stdio.printf("FAIL: AssignGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
|
string.memset(SubAssignGen, 0, AssignGenMixin.AssignGenMixin.__sizeof__())
|
||||||
SubAssignGen.Gen = self
|
SubAssignGen.Gen = self
|
||||||
self.AssignGen = SubAssignGen
|
self.AssignGen = SubAssignGen
|
||||||
SubVarGen: rawptr[VarGenMixin.VarGenMixin] = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
|
SubVarGen: VarGenMixin.VarGenMixin* = CompilerFactory.factory.PermPool.alloc(VarGenMixin.VarGenMixin.__sizeof__())
|
||||||
if SubVarGen is None:
|
if SubVarGen is None:
|
||||||
stdio.printf("FAIL: VarGenMixin alloc failed\n")
|
stdio.printf("FAIL: VarGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
|
string.memset(SubVarGen, 0, VarGenMixin.VarGenMixin.__sizeof__())
|
||||||
SubVarGen.Gen = self
|
SubVarGen.Gen = self
|
||||||
self.VarGen = SubVarGen
|
self.VarGen = SubVarGen
|
||||||
SubTranslatorGen: rawptr[TranslatorGenMixin.TranslatorGenMixin] = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
SubTranslatorGen: TranslatorGenMixin.TranslatorGenMixin* = CompilerFactory.factory.PermPool.alloc(TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||||
if SubTranslatorGen is None:
|
if SubTranslatorGen is None:
|
||||||
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
|
stdio.printf("FAIL: TranslatorGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
string.memset(SubTranslatorGen, 0, TranslatorGenMixin.TranslatorGenMixin.__sizeof__())
|
||||||
SubTranslatorGen.Gen = self
|
SubTranslatorGen.Gen = self
|
||||||
self.TranslatorGen = SubTranslatorGen
|
self.TranslatorGen = SubTranslatorGen
|
||||||
SubAsyncGen: rawptr[AsyncGenMixin.AsyncGenMixin] = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
SubAsyncGen: AsyncGenMixin.AsyncGenMixin* = CompilerFactory.factory.PermPool.alloc(AsyncGenMixin.AsyncGenMixin.__sizeof__())
|
||||||
if SubAsyncGen is None:
|
if SubAsyncGen is None:
|
||||||
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
|
stdio.printf("FAIL: AsyncGenMixin alloc failed\n")
|
||||||
stdlib.exit(1)
|
stdlib.exit(1)
|
||||||
@@ -444,7 +440,7 @@ novirtual class Generator:
|
|||||||
SubAsyncGen.Gen = self
|
SubAsyncGen.Gen = self
|
||||||
self.AsyncGen = SubAsyncGen
|
self.AsyncGen = SubAsyncGen
|
||||||
|
|
||||||
# ---- 初始化各子对象字段(Init* 方法内联调用) ----
|
# ---- initialize sub-object fields (Init* methods called inline) ----
|
||||||
SubBaseGen.InitBaseGen(pool)
|
SubBaseGen.InitBaseGen(pool)
|
||||||
SubTypeGenBase.InitTypeGen()
|
SubTypeGenBase.InitTypeGen()
|
||||||
SubCallGenBase.InitCallGen()
|
SubCallGenBase.InitCallGen()
|
||||||
@@ -456,7 +452,5 @@ novirtual class Generator:
|
|||||||
SubAssignGen.InitAssignGen()
|
SubAssignGen.InitAssignGen()
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# global generator singleton (composition-architecture compatibility layer)
|
||||||
# 全局生成器单例(组合架构兼容层)
|
|
||||||
# ============================================================
|
|
||||||
_g_gen: Generator* = None
|
_g_gen: Generator* = None
|
||||||
+295
-604
File diff suppressed because it is too large.
Load diff
+1501
-1331
File diff suppressed because it is too large.
Load diff
+1316
-1635
File diff suppressed because it is too large.
Load diff
@@ -1,8 +1,7 @@
|
|||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import stdlib
|
import stdlib
|
||||||
@@ -17,531 +16,419 @@ import lib.core.Gen.GenApi as GenApi
|
|||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# StmtGenDeclMixin - StmtGen 拆分 2/3:声明/枚举/nonlocal 访问
|
# StmtGenDeclMixin - StmtGen split 2/3: declaration/enum/nonlocal access
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 常量
|
# Constants
|
||||||
# ============================================================
|
|
||||||
define ENUM_MAX = 64
|
define ENUM_MAX = 64
|
||||||
define ENUM_MEMBER_MAX = 128
|
define ENUM_MEMBER_MAX = 128
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# StmtGenDeclMixin - 声明/枚举/nonlocal(中层级)
|
# StmtGenDeclMixin - declaration/enum/nonlocal (mid layer)
|
||||||
# ============================================================
|
|
||||||
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||||
"""声明/枚举/nonlocal 访问(StmtGen 拆分 2/3,继承 Cif)"""
|
"""Declaration/enum/nonlocal access (StmtGen split 2/3, extends Cif)"""
|
||||||
|
|
||||||
def translate_global(self, trans: HT.Translator*,
|
def translate_global(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
node: ast.AST*) -> int:
|
"""Translate a global statement and record the global variable names"""
|
||||||
"""翻译 global 语句:记录 global 变量名"""
|
gn: ast.Global* = ast.Global*(node)
|
||||||
gn: rawptr[ast.Global] = rawptr[ast.Global](node)
|
if gn is None: return 0
|
||||||
if gn is None:
|
|
||||||
return 0
|
|
||||||
names: list[ast.AST*]* = gn.names
|
names: list[ast.AST*]* = gn.names
|
||||||
if names is None:
|
if names is None: return 0
|
||||||
return 0
|
|
||||||
n: sizet = len(names)
|
n: sizet = len(names)
|
||||||
for i in range(n):
|
for i in range(int(n)):
|
||||||
nm_node: ast.AST* = names.get(i)
|
nm_node: ast.AST* = names.get(i)
|
||||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||||
nm: ast.Name* = ast.Name*(nm_node)
|
nm: ast.Name* = ast.Name*(nm_node)
|
||||||
if nm.id is not None:
|
if nm.id is not None: GenApi.add_global_name(trans, nm.id)
|
||||||
GenApi.add_global_name(trans, nm.id)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def translate_nonlocal(self, trans: HT.Translator*,
|
def translate_nonlocal(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
node: ast.AST*) -> int:
|
"""Translate a nonlocal statement and record names with their types"""
|
||||||
"""翻译 nonlocal 语句:记录 nonlocal 变量名及其类型
|
nl: ast.Nonlocal* = ast.Nonlocal*(node)
|
||||||
|
if nl is None: return 0
|
||||||
类型信息通过查找父作用域的 alloca 获取(用于 env 指针数组方案的类型恢复)。
|
|
||||||
"""
|
|
||||||
nl: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](node)
|
|
||||||
if nl is None:
|
|
||||||
return 0
|
|
||||||
names: list[ast.AST*]* = nl.names
|
names: list[ast.AST*]* = nl.names
|
||||||
if names is None:
|
if names is None: return 0
|
||||||
return 0
|
|
||||||
n: sizet = len(names)
|
n: sizet = len(names)
|
||||||
for i in range(n):
|
for i in range(int(n)):
|
||||||
nm_node: ast.AST* = names.get(i)
|
nm_node: ast.AST* = names.get(i)
|
||||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||||
nm: ast.Name* = ast.Name*(nm_node)
|
nm: ast.Name* = ast.Name*(nm_node)
|
||||||
if nm.id is not None:
|
if nm.id is not None:
|
||||||
# 查找父作用域中的变量 alloca,获取类型信息
|
# Look up the variable alloca in the parent scope to get its type
|
||||||
var_ty: llvmlite.LLVMType* = None
|
var_ty: mlirlite.LLVMType* = None
|
||||||
var_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
var_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||||
if var_alloca is not None and var_alloca.Ty is not None:
|
if var_alloca is not None and var_alloca.Ty is not None:
|
||||||
var_ty = var_alloca.Ty.Pointee
|
var_ty = var_alloca.Ty.Pointee
|
||||||
# Void 类型视为无效(Pointee 访问异常时可能返回 Void),
|
# Treat Void as invalid (Pointee access can return Void); report and abort
|
||||||
# 类型无效直接报错终止(fail-fast),避免生成
|
# rather than emit illegal IR like "bitcast ... to void*" / "load void, void*".
|
||||||
# "bitcast ... to void*" / "load void, void*" 非法 IR
|
|
||||||
if var_ty is not None:
|
if var_ty is not None:
|
||||||
match var_ty:
|
match var_ty:
|
||||||
case llvmlite.LLVMType.Void():
|
case mlirlite.LLVMType.Void():
|
||||||
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal 变量 '{nm.id}' 类型无效(Void/无法解析)", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(nm_node, f"nonlocal variable {nm.id} has an invalid type (Void/unresolvable)", "TypeError")
|
||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
GenApi.add_nonlocal_name(trans, nm.id, var_ty)
|
GenApi.add_nonlocal_name(trans, nm.id, var_ty)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def translate_expr_stmt(self, trans: HT.Translator*,
|
def translate_expr_stmt(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
node: ast.AST*) -> int:
|
"""Translate an expression statement (e.g. a printf call)"""
|
||||||
"""翻译表达式语句(printf 调用等)"""
|
ex: ast.Expr* = ast.Expr*(node)
|
||||||
ex: rawptr[ast.Expr] = rawptr[ast.Expr](node)
|
if ex is None: return 0
|
||||||
if ex is None:
|
|
||||||
return 0
|
|
||||||
call_node: ast.AST* = ex.value
|
call_node: ast.AST* = ex.value
|
||||||
if call_node is None:
|
if call_node is None: return 0
|
||||||
return 0
|
|
||||||
ck: int = call_node.kind()
|
ck: int = call_node.kind()
|
||||||
if ck != ast.ASTKind.Call:
|
if ck != ast.ASTKind.Call:
|
||||||
# REPL 模式(M2):片段函数本体内的裸表达式语句(如 1+2 / x / x + 1)
|
# REPL mode (M2): bare expression statements in a fragment body (e.g. 1+2 / x / x + 1)
|
||||||
# 求值后写入 ctx 结果区,由宿主回显。调用表达式仍走下方原路径
|
# are written to the ctx result area. Guarded by trans.ReplCtx; non-REPL behavior unchanged.
|
||||||
# (执行 + 丢弃返回值,同 print/printf 语义)。
|
if trans is not None and trans.ReplCtx is not None and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0: self.Gen.VarGen.repl_expr_result(trans, call_node)
|
||||||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ "表达式语句除调用外一律忽略"
|
|
||||||
# 的原行为逐字不变。
|
|
||||||
if trans is not None and trans.ReplCtx is not None \
|
|
||||||
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
|
|
||||||
self.Gen.VarGen.repl_expr_result(trans, call_node)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
cl: ast.Call* = ast.Call*(call_node)
|
cl: ast.Call* = ast.Call*(call_node)
|
||||||
func_node: ast.AST* = cl.func
|
func_node: ast.AST* = cl.func
|
||||||
if func_node is None:
|
if func_node is None: return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
|
func_name: str = self.Gen.ExprGenBase.get_func_name(func_node)
|
||||||
if func_name is None:
|
if func_name is None:
|
||||||
# 函数名解析失败:直接报错终止(fail-fast)
|
# Function name resolution failed: report and abort (fail-fast)
|
||||||
self.Gen.TypeGenErr.fatal_error(call_node, "表达式语句的函数名解析失败", "SyntaxError")
|
self.Gen.TypeGenErr.fatal_error(call_node, "failed to resolve the function name of the expression statement", "SyntaxError")
|
||||||
|
|
||||||
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
|
# printf / print take special paths (print is type-aware formatting, not mapped to printf)
|
||||||
if string.strcmp(func_name, "printf") == 0:
|
if string.strcmp(func_name, "printf") == 0: self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||||
self.Gen.CallGenLeafKit.translate_printf_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
elif string.strcmp(func_name, "print") == 0: self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
||||||
elif string.strcmp(func_name, "print") == 0:
|
|
||||||
self.Gen.CallGenLeafKit.translate_print_call(trans.Pool, trans._cur_builder, trans.Module, cl, trans)
|
|
||||||
else:
|
else:
|
||||||
# 通用函数调用(返回值丢弃)
|
# Generic call (result discarded). M3: a bare-name call to a session function also
|
||||||
# M3:裸名调用**会话函数**(跨片段)时额外把返回值写进 ctx 结果区回显
|
# writes the result to ctx (classified by the session table's return token).
|
||||||
# (M2 的限制条目"调用表达式的结果不回显"对会话函数放开——用户在
|
if trans is not None and trans.ReplCtx is not None and func_node.kind() == ast.ASTKind.Name and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
|
||||||
# `>>> f(41)` 后期望看到 `=> 42`)。结果区的类型分类直接用会话函数表里
|
sfn_ent: GenTypes.ReplFuncEntry* = self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
|
||||||
# 记录的返回类型 token(宿主从定义片段 IR 解析而来,是定义端 ABI 真相),
|
|
||||||
# 不用影子条目推断 ⇒ 与调用点签名同源。
|
|
||||||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 原"执行 + 丢弃返回值"逐字不变;
|
|
||||||
# 仅裸名(Name)且表中登记的会话函数走本分支(printf/print 等在上方已分流)。
|
|
||||||
if trans is not None and trans.ReplCtx is not None \
|
|
||||||
and func_node.kind() == ast.ASTKind.Name \
|
|
||||||
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
|
|
||||||
sfn_ent: rawptr[GenTypes.ReplFuncEntry] = \
|
|
||||||
self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
|
|
||||||
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
|
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
|
||||||
sfn_v: llvmlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node,
|
sfn_v: mlirlite.Value* = self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
|
||||||
trans._funcs, trans._func_count, trans)
|
|
||||||
if sfn_v is not None:
|
if sfn_v is not None:
|
||||||
ctx_v: llvmlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
|
ctx_v: mlirlite.Value* = self.Gen.VarGen.repl_ctx_ptr(trans, trans.ReplCtx, call_node)
|
||||||
if ctx_v is not None:
|
if ctx_v is not None:
|
||||||
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits,
|
self.Gen.VarGen.repl_emit_result(trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits, call_node)
|
||||||
call_node)
|
|
||||||
return 0
|
return 0
|
||||||
self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
|
self.Gen.CallGen.translate_call(trans.Pool, trans._cur_builder, trans.Module, call_node, trans._funcs, trans._func_count, trans)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def pre_scan_allocas(self, trans: HT.Translator*,
|
def pre_scan_allocas(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
node: ast.AST*) -> int:
|
"""Pre-scan AnnAssign statements and create their entry allocas early"""
|
||||||
"""预扫描语句中的 AnnAssign,提前创建 alloca
|
if node is None: return 0
|
||||||
|
|
||||||
递归分支/循环/异常体:if/else/for/while/try 分支内的 AnnAssign 局部
|
|
||||||
也必须提前注册到 entry alloca——否则闭包隐式捕获扫描(step 8c)
|
|
||||||
在分支局部未注册时会误判为外层函数捕获(Terminal _build_path 的
|
|
||||||
i/j/cwd_len 被当 nonlocal 捕获 → 循环计数器走 env/父函数存储 →
|
|
||||||
跨函数 SSA 错乱,llc "instruction expected to be numbered")。
|
|
||||||
|
|
||||||
返回新增的变量数(调用方均忽略返回值)。
|
|
||||||
"""
|
|
||||||
if node is None:
|
|
||||||
return 0
|
|
||||||
k: int = node.kind()
|
k: int = node.kind()
|
||||||
if k == ast.ASTKind.AnnAssign:
|
if k == ast.ASTKind.AnnAssign: return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
|
||||||
return self.Gen.AssignGen.translate_annassign_prescan(trans, node)
|
|
||||||
if k == ast.ASTKind.If:
|
if k == ast.ASTKind.If:
|
||||||
ifn: rawptr[ast.If] = rawptr[ast.If](node)
|
ifn: ast.If* = ast.If*(node)
|
||||||
if ifn.children is not None:
|
if ifn.children is not None:
|
||||||
bn_pre_i: sizet = len(ifn.children)
|
bn_pre_i: sizet = len(ifn.children)
|
||||||
for bi_pre_i in range(bn_pre_i):
|
for bi_pre_i in range(int(bn_pre_i)): self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
||||||
self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
|
||||||
if ifn.orelse is not None:
|
if ifn.orelse is not None:
|
||||||
bn_pre_e: sizet = len(ifn.orelse)
|
bn_pre_e: sizet = len(ifn.orelse)
|
||||||
for bi_pre_e in range(bn_pre_e):
|
for bi_pre_e in range(int(bn_pre_e)): self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
||||||
self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
|
||||||
return 0
|
return 0
|
||||||
if k == ast.ASTKind.For:
|
if k == ast.ASTKind.For:
|
||||||
for_n: ast.For* = ast.For*(node)
|
for_n: ast.For* = ast.For*(node)
|
||||||
if for_n.children is not None:
|
if for_n.children is not None:
|
||||||
bn_pre_f: sizet = len(for_n.children)
|
bn_pre_f: sizet = len(for_n.children)
|
||||||
for bi_pre_f in range(bn_pre_f):
|
for bi_pre_f in range(int(bn_pre_f)): self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
||||||
self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
|
||||||
if for_n.orelse is not None:
|
if for_n.orelse is not None:
|
||||||
bn_pre_fe: sizet = len(for_n.orelse)
|
bn_pre_fe: sizet = len(for_n.orelse)
|
||||||
for bi_pre_fe in range(bn_pre_fe):
|
for bi_pre_fe in range(int(bn_pre_fe)): self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
||||||
self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
|
||||||
return 0
|
return 0
|
||||||
if k == ast.ASTKind.While:
|
if k == ast.ASTKind.While:
|
||||||
wh_n: rawptr[ast.While] = rawptr[ast.While](node)
|
wh_n: ast.While* = ast.While*(node)
|
||||||
if wh_n.children is not None:
|
if wh_n.children is not None:
|
||||||
bn_pre_w: sizet = len(wh_n.children)
|
bn_pre_w: sizet = len(wh_n.children)
|
||||||
for bi_pre_w in range(bn_pre_w):
|
for bi_pre_w in range(int(bn_pre_w)): self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
||||||
self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
|
||||||
if wh_n.orelse is not None:
|
if wh_n.orelse is not None:
|
||||||
bn_pre_we: sizet = len(wh_n.orelse)
|
bn_pre_we: sizet = len(wh_n.orelse)
|
||||||
for bi_pre_we in range(bn_pre_we):
|
for bi_pre_we in range(int(bn_pre_we)): self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
||||||
self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
|
||||||
return 0
|
return 0
|
||||||
if k == ast.ASTKind.Try:
|
if k == ast.ASTKind.Try:
|
||||||
tr_n: ast.Try* = ast.Try*(node)
|
tr_n: ast.Try* = ast.Try*(node)
|
||||||
if tr_n.children is not None:
|
if tr_n.children is not None:
|
||||||
bn_pre_t: sizet = len(tr_n.children)
|
bn_pre_t: sizet = len(tr_n.children)
|
||||||
for bi_pre_t in range(bn_pre_t):
|
for bi_pre_t in range(int(bn_pre_t)): self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
||||||
self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
|
||||||
if tr_n.handlers is not None:
|
if tr_n.handlers is not None:
|
||||||
bn_pre_th: sizet = len(tr_n.handlers)
|
bn_pre_th: sizet = len(tr_n.handlers)
|
||||||
for bi_pre_th in range(bn_pre_th):
|
for bi_pre_th in range(int(bn_pre_th)):
|
||||||
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
|
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
|
||||||
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
|
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
|
||||||
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
|
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
|
||||||
if ehc_pre.children is not None:
|
if ehc_pre.children is not None:
|
||||||
bn_pre_ehb: sizet = len(ehc_pre.children)
|
bn_pre_ehb: sizet = len(ehc_pre.children)
|
||||||
for bi_pre_ehb in range(bn_pre_ehb):
|
for bi_pre_ehb in range(int(bn_pre_ehb)): self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
||||||
self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
|
||||||
if tr_n.orelse is not None:
|
if tr_n.orelse is not None:
|
||||||
bn_pre_to: sizet = len(tr_n.orelse)
|
bn_pre_to: sizet = len(tr_n.orelse)
|
||||||
for bi_pre_to in range(bn_pre_to):
|
for bi_pre_to in range(int(bn_pre_to)): self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
||||||
self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
|
||||||
if tr_n.finalbody is not None:
|
if tr_n.finalbody is not None:
|
||||||
bn_pre_tf: sizet = len(tr_n.finalbody)
|
bn_pre_tf: sizet = len(tr_n.finalbody)
|
||||||
for bi_pre_tf in range(bn_pre_tf):
|
for bi_pre_tf in range(int(bn_pre_tf)): self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
||||||
self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
|
||||||
return 0
|
return 0
|
||||||
if k == ast.ASTKind.With:
|
if k == ast.ASTKind.With:
|
||||||
wth: ast.With* = ast.With*(node)
|
wth: ast.With* = ast.With*(node)
|
||||||
if wth.children is not None:
|
if wth.children is not None:
|
||||||
bn_pre_wb: sizet = len(wth.children)
|
bn_pre_wb: sizet = len(wth.children)
|
||||||
for bi_pre_wb in range(bn_pre_wb):
|
for bi_pre_wb in range(int(bn_pre_wb)): self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
||||||
self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
|
||||||
return 0
|
return 0
|
||||||
if k == ast.ASTKind.Match:
|
if k == ast.ASTKind.Match:
|
||||||
mt_n: ast.Match* = ast.Match*(node)
|
mt_n: ast.Match* = ast.Match*(node)
|
||||||
if mt_n.cases is not None:
|
if mt_n.cases is not None:
|
||||||
bn_pre_mc: sizet = len(mt_n.cases)
|
bn_pre_mc: sizet = len(mt_n.cases)
|
||||||
for bi_pre_mc in range(bn_pre_mc):
|
for bi_pre_mc in range(int(bn_pre_mc)):
|
||||||
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
|
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
|
||||||
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
|
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
|
||||||
mcc_pre: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](mc_pre)
|
mcc_pre: ast.MatchCase* = ast.MatchCase*(mc_pre)
|
||||||
if mcc_pre.children is not None:
|
if mcc_pre.children is not None:
|
||||||
bn_pre_mcb: sizet = len(mcc_pre.children)
|
bn_pre_mcb: sizet = len(mcc_pre.children)
|
||||||
for bi_pre_mcb in range(bn_pre_mcb):
|
for bi_pre_mcb in range(int(bn_pre_mcb)): self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
||||||
self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
|
||||||
return 0
|
return 0
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def pre_scan_scope_decls(self, trans: HT.Translator*,
|
def pre_scan_scope_decls(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
node: ast.AST*) -> int:
|
"""Pre-scan Global/Nonlocal declarations and register scope names early"""
|
||||||
"""预扫描 Global/Nonlocal 声明,提前注册作用域名称"""
|
if node is None: return 0
|
||||||
if node is None:
|
|
||||||
return 0
|
|
||||||
k: int = node.kind()
|
k: int = node.kind()
|
||||||
if k == ast.ASTKind.Global:
|
if k == ast.ASTKind.Global: return self.translate_global(trans, node)
|
||||||
return self.translate_global(trans, node)
|
if k == ast.ASTKind.Nonlocal: return self.translate_nonlocal(trans, node)
|
||||||
if k == ast.ASTKind.Nonlocal:
|
|
||||||
return self.translate_nonlocal(trans, node)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def translate_break(self, trans: HT.Translator*) -> int:
|
def translate_break(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
"""翻译 break 语句:跳转到循环 end 块"""
|
"""Translate a break statement: branch to the break target"""
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
func: llvmlite.Function* = trans._cur_func
|
func: mlirlite.Function* = trans._cur_func
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
|
|
||||||
if builder is None or func is None:
|
if builder is None or func is None: return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
# 发射 br 到 break 目标
|
# Emit br to the break target (_break_bb set by the loop / switch / match case side)
|
||||||
if trans._break_bb is not None:
|
if trans._break_bb is None:
|
||||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
# fail-fast: with no loop / switch / match there is nowhere to jump; never silently no-op
|
||||||
llvmlite.build_br(builder, trans._break_bb)
|
self.Gen.TypeGenErr.fatal_error(node, "break is only allowed inside a loop or a switch/match case body (there is no target to break out of)", "SyntaxError")
|
||||||
|
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._break_bb)
|
||||||
|
|
||||||
# 创建死代码 BB,用于后续语句(break 后面的代码不可达)
|
# Create a dead-code BB for following statements (code after break is unreachable)
|
||||||
cnt: int = trans._label_counter
|
cnt: int = trans._label_counter
|
||||||
trans._label_counter = cnt + 1
|
trans._label_counter = cnt + 1
|
||||||
name_buf: str = pool.alloc(32)
|
name_buf: str = pool.alloc(32)
|
||||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||||
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||||
llvmlite.position_at_end(builder, dead_bb)
|
mlirlite.position_at_end(builder, dead_bb)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def translate_continue(self, trans: HT.Translator*) -> int:
|
def translate_fallthrough(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
"""翻译 continue 语句:跳转到循环 cond/incr 块"""
|
"""Translate a fallthrough statement: emits no IR, consumed by the switch side"""
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
if trans is None: return 0
|
||||||
func: llvmlite.Function* = trans._cur_func
|
if trans._in_switch_case == 0:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(node, "fallthrough is only allowed inside a switch case body (fallthrough is completely forbidden in match; " "the semantics are falling through to the next case)", "SyntaxError")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def translate_continue(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||||
|
"""Translate a continue statement: branch to the loop cond/incr block"""
|
||||||
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
|
func: mlirlite.Function* = trans._cur_func
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
|
|
||||||
if builder is None or func is None:
|
if builder is None or func is None: return 0
|
||||||
return 0
|
|
||||||
|
|
||||||
# 发射 br 到 continue 目标
|
# Emit br to the continue target
|
||||||
if trans._continue_bb is not None:
|
if trans._continue_bb is None:
|
||||||
if llvmlite.builder_cur_block_is_terminated(builder) == 0:
|
# fail-fast: a continue outside a loop has nowhere to jump; never silently no-op
|
||||||
llvmlite.build_br(builder, trans._continue_bb)
|
self.Gen.TypeGenErr.fatal_error(node, "continue is only allowed inside a loop body (there is no enclosing loop to continue)", "SyntaxError")
|
||||||
|
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, trans._continue_bb)
|
||||||
|
|
||||||
# 创建死代码 BB,用于后续语句
|
# Create a dead-code BB for following statements
|
||||||
cnt: int = trans._label_counter
|
cnt: int = trans._label_counter
|
||||||
trans._label_counter = cnt + 1
|
trans._label_counter = cnt + 1
|
||||||
name_buf: str = pool.alloc(32)
|
name_buf: str = pool.alloc(32)
|
||||||
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
viperlib.snprintf(name_buf, 32, "dead.%d", cnt)
|
||||||
dead_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
dead_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||||
llvmlite.position_at_end(builder, dead_bb)
|
mlirlite.position_at_end(builder, dead_bb)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_stmt_kind_name(self, node: ast.AST*) -> str:
|
def get_stmt_kind_name(self, node: ast.AST*) -> str:
|
||||||
"""获取语句类型名"""
|
"""Return the statement type name"""
|
||||||
if node is None:
|
if node is None: return None
|
||||||
return None
|
|
||||||
return node.type_name()
|
return node.type_name()
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 簇 D:nonlocal/闭包 env 访问(迁移自 HandlesNonlocal.vp)
|
# Cluster D: nonlocal/closure env access (migrated from HandlesNonlocal.vp)
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def _is_void_type(self, ty: llvmlite.LLVMType*) -> int:
|
def _is_void_type(self, ty: mlirlite.LLVMType*) -> int:
|
||||||
"""判断是否为 Void 类型,是则返回 1,否则 0"""
|
"""Return 1 if the type is Void, else 0"""
|
||||||
if ty is None:
|
if ty is None: return 0
|
||||||
return 0
|
|
||||||
match ty:
|
match ty:
|
||||||
case llvmlite.LLVMType.Void():
|
case mlirlite.LLVMType.Void():
|
||||||
return 1
|
return 1
|
||||||
case _:
|
case _:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_env_ptr_var(self, trans: HT.Translator*) -> llvmlite.Value*:
|
def get_env_ptr_var(self, trans: HT.Translator*) -> mlirlite.Value*:
|
||||||
"""获取或创建 _env_ptr 变量(存储闭包 env 指针)"""
|
"""Get or create the _env_ptr variable holding the closure env pointer"""
|
||||||
env_var: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
|
env_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "_env_ptr")
|
||||||
if env_var is not None:
|
if env_var is not None: return env_var
|
||||||
return env_var
|
# Create an alloca to store env
|
||||||
# 创建 alloca 存储 env
|
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
alloca: llvmlite.Value* = llvmlite.build_alloca(builder, i8_ptr_ty)
|
alloca: mlirlite.Value* = mlirlite.build_alloca(builder, i8_ptr_ty)
|
||||||
if alloca is None:
|
if alloca is None: return None
|
||||||
return None
|
|
||||||
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
|
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", alloca)
|
||||||
return alloca
|
return alloca
|
||||||
|
|
||||||
def _load_raw_var_ptr(self, trans: HT.Translator*,
|
def _load_raw_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||||
name: str) -> llvmlite.Value*:
|
"""Load the raw pointer (i8*) of a nonlocal variable from the closure env"""
|
||||||
"""从闭包 env 加载 nonlocal 变量的原始指针(i8*)
|
|
||||||
|
|
||||||
返回 i8* 类型的 Value,调用方需 bitcast 为具体类型指针后 load/store。
|
|
||||||
"""
|
|
||||||
idx: int = GenApi.get_nonlocal_index(trans, name)
|
idx: int = GenApi.get_nonlocal_index(trans, name)
|
||||||
if idx < 0:
|
if idx < 0: return None
|
||||||
return None
|
|
||||||
|
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||||
i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||||||
i8_ptr_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ptr_ty)
|
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||||
|
|
||||||
# 1. 加载 env_ptr
|
# 1. Load env_ptr
|
||||||
env_alloca: llvmlite.Value* = self.get_env_ptr_var(trans)
|
env_alloca: mlirlite.Value* = self.get_env_ptr_var(trans)
|
||||||
if env_alloca is None:
|
if env_alloca is None: return None
|
||||||
return None
|
env_ptr: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty, env_alloca)
|
||||||
env_ptr: llvmlite.Value* = llvmlite.build_load(builder, i8_ptr_ty, env_alloca)
|
if env_ptr is None: return None
|
||||||
if env_ptr is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 2. GEP to offset (idx * 8) — env 存储指针数组,每槽 8 字节
|
# 2. GEP to offset (idx * 8) - env is an array of pointers, 8 bytes per slot
|
||||||
offset_val: llvmlite.Value* = llvmlite.const_int32(pool, idx * 8)
|
offset_val: mlirlite.Value* = mlirlite.const_int32(pool, idx * 8)
|
||||||
addr: llvmlite.Value* = llvmlite.build_gep(builder, i8_ty, env_ptr, offset_val)
|
addr: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, env_ptr, offset_val)
|
||||||
if addr is None:
|
if addr is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 3. Bitcast to i8** and load the raw pointer
|
# 3. Bitcast to i8** and load the raw pointer
|
||||||
ptr_addr: llvmlite.Value* = llvmlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
|
ptr_addr: mlirlite.Value* = mlirlite.build_bitcast(builder, addr, i8_ptr_ptr_ty)
|
||||||
if ptr_addr is None:
|
if ptr_addr is None: return None
|
||||||
return None
|
return mlirlite.build_load(builder, i8_ptr_ty, ptr_addr)
|
||||||
return llvmlite.build_load(builder, i8_ptr_ty, ptr_addr)
|
|
||||||
|
|
||||||
def load_nonlocal_var(self, trans: HT.Translator*,
|
def load_nonlocal_var(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||||
name: str) -> llvmlite.Value*:
|
"""Load the value of a nonlocal variable from the closure env"""
|
||||||
"""从闭包 env 加载 nonlocal 变量值
|
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||||
|
if raw_ptr is None: return None
|
||||||
通过 HT.get_nonlocal_type 获取变量类型,bitcast 原始指针后 load。
|
|
||||||
"""
|
|
||||||
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
|
|
||||||
if raw_ptr is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
|
|
||||||
# 获取变量类型(从注册时存储的类型信息)
|
# Get the variable type (from the type info stored at registration)
|
||||||
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "load void, void*" 非法 IR
|
# fail-fast on a missing/invalid type, avoiding illegal IR like "load void, void*"
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
|
||||||
|
|
||||||
# bitcast raw i8* to var_ty*
|
# bitcast raw i8* to var_ty*
|
||||||
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
|
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
|
||||||
var_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
var_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||||
if var_ptr is None:
|
if var_ptr is None: return None
|
||||||
return None
|
return mlirlite.build_load(builder, var_ty, var_ptr)
|
||||||
return llvmlite.build_load(builder, var_ty, var_ptr)
|
|
||||||
|
|
||||||
def get_nonlocal_var_ptr(self, trans: HT.Translator*,
|
def get_nonlocal_var_ptr(self, trans: HT.Translator*, name: str) -> mlirlite.Value*:
|
||||||
name: str) -> llvmlite.Value*:
|
"""Get the storage pointer (var_ty*) of a nonlocal variable for store operations"""
|
||||||
"""获取 nonlocal 变量的存储指针(var_ty*),用于 store 操作
|
raw_ptr: mlirlite.Value* = self._load_raw_var_ptr(trans, name)
|
||||||
|
if raw_ptr is None: return None
|
||||||
返回的指针指向父函数的 alloca,store 操作对父函数可见。
|
|
||||||
"""
|
|
||||||
raw_ptr: llvmlite.Value* = self._load_raw_var_ptr(trans, name)
|
|
||||||
if raw_ptr is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
pool: memhub.MemBuddy* = trans.Pool
|
pool: memhub.MemBuddy* = trans.Pool
|
||||||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||||
|
|
||||||
# 获取变量类型
|
# Get the variable type
|
||||||
var_ty: llvmlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
var_ty: mlirlite.LLVMType* = GenApi.get_nonlocal_type(trans, name)
|
||||||
if var_ty is None or self._is_void_type(var_ty) != 0:
|
if var_ty is None or self._is_void_type(var_ty) != 0:
|
||||||
# 类型缺失/无效直接报错终止(fail-fast),避免生成 "bitcast ... to void*" 非法 IR
|
# Missing/invalid type aborts (fail-fast) to avoid illegal "bitcast ... to void*" IR
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法 load", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal variable {name} is missing its type; cannot load", "TypeError")
|
||||||
|
|
||||||
# bitcast raw i8* to var_ty*
|
# bitcast raw i8* to var_ty*
|
||||||
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
|
var_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, var_ty)
|
||||||
return llvmlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
return mlirlite.build_bitcast(builder, raw_ptr, var_ptr_ty)
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 簇 E:枚举注册表(迁移自 HandlesEnum.vp)
|
# Cluster E: enum registry (migrated from HandlesEnum.vp)
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
|
def init_enum_table(self, pool: memhub.MemBuddy*) -> int:
|
||||||
"""初始化枚举注册表,返回 1 成功
|
"""Initialize the enum registry; returns 1 on success"""
|
||||||
|
if self.EnumTable is not None: return 1
|
||||||
表用 stdlib 分配(跨 Phase1 1b 的 mark/reset_to 存活——枚举表是
|
|
||||||
跨文件持久表,pool 分配会被 reset 回收导致 Phase B 读垃圾,
|
|
||||||
enum_count 出现 512 等垃圾值 / entry.Name 解引用崩溃)。
|
|
||||||
"""
|
|
||||||
if self.EnumTable is not None:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||||
self.EnumTable = CompilerFactory.factory.PermPool.alloc(entry_size * ENUM_MAX)
|
self.EnumTable = CompilerFactory.factory.PermPool.alloc(entry_size * ENUM_MAX)
|
||||||
if self.EnumTable is None:
|
if self.EnumTable is None: return 0
|
||||||
return 0
|
|
||||||
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
|
string.memset(self.EnumTable, 0, entry_size * ENUM_MAX)
|
||||||
self.EnumCount = 0
|
self.EnumCount = 0
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def _get_enum_entry(self, i: int) -> rawptr[GenTypes.EnumEntry]:
|
def _get_enum_entry(self, i: int) -> GenTypes.EnumEntry*:
|
||||||
"""获取第 i 个枚举条目"""
|
"""Get the i-th enum entry"""
|
||||||
if self.EnumTable is None or i < 0 or i >= ENUM_MAX:
|
if self.EnumTable is None or i < 0 or i >= ENUM_MAX: return None
|
||||||
return None
|
|
||||||
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
entry_size: sizet = GenTypes.EnumEntry.__sizeof__()
|
||||||
addr: uint64 = uint64(self.EnumTable) + i * entry_size
|
addr: uint64 = uint64(self.EnumTable) + i * entry_size
|
||||||
return rawptr[GenTypes.EnumEntry](addr)
|
return GenTypes.EnumEntry*(addr)
|
||||||
|
|
||||||
def _get_enum_member(self, enum_entry: rawptr[GenTypes.EnumEntry],
|
def _get_enum_member(self, enum_entry: GenTypes.EnumEntry*, i: int) -> GenTypes.EnumMember*:
|
||||||
i: int) -> rawptr[GenTypes.EnumMember]:
|
"""Get the i-th enum member entry"""
|
||||||
"""获取枚举中第 i 个成员条目"""
|
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX: return None
|
||||||
if enum_entry is None or i < 0 or i >= ENUM_MEMBER_MAX:
|
|
||||||
return None
|
|
||||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||||
addr: uint64 = uint64(enum_entry.Members) + i * member_size
|
addr: uint64 = uint64(enum_entry.Members) + i * member_size
|
||||||
return rawptr[GenTypes.EnumMember](addr)
|
return GenTypes.EnumMember*(addr)
|
||||||
|
|
||||||
def register_enum(self, pool: memhub.MemBuddy*,
|
def register_enum(self, pool: memhub.MemBuddy*, name: str, base_ty: mlirlite.LLVMType*) -> GenTypes.EnumEntry*:
|
||||||
name: str,
|
"""Register an enum type and return its EnumEntry pointer"""
|
||||||
base_ty: llvmlite.LLVMType*) -> rawptr[GenTypes.EnumEntry]:
|
if self.init_enum_table(pool) == 0: return None
|
||||||
"""注册枚举类型,返回 EnumEntry 指针"""
|
|
||||||
if self.init_enum_table(pool) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 检查是否已注册
|
# Check whether it is already registered
|
||||||
existing: rawptr[GenTypes.EnumEntry] = self.find_enum(name)
|
existing: GenTypes.EnumEntry* = self.find_enum(name)
|
||||||
if existing is not None:
|
if existing is not None: return existing
|
||||||
return existing
|
|
||||||
|
|
||||||
if self.EnumCount >= ENUM_MAX:
|
if self.EnumCount >= ENUM_MAX:
|
||||||
VLogger.error(f"table full, cannot register {name}", "ENUM")
|
VLogger.error(f"table full, cannot register {name}", "ENUM")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(self.EnumCount)
|
entry: GenTypes.EnumEntry* = self._get_enum_entry(self.EnumCount)
|
||||||
if entry is None:
|
if entry is None: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 分配成员数组(永久池:跨 mark/reset 存活,对齐 EnumTable 永久池化)
|
# Allocate the member array (permanent pool: survives mark/reset like EnumTable)
|
||||||
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
member_size: sizet = GenTypes.EnumMember.__sizeof__()
|
||||||
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
|
entry.Members = CompilerFactory.factory.PermPool.alloc(member_size * ENUM_MEMBER_MAX)
|
||||||
if entry.Members is None:
|
if entry.Members is None: return None
|
||||||
return None
|
|
||||||
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
|
string.memset(entry.Members, 0, member_size * ENUM_MEMBER_MAX)
|
||||||
|
|
||||||
# 复制类名(永久池深拷贝)
|
# Copy the class name (permanent-pool deep copy)
|
||||||
name_len: sizet = string.strlen(name)
|
name_len: sizet = string.strlen(name)
|
||||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||||
if name_buf is not None:
|
if name_buf is not None:
|
||||||
string.strcpy(name_buf, name)
|
string.strcpy(name_buf, name)
|
||||||
entry.Name = name_buf
|
entry.Name = name_buf
|
||||||
|
|
||||||
# BaseTy 深拷贝:LLVMType 是 pool 分配,StdlibAlloc=1 时 reset 回收悬空 → Phase B
|
# Deep-copy BaseTy: LLVMType is pool-allocated and would dangle after reset under
|
||||||
# 枚举访问 ConstInt(悬空 BaseTy) 生成 void 值(llc 报错)。Phase1 1a(mark1 前)
|
# StdlibAlloc=1. StdlibAlloc is a StructGenMixin field, so delegate via the module.
|
||||||
# 保持 pool 存活,无需拷贝。
|
|
||||||
# 注意:StdlibAlloc 是 StructGenMixin 字段,本 Mixin 方法体不能直接 self.StdlibAlloc
|
|
||||||
# (NoVTable 按本 Mixin 字段布局编译),走模块级委托。
|
|
||||||
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
if self.Gen.StructGen.get_stdlib_alloc() != 0:
|
||||||
base_ty_copy: llvmlite.LLVMType* = llvmlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
|
base_ty_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, base_ty)
|
||||||
if base_ty_copy is not None:
|
if base_ty_copy is not None: entry.BaseTy = base_ty_copy
|
||||||
entry.BaseTy = base_ty_copy
|
else: entry.BaseTy = base_ty
|
||||||
else:
|
else: entry.BaseTy = base_ty
|
||||||
entry.BaseTy = base_ty
|
|
||||||
else:
|
|
||||||
entry.BaseTy = base_ty
|
|
||||||
entry.MemberCount = 0
|
entry.MemberCount = 0
|
||||||
|
|
||||||
self.EnumCount += 1
|
self.EnumCount += 1
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def add_enum_member(self, pool: memhub.MemBuddy*,
|
def add_enum_member(self, pool: memhub.MemBuddy*, enum_entry: GenTypes.EnumEntry*, member_name: str, member_val: int64, member_ty: mlirlite.LLVMType*) -> int:
|
||||||
enum_entry: rawptr[GenTypes.EnumEntry],
|
"""Add a member to an enum; returns the member index or -1 on failure"""
|
||||||
member_name: str,
|
if enum_entry is None or member_name is None or member_ty is None: return -1
|
||||||
member_val: int64,
|
|
||||||
member_ty: llvmlite.LLVMType*) -> int:
|
|
||||||
"""向枚举添加成员,返回成员索引(-1 失败)"""
|
|
||||||
if enum_entry is None or member_name is None or member_ty is None:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
# 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块
|
# Dedup: skip an existing member with the same name (cross-module enum merging, e.g.
|
||||||
# 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT)
|
# win32.fileio and fat32_types both define FRESULT).
|
||||||
for ei_d in range(enum_entry.MemberCount):
|
for ei_d in range(int(enum_entry.MemberCount)):
|
||||||
ex_d: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, ei_d)
|
ex_d: GenTypes.EnumMember* = self._get_enum_member(enum_entry, ei_d)
|
||||||
if ex_d is not None and ex_d.Name is not None:
|
if ex_d is not None and ex_d.Name is not None:
|
||||||
if string.strcmp(ex_d.Name, member_name) == 0:
|
if string.strcmp(ex_d.Name, member_name) == 0: return ei_d
|
||||||
return ei_d
|
|
||||||
|
|
||||||
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
||||||
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
|
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
idx: int = enum_entry.MemberCount
|
idx: int = enum_entry.MemberCount
|
||||||
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(enum_entry, idx)
|
me: GenTypes.EnumMember* = self._get_enum_member(enum_entry, idx)
|
||||||
if me is None:
|
if me is None: return -1
|
||||||
return -1
|
|
||||||
|
|
||||||
# 复制成员名(永久池深拷贝:成员表跨 mark/reset 存活)
|
# Copy the member name (permanent-pool deep copy: the member table survives mark/reset)
|
||||||
name_len: sizet = string.strlen(member_name)
|
name_len: sizet = string.strlen(member_name)
|
||||||
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
name_buf: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
|
||||||
if name_buf is not None:
|
if name_buf is not None:
|
||||||
@@ -554,43 +441,34 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
|||||||
enum_entry.MemberCount = idx + 1
|
enum_entry.MemberCount = idx + 1
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
def find_enum(self, name: str) -> rawptr[GenTypes.EnumEntry]:
|
def find_enum(self, name: str) -> GenTypes.EnumEntry*:
|
||||||
"""按枚举类名查找,返回 EnumEntry 或 None"""
|
"""Look up an enum by class name; returns EnumEntry or None"""
|
||||||
if name is None or self.EnumTable is None:
|
if name is None or self.EnumTable is None: return None
|
||||||
return None
|
for i in range(int(self.EnumCount)):
|
||||||
for i in range(self.EnumCount):
|
entry: GenTypes.EnumEntry* = self._get_enum_entry(i)
|
||||||
entry: rawptr[GenTypes.EnumEntry] = self._get_enum_entry(i)
|
|
||||||
if entry is not None and entry.Name is not None:
|
if entry is not None and entry.Name is not None:
|
||||||
if string.strcmp(entry.Name, name) == 0:
|
if string.strcmp(entry.Name, name) == 0: return entry
|
||||||
return entry
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def lookup_enum_member(self, enum_name: str,
|
def lookup_enum_member(self, enum_name: str, member_name: str) -> GenTypes.EnumMember*:
|
||||||
member_name: str) -> rawptr[GenTypes.EnumMember]:
|
"""Look up a member by enum class name and member name; returns EnumMember or None"""
|
||||||
"""按枚举类名和成员名查找,返回 EnumMember 或 None"""
|
if enum_name is None or member_name is None: return None
|
||||||
if enum_name is None or member_name is None:
|
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
|
||||||
return None
|
if entry is None: return None
|
||||||
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
|
for mi in range(int(entry.MemberCount)):
|
||||||
if entry is None:
|
me: GenTypes.EnumMember* = self._get_enum_member(entry, mi)
|
||||||
return None
|
|
||||||
for mi in range(entry.MemberCount):
|
|
||||||
me: rawptr[GenTypes.EnumMember] = self._get_enum_member(entry, mi)
|
|
||||||
if me is not None and me.Name is not None:
|
if me is not None and me.Name is not None:
|
||||||
if string.strcmp(me.Name, member_name) == 0:
|
if string.strcmp(me.Name, member_name) == 0: return me
|
||||||
return me
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_enum_class(self, name: str) -> int:
|
def is_enum_class(self, name: str) -> int:
|
||||||
"""检查类名是否为已注册枚举,返回 1=是 / 0=否"""
|
"""Return 1 if the class name is a registered enum, else 0"""
|
||||||
if name is None:
|
if name is None: return 0
|
||||||
return 0
|
if self.find_enum(name) is not None: return 1
|
||||||
if self.find_enum(name) is not None:
|
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_enum_base_type(self, enum_name: str) -> llvmlite.LLVMType*:
|
def get_enum_base_type(self, enum_name: str) -> mlirlite.LLVMType*:
|
||||||
"""按枚举类名获取基准类型"""
|
"""Get the base type for an enum class name"""
|
||||||
entry: rawptr[GenTypes.EnumEntry] = self.find_enum(enum_name)
|
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
|
||||||
if entry is not None:
|
if entry is not None: return entry.BaseTy
|
||||||
return entry.BaseTy
|
|
||||||
return None
|
return None
|
||||||
File diff suppressed because it is too large.
Load diff
+3496
-2307
File diff suppressed because it is too large.
Load diff
+1002
-1016
File diff suppressed because it is too large.
Load diff
+1130
-1390
File diff suppressed because it is too large.
Load diff
@@ -1,132 +1,120 @@
|
|||||||
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import lib.core.Gen.Generator as Generator
|
import lib.core.Gen.Generator as Generator
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
|
||||||
import memhub
|
import memhub
|
||||||
import hashtable
|
import hashtable
|
||||||
import string
|
import string
|
||||||
import viperlib
|
|
||||||
import stdio
|
|
||||||
import stdlib
|
import stdlib
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.VLogger as VLogger
|
||||||
import lib.Stub.StubMerger as StubMerger
|
import lib.Stub.StubMerger as StubMerger
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
import lib.core.CompilerFactory as CompilerFactory
|
||||||
# TypeSpec 模块导入别名用 TSpec(模块内类名也叫 TypeSpec,别名同名会被解析器
|
# Use alias TSpec for the TypeSpec module (its class is also named TypeSpec; a same-name alias
|
||||||
# 当成"类 TypeSpec 的静态方法",导致跨模块调用被 mangled 成 <sha1>.TypeSpec.<fn>
|
# would be parsed as a static method -> mangled cross-module call with implicit self -> undefined symbol).
|
||||||
# 并隐式补 self 实参 → undefined symbol)。
|
|
||||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# TypeGenBase - type-table infrastructure: fields + InitTypeGen + trace/temp-dir/generic context
|
||||||
# TypeGenBase - 类型表基础设施:字段 + InitTypeGen + trace/临时目录/generic 上下文
|
|
||||||
# ============================================================
|
|
||||||
novirtual class TypeGenBase:
|
novirtual class TypeGenBase:
|
||||||
"""字段 + InitTypeGen + trace/临时目录/generic 上下文"""
|
"""Field storage, InitTypeGen, and trace/temp-dir/generic context helpers."""
|
||||||
|
|
||||||
# --- 仿 Python Traceback 调用栈(原 _g_trace_frames/_g_trace_count) ---
|
# --- Python-Traceback-like call stack (was _g_trace_frames/_g_trace_count) ---
|
||||||
Gen: Generator.Generator*
|
Gen: Generator.Generator*
|
||||||
TraceFrames: rawptr[GenTypes.TraceFrame] # malloc 数组(TypeGenMixin.TRACE_MAX 槽)
|
TraceFrames: GenTypes.TraceFrame* # malloc array (TypeGenMixin.TRACE_MAX slots)
|
||||||
TraceCount: int
|
TraceCount: int
|
||||||
|
# --- 当前实参位置:翻译调用实参时记录。报错节点是**合成**的(lineno==0,如构造器内联出的
|
||||||
|
# 参数赋值)时,用它兜底定位到具体表达式(如 `&_on_submit`),而不是回退到函数定义行。---
|
||||||
|
CurArgFile: str
|
||||||
|
CurArgLine: int
|
||||||
|
CurArgCol: int
|
||||||
|
|
||||||
# --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) ---
|
# --- Global generic specialization context (was _g_generic_tp_names/_g_generic_type_args) ---
|
||||||
GenericTpNames: list[str]*
|
GenericTpNames: list[str]*
|
||||||
GenericTypeArgs: list[str]*
|
GenericTypeArgs: list[str]*
|
||||||
# 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出
|
# Nesting-safe stack: set_generic_context pushes the outer value, clear_generic_context pops it.
|
||||||
# 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None →
|
# (A single slot would clear the outer context to None on exit, losing T substitution -> NameError.)
|
||||||
# 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest
|
GenericCtxStack: sizet* # fixed i64* array: 2 slots per level (tp_names, type_args pointers)
|
||||||
# 特化 Vector[int].__init__ 回归根因)。
|
|
||||||
GenericCtxStack: sizet* # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针)
|
|
||||||
GenericCtxDepth: int
|
GenericCtxDepth: int
|
||||||
|
|
||||||
# --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) ---
|
# --- Global CDefine compile-time constant table (was _g_cdefine_*) ---
|
||||||
CDefineNames: list[str]*
|
CDefineNames: list[str]*
|
||||||
CDefineValues: list[str]*
|
CDefineValues: list[str]*
|
||||||
CDefineDoubleFlags: list[int] | t.CPtr
|
# 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。
|
||||||
CDefineStrFlags: list[int] | t.CPtr # 1=字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2")
|
# 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32:
|
||||||
|
# `define PI: float = 3.14159...` 必须直接出 f32 常量,否则与 f32 字面量混算会多出 fpext。
|
||||||
|
CDefineDoubleFlags: list[int]*
|
||||||
|
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||||
|
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
|
||||||
|
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
|
||||||
|
CDefineBits: list[int]*
|
||||||
CDefineIsDouble: int
|
CDefineIsDouble: int
|
||||||
CDefineIsStr: int
|
CDefineIsStr: int
|
||||||
CDefineFound: int
|
CDefineFound: int
|
||||||
# --- CDefine 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
|
# --- CDefine name -> slot index (open addressing; slot stores index+1, 0=empty) ---
|
||||||
# 与 CDefineNames 同池分配、同 clear_cdefine_constants 复位;CDefineNames 只
|
# Same pool/reset as CDefineNames; valid only when Built == len(CDefineNames), else fall back to linear scan.
|
||||||
# 追加不重排 ⇒ 存槽下标稳定;Built == len(CDefineNames) 才可信,不可信/未命中
|
|
||||||
# 一律回退线性扫描(正确性保证)。
|
|
||||||
CDefineNameIdx: sizet*
|
CDefineNameIdx: sizet*
|
||||||
CDefineNameIdxCap: int
|
CDefineNameIdxCap: int
|
||||||
CDefineNameIdxBuilt: int
|
CDefineNameIdxBuilt: int
|
||||||
|
|
||||||
# --- 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) ---
|
# --- Cross-module CDefine lookup cache (PermPool-allocated, reused across files) ---
|
||||||
# 缓存 reset_to(mark2)/clear_cdefine_constants 不清除 → 跨文件复用,
|
# Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once.
|
||||||
# CONST_INT 递归查找只做一次。
|
|
||||||
XmodCdefNames: list[str]*
|
XmodCdefNames: list[str]*
|
||||||
XmodCdefValues: list[str]*
|
XmodCdefValues: list[str]*
|
||||||
XmodCdefDoubleFlags: list[int] | t.CPtr
|
# 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。
|
||||||
XmodCdefStrFlags: list[int] | t.CPtr
|
XmodCdefDoubleFlags: list[int]*
|
||||||
|
XmodCdefStrFlags: list[int]*
|
||||||
|
|
||||||
# --- 当前模块 SHA1(原 _g_current_module_sha1) ---
|
# --- Current module SHA1 (was _g_current_module_sha1) ---
|
||||||
CurrentModuleSha1: str
|
CurrentModuleSha1: str
|
||||||
|
|
||||||
# --- 全局 CTypedef 表(原 _g_ctypedef_*) ---
|
# --- Global CTypedef table (was _g_ctypedef_*) ---
|
||||||
CTypedefNames: list[str]*
|
CTypedefNames: list[str]*
|
||||||
CTypedefTypeStrs: list[str]*
|
CTypedefTypeStrs: list[str]*
|
||||||
CTypedefFound: int
|
CTypedefFound: int
|
||||||
# CTypedef 名 → 槽下标 索引(开放寻址 | 线性探测;槽存 index+1,0=空槽)。
|
# CTypedef name -> slot index (open addressing; slot stores index+1, 0=empty).
|
||||||
# 与 CTypedefNames 同池分配、同 clear_ctypedefs 复位;CTypedefNames 只追加
|
# Same pool/reset as CTypedefNames; valid only when Built == len(CTypedefNames), else fall back to linear scan.
|
||||||
# 不重排(同名注册原地更新类型串)⇒ 存槽下标稳定;Built == len(CTypedefNames)
|
|
||||||
# 才可信,不可信/未命中一律回退线性扫描(正确性保证)。
|
|
||||||
CTypedefNameIdx: sizet*
|
CTypedefNameIdx: sizet*
|
||||||
CTypedefNameIdxCap: int
|
CTypedefNameIdxCap: int
|
||||||
CTypedefNameIdxBuilt: int
|
CTypedefNameIdxBuilt: int
|
||||||
CTypedefResolveDepth: int
|
CTypedefResolveDepth: int
|
||||||
CTypedefVisiting: list[str]*
|
CTypedefVisiting: list[str]*
|
||||||
# builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef)
|
# builtins source scan flag: "last match" fallback for annotation resolution (_lookup_builtins_typedef).
|
||||||
# 每文件最多读一次 includes/builtins.vp;随 clear_ctypedefs 复位
|
# Reads includes/builtins.vp at most once per file; reset by clear_ctypedefs.
|
||||||
BuiltinsTypedefScanned: int
|
BuiltinsTypedefScanned: int
|
||||||
# builtins 最后匹配诊断("变量类型注解解析失败"详细报错用):
|
# Builtins last-match diagnostics (for detailed "variable annotation resolve failed" errors):
|
||||||
# LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult =
|
# the two strings are held by their own buffers (stdlib.malloc, freed by clear_ctypedefs) because callers pass temporaries that would dangle (ASan use-after-free).
|
||||||
# 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。
|
|
||||||
# 两个被指串由以下自有缓冲承载(stdlib.malloc,clear_ctypedefs 释放):
|
|
||||||
# 调用方常传临时缓冲(VpiReader._resolve_single_vpi_type_str 的段缓冲
|
|
||||||
# malloc 后立刻 free),直接存指针会悬垂 → _build_ann_fail_msg 的
|
|
||||||
# snprintf 读已释放内存(ASan heap-use-after-free)。
|
|
||||||
LastBuiltinsTypedefName: str
|
LastBuiltinsTypedefName: str
|
||||||
LastBuiltinsTypedefResult: str
|
LastBuiltinsTypedefResult: str
|
||||||
LastBuiltinsTypedefNameBuf: bytes
|
LastBuiltinsTypedefNameBuf: bytes
|
||||||
LastBuiltinsTypedefResultBuf: bytes
|
LastBuiltinsTypedefResultBuf: bytes
|
||||||
# builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段):
|
# builtins import alias buffer (stdlib.malloc, format "alias\0target\0").
|
||||||
# _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块限定
|
# Filled by scanning builtins.vp import lines to restore real module names from aliases; malloc (not pool) so it survives per-file reset.
|
||||||
# 引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。
|
|
||||||
# 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空;malloc 跨 pool
|
|
||||||
# 生命周期存活,clear_ctypedefs 释放置空。
|
|
||||||
ImportAliasBuf: bytes
|
ImportAliasBuf: bytes
|
||||||
|
|
||||||
# --- 库 typedef 按模块一次性门禁(_lookup_library_typedef 性能优化) ---
|
# --- Per-module one-shot gate for library typedefs (perf optimization) ---
|
||||||
# 库模块 typedef 缓存(PermPool 永久分配,跨文件/Phase 存活):
|
# PermPool caches (across files/phases): LibTypedefScannedMods (key=module -> scanned) and LibTypedefTypeMap (key="module|typedef" -> raw type expr); scanning 64KB per query costs 7-12s.
|
||||||
# LibTypedefScannedMods: key=库模块名 → 已扫描标记(命中=该模块源已读并入缓存);
|
|
||||||
# LibTypedefTypeMap: key="模块名|typedef 名" → 源中原始类型表达式。
|
|
||||||
# 每次查询都读 64KB 库源全文扫描会使单文件 translate 耗时 7–12s;库源单次编译内
|
|
||||||
# 不变,故缓存命中结果与逐次读源等价。
|
|
||||||
LibTypedefScannedMods: hashtable.HashTable*
|
LibTypedefScannedMods: hashtable.HashTable*
|
||||||
LibTypedefTypeMap: hashtable.HashTable*
|
LibTypedefTypeMap: hashtable.HashTable*
|
||||||
|
|
||||||
# --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) ---
|
# --- Const integer expression eval success flag (was _g_const_eval_ok) ---
|
||||||
ConstEvalOk: int
|
ConstEvalOk: int
|
||||||
|
|
||||||
# --- 全局当前文件名(原 _g_current_filename) ---
|
# --- Global current filename (was _g_current_filename) ---
|
||||||
CurrentFile: str
|
CurrentFile: str
|
||||||
|
|
||||||
# --- TypeSpec 驻留表(统一类型描述符,Debugger/_Type.md 第 6 节) ---
|
# --- TypeSpec arena (unified type descriptors; Debugger/_Type.md section 6) ---
|
||||||
# 惰性创建(ensure_spec_arena);由类型子系统自身持有,避免模块级全局表。
|
# Lazily created (ensure_spec_arena); instance and arrays are PermPool-allocated, so they survive across files/marks.
|
||||||
# 实例与内部数组均 PermPool 分配 ⇒ 跨文件/跨 mark 存活。
|
|
||||||
Specs: TSpec.TypeSpecArena*
|
Specs: TSpec.TypeSpecArena*
|
||||||
def InitTypeGen(self) -> int:
|
def InitTypeGen(self) -> int:
|
||||||
"""初始化类型 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
|
"""Initialize the type mixin fields (inlined from subclass constructors)."""
|
||||||
if self is None: return 1
|
if self is None: return 1
|
||||||
self.TraceFrames = None
|
self.TraceFrames = None
|
||||||
self.TraceCount = 0
|
self.TraceCount = 0
|
||||||
|
self.CurArgFile = None
|
||||||
|
self.CurArgLine = 0
|
||||||
|
self.CurArgCol = 0
|
||||||
self.GenericTpNames = None
|
self.GenericTpNames = None
|
||||||
self.GenericTypeArgs = None
|
self.GenericTypeArgs = None
|
||||||
self.GenericCtxStack = None
|
self.GenericCtxStack = None
|
||||||
@@ -135,6 +123,7 @@ novirtual class TypeGenBase:
|
|||||||
self.CDefineValues = None
|
self.CDefineValues = None
|
||||||
self.CDefineDoubleFlags = None
|
self.CDefineDoubleFlags = None
|
||||||
self.CDefineStrFlags = None
|
self.CDefineStrFlags = None
|
||||||
|
self.CDefineBits = None
|
||||||
self.CDefineIsDouble = 0
|
self.CDefineIsDouble = 0
|
||||||
self.CDefineIsStr = 0
|
self.CDefineIsStr = 0
|
||||||
self.CDefineFound = 0
|
self.CDefineFound = 0
|
||||||
@@ -160,7 +149,7 @@ novirtual class TypeGenBase:
|
|||||||
self.LastBuiltinsTypedefNameBuf = None
|
self.LastBuiltinsTypedefNameBuf = None
|
||||||
self.LastBuiltinsTypedefResultBuf = None
|
self.LastBuiltinsTypedefResultBuf = None
|
||||||
self.ImportAliasBuf = None
|
self.ImportAliasBuf = None
|
||||||
# 库 typedef 持久缓存字段(不随 clear_ctypedefs 复位 → 跨文件/Phase 存活)
|
# Persistent library typedef cache fields (not reset by clear_ctypedefs, so they survive across files/phases)
|
||||||
self.LibTypedefScannedMods = None
|
self.LibTypedefScannedMods = None
|
||||||
self.LibTypedefTypeMap = None
|
self.LibTypedefTypeMap = None
|
||||||
self.ConstEvalOk = 0
|
self.ConstEvalOk = 0
|
||||||
@@ -168,14 +157,10 @@ novirtual class TypeGenBase:
|
|||||||
self.Specs = None
|
self.Specs = None
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# ============================================================
|
# ensure_spec_arena - lazily create the TypeSpec arena (idempotent).
|
||||||
# ensure_spec_arena - 惰性创建 TypeSpec 驻留表(幂等)
|
# PermPool-allocated; returns None on failure (callers fail-fast, no silent degradation).
|
||||||
#
|
|
||||||
# 用 PermPool 分配 arena 本体;表内部缓冲由 TSpec.SpecArenaInit 分配
|
|
||||||
# (同样 PermPool)。失败返回 None(调用方按 fail-fast 处理,不静默退化)。
|
|
||||||
# ============================================================
|
|
||||||
def ensure_spec_arena(self) -> TSpec.TypeSpecArena*:
|
def ensure_spec_arena(self) -> TSpec.TypeSpecArena*:
|
||||||
"""确保 TypeSpec 驻留表已创建,返回 arena;失败返回 None"""
|
"""Ensure the TypeSpec arena exists and return it; None on failure."""
|
||||||
if self.Specs is not None: return self.Specs
|
if self.Specs is not None: return self.Specs
|
||||||
size: sizet = TSpec.TypeSpecArena.__sizeof__()
|
size: sizet = TSpec.TypeSpecArena.__sizeof__()
|
||||||
arena: TSpec.TypeSpecArena* = TSpec.TypeSpecArena*(CompilerFactory.factory.PermPool.alloc(size))
|
arena: TSpec.TypeSpecArena* = TSpec.TypeSpecArena*(CompilerFactory.factory.PermPool.alloc(size))
|
||||||
@@ -184,30 +169,28 @@ novirtual class TypeGenBase:
|
|||||||
if TSpec.SpecArenaInit(arena, 0) != 0: return None
|
if TSpec.SpecArenaInit(arena, 0) != 0: return None
|
||||||
self.Specs = arena
|
self.Specs = arena
|
||||||
return arena
|
return arena
|
||||||
# ============================================================
|
# P2 migrated methods (bodies from HandlesType.vp; globals -> self fields, same-module calls -> self methods).
|
||||||
# P2 迁移方法(函数体来自 HandlesType.vp,全局变量改 self.字段,
|
# Signatures match the original HandlesType functions.
|
||||||
# 同模块函数调用改 self.方法;签名与 HandlesType 原函数一致)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def _ensure_trace_init(self) -> int:
|
def _ensure_trace_init(self) -> int:
|
||||||
"""确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)"""
|
"""Ensure the trace stack is initialized (lazy, malloc-based)."""
|
||||||
if self.TraceFrames is not None: return 1
|
if self.TraceFrames is not None: return 1
|
||||||
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
||||||
self.TraceFrames = CompilerFactory.factory.PermPool.alloc(entry_size * TypeGenMixin.TRACE_MAX)
|
self.TraceFrames = CompilerFactory.factory.PermPool.alloc(entry_size * TypeGenMixin.TRACE_MAX)
|
||||||
if self.TraceFrames is None: return 0
|
if self.TraceFrames is None: return 0
|
||||||
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
|
string.memset(self.TraceFrames, 0, entry_size * TypeGenMixin.TRACE_MAX)
|
||||||
return 1
|
return 1
|
||||||
def _get_trace_frame(self, i: int) -> rawptr[GenTypes.TraceFrame]:
|
def _get_trace_frame(self, i: int) -> GenTypes.TraceFrame*:
|
||||||
"""获取第 i 个 TraceFrame 槽位"""
|
"""Return the i-th TraceFrame slot."""
|
||||||
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX: return None
|
if self.TraceFrames is None or i < 0 or i >= TypeGenMixin.TRACE_MAX: return None
|
||||||
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
entry_size: sizet = GenTypes.TraceFrame.__sizeof__()
|
||||||
addr: uint64 = uint64(self.TraceFrames) + i * entry_size
|
addr: uint64 = uint64(self.TraceFrames) + i * entry_size
|
||||||
return rawptr[GenTypes.TraceFrame](addr)
|
return GenTypes.TraceFrame*(addr)
|
||||||
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
|
def push_trace(self, file_name: str, lineno: int, col_start: int = 0, col_end: int = 0) -> None:
|
||||||
"""压入一个翻译上下文帧(进入新的翻译作用域时调用)"""
|
"""Push a translation context frame."""
|
||||||
if self._ensure_trace_init() == 0: return
|
if self._ensure_trace_init() == 0: return
|
||||||
if self.TraceCount >= TypeGenMixin.TRACE_MAX: return
|
if self.TraceCount >= TypeGenMixin.TRACE_MAX: return
|
||||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
|
frame: GenTypes.TraceFrame* = self._get_trace_frame(self.TraceCount)
|
||||||
if frame is None: return
|
if frame is None: return
|
||||||
frame.file_name = file_name
|
frame.file_name = file_name
|
||||||
frame.lineno = lineno
|
frame.lineno = lineno
|
||||||
@@ -215,19 +198,25 @@ novirtual class TypeGenBase:
|
|||||||
frame.col_end = col_end
|
frame.col_end = col_end
|
||||||
self.TraceCount += 1
|
self.TraceCount += 1
|
||||||
def pop_trace(self) -> None:
|
def pop_trace(self) -> None:
|
||||||
"""弹出栈顶帧(退出翻译作用域时调用)"""
|
"""Pop the top trace frame."""
|
||||||
if self.TraceCount <= 0: return
|
if self.TraceCount <= 0: return
|
||||||
self.TraceCount -= 1
|
self.TraceCount -= 1
|
||||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(self.TraceCount)
|
frame: GenTypes.TraceFrame* = self._get_trace_frame(self.TraceCount)
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
frame.file_name = None
|
frame.file_name = None
|
||||||
frame.lineno = 0
|
frame.lineno = 0
|
||||||
frame.col_start = 0
|
frame.col_start = 0
|
||||||
frame.col_end = 0
|
frame.col_end = 0
|
||||||
|
# set_cur_arg - 记录正在翻译的实参位置(file/line/col),供“合成节点无行号”时兜底定位。
|
||||||
|
def set_cur_arg(self, file_name: str, lineno: int, col_start: int) -> None:
|
||||||
|
"""Remember the argument position currently being translated."""
|
||||||
|
self.CurArgFile = file_name
|
||||||
|
self.CurArgLine = lineno
|
||||||
|
self.CurArgCol = col_start
|
||||||
def clear_trace(self) -> None:
|
def clear_trace(self) -> None:
|
||||||
"""清空调用栈(每个文件翻译开始前调用)"""
|
"""Clear the trace stack (called before translating each file)."""
|
||||||
for i in range(self.TraceCount):
|
for i in range(int(self.TraceCount)):
|
||||||
frame: rawptr[GenTypes.TraceFrame] = self._get_trace_frame(i)
|
frame: GenTypes.TraceFrame* = self._get_trace_frame(i)
|
||||||
if frame is not None:
|
if frame is not None:
|
||||||
frame.file_name = None
|
frame.file_name = None
|
||||||
frame.lineno = 0
|
frame.lineno = 0
|
||||||
@@ -235,25 +224,19 @@ novirtual class TypeGenBase:
|
|||||||
frame.col_end = 0
|
frame.col_end = 0
|
||||||
self.TraceCount = 0
|
self.TraceCount = 0
|
||||||
def set_temp_dir(self, temp_dir: str) -> None:
|
def set_temp_dir(self, temp_dir: str) -> None:
|
||||||
"""设置全局 temp_dir(统一写入 CallGenBase.TempDir,跨子对象经 Gen 指针)"""
|
"""Set the global temp_dir via CallGenBase.TempDir."""
|
||||||
self.Gen.CallGenBase.SetTempDir(temp_dir)
|
self.Gen.CallGenBase.SetTempDir(temp_dir)
|
||||||
def get_temp_dir(self) -> str:
|
def get_temp_dir(self) -> str:
|
||||||
"""获取全局 temp_dir(从 CallGenBase 读取)"""
|
"""Get the global temp_dir from CallGenBase."""
|
||||||
return self.Gen.CallGenBase.TempDir
|
return self.Gen.CallGenBase.TempDir
|
||||||
def set_current_module_sha1(self, sha1: str) -> None:
|
def set_current_module_sha1(self, sha1: str) -> None:
|
||||||
"""设置当前正在编译的模块 SHA1(Phase1 在模块开始翻译前调用)"""
|
"""Set the SHA1 of the module currently being compiled."""
|
||||||
self.CurrentModuleSha1 = sha1
|
self.CurrentModuleSha1 = sha1
|
||||||
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*,
|
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*, tree: ast.AST*, tag: str):
|
||||||
tree: ast.AST*, tag: str):
|
"""Removed debug hook; signature kept as a placeholder for external callers."""
|
||||||
"""已移除调试插桩(占位保留签名,避免外部调用引用失败)"""
|
|
||||||
pass
|
pass
|
||||||
def set_generic_context(self, tp_names: list[str]*,
|
def set_generic_context(self, tp_names: list[str]*, type_args: list[str]*):
|
||||||
type_args: list[str]*):
|
"""Push and set the generic specialization context (nesting-safe)."""
|
||||||
"""设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全
|
|
||||||
|
|
||||||
先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复,
|
|
||||||
避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。
|
|
||||||
"""
|
|
||||||
if self.GenericCtxStack is None:
|
if self.GenericCtxStack is None:
|
||||||
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
|
self.GenericCtxStack = stdlib.malloc(8 * 2 * 32)
|
||||||
if self.GenericCtxStack is None:
|
if self.GenericCtxStack is None:
|
||||||
@@ -269,7 +252,7 @@ novirtual class TypeGenBase:
|
|||||||
self.GenericTypeArgs = type_args
|
self.GenericTypeArgs = type_args
|
||||||
|
|
||||||
def clear_generic_context(self):
|
def clear_generic_context(self):
|
||||||
"""清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None"""
|
"""Pop the generic specialization context, restoring the outer one."""
|
||||||
if self.GenericCtxDepth > 0:
|
if self.GenericCtxDepth > 0:
|
||||||
self.GenericCtxDepth -= 1
|
self.GenericCtxDepth -= 1
|
||||||
self.GenericTpNames = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2])
|
self.GenericTpNames = (list[str]*)(self.GenericCtxStack[self.GenericCtxDepth * 2])
|
||||||
@@ -278,17 +261,13 @@ novirtual class TypeGenBase:
|
|||||||
self.GenericTpNames = None
|
self.GenericTpNames = None
|
||||||
self.GenericTypeArgs = None
|
self.GenericTypeArgs = None
|
||||||
def resolve_generic_type_name(self, name: str) -> str:
|
def resolve_generic_type_name(self, name: str) -> str:
|
||||||
"""如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身
|
"""Return the concrete type name for a generic type parameter, else name itself."""
|
||||||
|
if name is None: return name
|
||||||
用于表达式中的 T 替换(如 T.__sizeof__() → int.__sizeof__())
|
|
||||||
"""
|
|
||||||
if name is None:
|
|
||||||
return name
|
|
||||||
rv_d: str = name
|
rv_d: str = name
|
||||||
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
|
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
|
||||||
rg_n: sizet = len(self.GenericTpNames)
|
rg_n: sizet = len(self.GenericTpNames)
|
||||||
rg_i: sizet
|
rg_i: sizet
|
||||||
for rg_i in range(rg_n):
|
for rg_i in range(int(rg_n)):
|
||||||
rg_tpn: str = self.GenericTpNames.get(rg_i)
|
rg_tpn: str = self.GenericTpNames.get(rg_i)
|
||||||
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
|
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
|
||||||
rg_ta: str = self.GenericTypeArgs.get(rg_i)
|
rg_ta: str = self.GenericTypeArgs.get(rg_i)
|
||||||
|
|||||||
@@ -1,48 +1,40 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
|
||||||
import llvmlite
|
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import viperlib
|
import viperlib
|
||||||
import stdio
|
import stdio
|
||||||
import stdlib
|
|
||||||
import lib.core.VLogger as VLogger
|
import lib.core.VLogger as VLogger
|
||||||
import lib.core.Gen.Translator as HT
|
|
||||||
import lib.Stub.StubMerger as StubMerger
|
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
import lib.core.CompilerFactory as CompilerFactory
|
||||||
|
|
||||||
|
|
||||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||||
|
|
||||||
# ============================================================
|
# CDefine name index (name -> slot index; open addressing with linear probing).
|
||||||
# CDefine 名索引(名 → 槽下标,开放寻址 | 线性探测)
|
# 4096 power-of-two slots; slot stores index+1 (0=empty); valid only when Built == len(CDefineNames).
|
||||||
#
|
|
||||||
# 槽数 = 4096(2 的幂;负载 < 0.5 上限 2048 条,超限标记不可信回退线性扫描)。
|
|
||||||
# 槽存槽下标 + 1(0=空槽)。CDefineNames 只追加不重排 ⇒ 槽下标稳定;
|
|
||||||
# Built == len(CDefineNames) 才可信。
|
|
||||||
# ============================================================
|
|
||||||
define CDEFINE_IDX_CAP = 4096
|
define CDEFINE_IDX_CAP = 4096
|
||||||
# 索引哈希常量(32 位 FNV-1a)
|
# Index hash constants (32-bit FNV-1a)
|
||||||
define CDEF_IDX_FNV_OFFSET = 2166136261
|
define CDEF_IDX_FNV_OFFSET = 2166136261
|
||||||
define CDEF_IDX_FNV_PRIME = 16777619
|
define CDEF_IDX_FNV_PRIME = 16777619
|
||||||
define CDEF_IDX_FNV_MASK = 4294967295
|
define CDEF_IDX_FNV_MASK = 4294967295
|
||||||
|
|
||||||
|
# CDefine 浮点常量的宽度种类(CDefineDoubleFlags / XmodCdefDoubleFlags 条目编码)。
|
||||||
|
# 1=f64(double,默认),2=f32(float);0 表示非浮点。既有读取处一律按 `!= 0` 判定
|
||||||
|
# "是浮点常量",故 f32 天然兼容;只有浮点常量的物化处需要区分 64/32。
|
||||||
|
# 必须写明 `: int`:无注解 define 按**值的最小可存宽度**物化(1/2 → i8),
|
||||||
|
# 直接当实参调用 `list[int].append(x: int)` 会落成 `i8 != i32`(mlir-opt 校验失败)。
|
||||||
|
define CDEFINE_FLOAT_KIND_F64: int = 1
|
||||||
|
define CDEFINE_FLOAT_KIND_F32: int = 2
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# TypeGenCDef - TypeGenMixin 拆分(阶段 D):CDefine 编译期常量表(register/lookup/is/set/clear)
|
# TypeGenCDef - split from TypeGenMixin (stage D): CDefine compile-time constant table (register/lookup/is/set/clear)
|
||||||
# ============================================================
|
|
||||||
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||||
"""CDefine 编译期常量表(register/lookup/is/set/clear)"""
|
"""CDefine compile-time constant table (register/lookup/is/set/clear)."""
|
||||||
|
|
||||||
# ============================================================
|
# CDefine name index (open addressing; slot stores index+1, 0=empty)
|
||||||
# CDefine 名索引(开放寻址;槽存 index+1,0=空槽)
|
|
||||||
# ============================================================
|
|
||||||
def _cdef_idx_hash(self, name: str) -> uint64:
|
def _cdef_idx_hash(self, name: str) -> uint64:
|
||||||
"""名 → 64 位哈希(32 位 FNV-1a,逐字节到 NUL 结束)"""
|
"""64-bit FNV-1a hash of the name, computed byte by byte until NUL."""
|
||||||
h: uint64 = uint64(CDEF_IDX_FNV_OFFSET)
|
h: uint64 = uint64(CDEF_IDX_FNV_OFFSET)
|
||||||
if name is None:
|
if name is None: return h
|
||||||
return h
|
|
||||||
i: sizet = 0
|
i: sizet = 0
|
||||||
while name[i] != '\0':
|
while name[i] != '\0':
|
||||||
h = (h ^ uint64(int(name[i]))) * uint64(CDEF_IDX_FNV_PRIME)
|
h = (h ^ uint64(int(name[i]))) * uint64(CDEF_IDX_FNV_PRIME)
|
||||||
@@ -51,11 +43,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
return h
|
return h
|
||||||
|
|
||||||
def _cdef_idx_slot(self, si: sizet) -> sizet*:
|
def _cdef_idx_slot(self, si: sizet) -> sizet*:
|
||||||
"""取索引第 si 个槽(8 字节,存槽下标+1;0=空槽)"""
|
"""Return index slot si (slot stores index+1; 0 means empty)."""
|
||||||
return (sizet*)(uint64(self.CDefineNameIdx) + uint64(si) * 8)
|
return (sizet*)(uint64(self.CDefineNameIdx) + uint64(si) * 8)
|
||||||
|
|
||||||
def _cdef_idx_init(self, pool: memhub.MemBuddy*) -> None:
|
def _cdef_idx_init(self, pool: memhub.MemBuddy*) -> None:
|
||||||
"""创建/清零名索引(与 CDefineNames 同池);失败则标记不可信"""
|
"""Create and zero the name index in the same pool; mark it untrusted on failure."""
|
||||||
idx: sizet* = pool.alloc(sizet(CDEFINE_IDX_CAP) * 8)
|
idx: sizet* = pool.alloc(sizet(CDEFINE_IDX_CAP) * 8)
|
||||||
if idx is None:
|
if idx is None:
|
||||||
self.CDefineNameIdx = None
|
self.CDefineNameIdx = None
|
||||||
@@ -68,118 +60,81 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
self.CDefineNameIdxBuilt = 0
|
self.CDefineNameIdxBuilt = 0
|
||||||
|
|
||||||
def _cdef_idx_find(self, name: str) -> int:
|
def _cdef_idx_find(self, name: str) -> int:
|
||||||
"""在名索引中探测 name,返回命中槽下标(>=0);未命中/不可用返回 -1"""
|
"""Probe the name index for name; return the slot index, or -1 if missing/unusable."""
|
||||||
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
|
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return -1
|
||||||
return -1
|
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)): return -1
|
||||||
if self.CDefineNameIdxBuilt != int(len(self.CDefineNames)):
|
|
||||||
return -1
|
|
||||||
h: uint64 = self._cdef_idx_hash(name)
|
h: uint64 = self._cdef_idx_hash(name)
|
||||||
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
||||||
si: sizet = sizet(h) & mask
|
si: sizet = sizet(h) & mask
|
||||||
i: int = 0
|
for i in range(int(self.CDefineNameIdxCap)):
|
||||||
while i < self.CDefineNameIdxCap:
|
|
||||||
slot: sizet* = self._cdef_idx_slot(si)
|
slot: sizet* = self._cdef_idx_slot(si)
|
||||||
sv: sizet = slot[0]
|
sv: sizet = slot[0]
|
||||||
if sv == 0:
|
if sv == 0: return -1
|
||||||
return -1
|
|
||||||
se: int = int(sv) - 1
|
se: int = int(sv) - 1
|
||||||
nm: str = self.CDefineNames.get(sizet(se))
|
nm: str = self.CDefineNames.get(sizet(se))
|
||||||
if nm is not None and string.strcmp(nm, name) == 0:
|
if nm is not None and string.strcmp(nm, name) == 0: return se
|
||||||
return se
|
|
||||||
si = (si + 1) & mask
|
si = (si + 1) & mask
|
||||||
i += 1
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def _cdef_idx_insert(self, slot_index: int, name: str) -> int:
|
def _cdef_idx_insert(self, slot_index: int, name: str) -> int:
|
||||||
"""把 (name → slot_index) 记入索引;0=成功 / 1=失败"""
|
"""Record name -> slot_index in the index; 0=success, 1=failure."""
|
||||||
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None:
|
if self.CDefineNameIdx is None or self.CDefineNameIdxCap <= 0 or name is None: return 1
|
||||||
return 1
|
|
||||||
h: uint64 = self._cdef_idx_hash(name)
|
h: uint64 = self._cdef_idx_hash(name)
|
||||||
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
||||||
si: sizet = sizet(h) & mask
|
si: sizet = sizet(h) & mask
|
||||||
i: int = 0
|
for i in range(int(self.CDefineNameIdxCap)):
|
||||||
while i < self.CDefineNameIdxCap:
|
|
||||||
slot: sizet* = self._cdef_idx_slot(si)
|
slot: sizet* = self._cdef_idx_slot(si)
|
||||||
if slot[0] == 0:
|
if slot[0] == 0:
|
||||||
slot[0] = sizet(slot_index + 1)
|
slot[0] = sizet(slot_index + 1)
|
||||||
return 0
|
return 0
|
||||||
si = (si + 1) & mask
|
si = (si + 1) & mask
|
||||||
i += 1
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def _cdef_idx_sync_append(self, name: str) -> None:
|
def _cdef_idx_sync_append(self, name: str) -> None:
|
||||||
"""CDefineNames.append 后同步索引(槽下标 = len-1);不可信则保持不可信"""
|
"""Sync the index after CDefineNames.append (slot = len-1); keep it untrusted if so."""
|
||||||
n: int = int(len(self.CDefineNames))
|
n: int = int(len(self.CDefineNames))
|
||||||
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0:
|
if self.CDefineNameIdxBuilt == n - 1 and self._cdef_idx_insert(n - 1, name) == 0: self.CDefineNameIdxBuilt = n
|
||||||
self.CDefineNameIdxBuilt = n
|
else: self.CDefineNameIdxBuilt = -1
|
||||||
else:
|
|
||||||
self.CDefineNameIdxBuilt = -1
|
|
||||||
|
|
||||||
# ============================================================
|
# Pool discipline for the CDefine table body.
|
||||||
# CDefine 表本体的池归一
|
# Body (4 lists + name index) always lives in PermPool so it survives per-file reset_to(mark); falls back to the passed pool if unavailable.
|
||||||
#
|
def _cdefine_table_pool(self, pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
|
||||||
# 表指针挂在**进程级单例** _g_gen.TypeGenCDef 上,不在任何 pool 的管辖
|
"""Pool for the CDefine table body: PermPool, or the passed pool as a fallback."""
|
||||||
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
|
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
|
||||||
# (Phase1 1a/1b、Phase2 PhaseA、StubMerger 扫描、Repl 每片段都按 mark/reset
|
|
||||||
# 轮转)。表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None`
|
|
||||||
# 守卫失效 ⇒ 下一次 register_cdefine_* 的 append 写进已回收内存(堆损坏;
|
|
||||||
# 检测模式下该区被 free 填成 0xCD ⇒ append 的接收者 = 0xcdcdcdcdcdcdcdcd)。
|
|
||||||
# 故表本体(四个 list + 名索引)一律落 PermPool(进程级永久池,从不 reset),
|
|
||||||
# 与"文件级/片段级 reset"彻底解耦。
|
|
||||||
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
|
|
||||||
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
|
|
||||||
# ============================================================
|
|
||||||
def _cdefine_table_pool(self,
|
|
||||||
pool: memhub.MemBuddy*) -> memhub.MemBuddy*:
|
|
||||||
"""CDefine 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
|
|
||||||
if CompilerFactory.factory is not None \
|
|
||||||
and CompilerFactory.factory.PermPool is not None:
|
|
||||||
return CompilerFactory.factory.PermPool
|
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
def register_cdefine_constant(self, pool: memhub.MemBuddy*,
|
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None:
|
||||||
name: str, value: int64) -> None:
|
"""Register an integer CDefine constant; store the value as a decimal string.
|
||||||
"""注册 CDefine 编译期常量到本地表
|
|
||||||
|
|
||||||
值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如
|
bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。
|
||||||
sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断
|
|
||||||
成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。
|
|
||||||
0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。
|
|
||||||
|
|
||||||
名称深拷贝到 pool(与 register_xmod_cdefine 对 key 深拷贝同一教训):
|
|
||||||
调用方传入的 name 可能指向临时缓冲——VpiReader 注入 .vpi 时 Name.id 是
|
|
||||||
零拷贝指向 vpi_buf(见 ParseVpiIntoModule 的 token 零拷贝注释),而
|
|
||||||
vpi_buf 在注入函数末尾即 stdlib.free。只存指针会让 CDefineNames 持有
|
|
||||||
悬空名:之后 lookup 的 strcmp 若命中被回收/复用内存里恰好出现的同名文本,
|
|
||||||
就会返回别的条目的值(Phase1.SHA1_LEN=17 被读成 0 的根因)。
|
|
||||||
"""
|
"""
|
||||||
if name is None:
|
if name is None: return
|
||||||
return
|
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None:
|
||||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||||
tn: list[str]* = list[str](tp, 64)
|
tn: list[str]* = list[str](tp, 64)
|
||||||
tv: list[str]* = list[str](tp, 64)
|
tv: list[str]* = list[str](tp, 64)
|
||||||
td: list[int] | t.CPtr = list[int](tp, 64)
|
td: list[int]* = list[int](tp, 64)
|
||||||
tsf: list[int] | t.CPtr = list[int](tp, 64)
|
tsf: list[int]* = list[int](tp, 64)
|
||||||
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
|
tbits: list[int]* = list[int](tp, 64)
|
||||||
# 落到 None 或未初始化地址(原实现无检查 ⇒ append 写进 NULL/0xCD 区崩)
|
# Table build failed (pool unusable/exhausted): report and return, never let the
|
||||||
if tn is None or tv is None or td is None or tsf is None:
|
# append below hit None or an uninitialized address (would write NULL/0xCD).
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表分配失败(表本体池不可用或耗尽)", "MemoryError")
|
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||||
return
|
return
|
||||||
self.CDefineNames = tn
|
self.CDefineNames = tn
|
||||||
self.CDefineValues = tv
|
self.CDefineValues = tv
|
||||||
self.CDefineDoubleFlags = td
|
self.CDefineDoubleFlags = td
|
||||||
self.CDefineStrFlags = tsf
|
self.CDefineStrFlags = tsf
|
||||||
|
self.CDefineBits = tbits
|
||||||
self._cdef_idx_init(tp)
|
self._cdef_idx_init(tp)
|
||||||
# 表就绪守卫:表本体缺失/半初始化(建表失败或外部置空)⇒ fail-fast,
|
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
|
||||||
# 不写垃圾(append 之前必有非空断言)
|
# (assert non-null before append).
|
||||||
if self.CDefineNames is None or self.CDefineValues is None \
|
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||||
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
|
||||||
return
|
return
|
||||||
name_copy: str = pool.alloc(string.strlen(name) + 1)
|
name_copy: str = pool.alloc(string.strlen(name) + 1)
|
||||||
if name_copy is None:
|
if name_copy is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 名称缓冲区分配失败", "MemoryError")
|
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' name buffer allocation failed", "MemoryError")
|
||||||
return
|
return
|
||||||
string.strcpy(name_copy, name)
|
string.strcpy(name_copy, name)
|
||||||
self.CDefineNames.append(name_copy)
|
self.CDefineNames.append(name_copy)
|
||||||
@@ -188,105 +143,93 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
if val_buf is not None:
|
if val_buf is not None:
|
||||||
viperlib.snprintf(val_buf, 32, "%lld", value)
|
viperlib.snprintf(val_buf, 32, "%lld", value)
|
||||||
self.CDefineValues.append(val_buf)
|
self.CDefineValues.append(val_buf)
|
||||||
else:
|
else: self.CDefineValues.append("0")
|
||||||
self.CDefineValues.append("0")
|
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||||
# fail-fast:CDefine 值缓冲区分配失败必须终止编译
|
# (consistent with the double/string branches; do not silently write "0").
|
||||||
# (与浮点/字符串分支口径一致,不再静默写 "0" 污染常量表)
|
if val_buf is None: self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' value buffer allocation failed", "MemoryError")
|
||||||
if val_buf is None:
|
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 值缓冲区分配失败", "MemoryError")
|
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||||
if self.CDefineDoubleFlags is not None:
|
if self.CDefineBits is not None: self.CDefineBits.append(bits)
|
||||||
self.CDefineDoubleFlags.append(0)
|
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None:
|
||||||
if self.CDefineStrFlags is not None:
|
"""Register a float CDefine constant; store the value as %f text and flag it.
|
||||||
self.CDefineStrFlags.append(0)
|
|
||||||
def register_cdefine_double(self, pool: memhub.MemBuddy*,
|
|
||||||
name: str, value: double) -> None:
|
|
||||||
"""注册浮点 CDefine 编译期常量(如 U_M_PI: t.CDefine = 3.14159...)
|
|
||||||
|
|
||||||
值以 %f 文本存储并标记浮点,供 translate_value 返回浮点常量。
|
is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`),
|
||||||
|
物化时直接出 f32 常量;0(默认)表示 double。
|
||||||
名称同样深拷贝到 pool(理由见 register_cdefine_constant:注入 .vpi 时
|
|
||||||
Name.id 指向的 vpi_buf 会被释放,只存指针会留下悬空名)。
|
|
||||||
"""
|
"""
|
||||||
if name is None:
|
if name is None: return
|
||||||
return
|
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None:
|
||||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||||
tn: list[str]* = list[str](tp, 64)
|
tn: list[str]* = list[str](tp, 64)
|
||||||
tv: list[str]* = list[str](tp, 64)
|
tv: list[str]* = list[str](tp, 64)
|
||||||
td: list[int] | t.CPtr = list[int](tp, 64)
|
td: list[int]* = list[int](tp, 64)
|
||||||
tsf: list[int] | t.CPtr = list[int](tp, 64)
|
tsf: list[int]* = list[int](tp, 64)
|
||||||
if tn is None or tv is None or td is None or tsf is None:
|
tbits: list[int]* = list[int](tp, 64)
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表分配失败(表本体池不可用或耗尽)", "MemoryError")
|
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||||
return
|
return
|
||||||
self.CDefineNames = tn
|
self.CDefineNames = tn
|
||||||
self.CDefineValues = tv
|
self.CDefineValues = tv
|
||||||
self.CDefineDoubleFlags = td
|
self.CDefineDoubleFlags = td
|
||||||
self.CDefineStrFlags = tsf
|
self.CDefineStrFlags = tsf
|
||||||
|
self.CDefineBits = tbits
|
||||||
self._cdef_idx_init(tp)
|
self._cdef_idx_init(tp)
|
||||||
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
|
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||||
if self.CDefineNames is None or self.CDefineValues is None \
|
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||||
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
|
||||||
return
|
return
|
||||||
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
|
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
|
||||||
if name_copy_d is None:
|
if name_copy_d is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 浮点 '{name}' 名称缓冲区分配失败", "MemoryError")
|
self.Gen.TypeGenErr.fatal_error(None, f"CDefine float '{name}' name buffer allocation failed", "MemoryError")
|
||||||
return
|
return
|
||||||
string.strcpy(name_copy_d, name)
|
string.strcpy(name_copy_d, name)
|
||||||
self.CDefineNames.append(name_copy_d)
|
self.CDefineNames.append(name_copy_d)
|
||||||
self._cdef_idx_sync_append(name_copy_d)
|
self._cdef_idx_sync_append(name_copy_d)
|
||||||
val_buf: str = pool.alloc(32)
|
val_buf: str = pool.alloc(32)
|
||||||
if val_buf is not None:
|
if val_buf is not None:
|
||||||
# 用 CRT stdio.snprintf 而非 viperlib.snprintf:viperlib 自实现格式化器的
|
# Use CRT stdio.snprintf, not viperlib.snprintf: viperlib's %g float path is buggy
|
||||||
# %g 浮点路径有 bug(%.17g 输出错误值,如 pi 显示 3.1397985 而非 3.1415926,
|
# (wrong %.17g, e.g. pi prints 3.1397985 not 3.1415926), which would corrupt CDefine text.
|
||||||
# 位模式 %a 正确),会导致 CDefine 表存错文本 → lookup 生成错 LLVM 常量。
|
|
||||||
stdio.snprintf(val_buf, 32, "%.17g", value)
|
stdio.snprintf(val_buf, 32, "%.17g", value)
|
||||||
self.CDefineValues.append(val_buf)
|
self.CDefineValues.append(val_buf)
|
||||||
else:
|
else: self.CDefineValues.append("0")
|
||||||
self.CDefineValues.append("0")
|
# 浮点宽度种类:f32=2,f64=1(判定处一律 `!= 0`,故与旧数据兼容)。
|
||||||
if self.CDefineDoubleFlags is not None:
|
if self.CDefineDoubleFlags is not None:
|
||||||
self.CDefineDoubleFlags.append(1)
|
if is_f32 != 0: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F32)
|
||||||
if self.CDefineStrFlags is not None:
|
else: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F64)
|
||||||
self.CDefineStrFlags.append(0)
|
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||||
# fail-fast:CDefine 值缓冲区分配失败必须终止编译,
|
if self.CDefineBits is not None: self.CDefineBits.append(0)
|
||||||
|
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||||
if val_buf is None:
|
if val_buf is None:
|
||||||
VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG")
|
VLogger.error("CDefine float value buffer allocation failed", "CDEF-DBG")
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError")
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine float value buffer allocation failed", "MemoryError")
|
||||||
def register_cdefine_string(self, pool: memhub.MemBuddy*,
|
def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str) -> None:
|
||||||
name: str, value: str) -> None:
|
"""Register a string CDefine constant, deep-copying name and value into pool."""
|
||||||
"""注册字符串 CDefine 编译期常量(如 ZLIB_VERSION: t.CDefine = "1.3.2")
|
if pool is None or name is None: return
|
||||||
|
|
||||||
字符串值深拷贝到 pool(AST 节点的 str_val 可能被 pool 操作覆写,
|
|
||||||
与浮点 CDefine 的恢复同理)。名称同样深拷贝(理由见
|
|
||||||
register_cdefine_constant:注入 .vpi 的 Name.id 指向会被释放的 vpi_buf)。
|
|
||||||
"""
|
|
||||||
if pool is None or name is None:
|
|
||||||
return
|
|
||||||
if value is None:
|
if value is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
|
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string value missing: {name}", "InternalCompilerError")
|
||||||
return
|
return
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None:
|
||||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||||
tn: list[str]* = list[str](tp, 64)
|
tn: list[str]* = list[str](tp, 64)
|
||||||
tv: list[str]* = list[str](tp, 64)
|
tv: list[str]* = list[str](tp, 64)
|
||||||
td: list[int] | t.CPtr = list[int](tp, 64)
|
td: list[int]* = list[int](tp, 64)
|
||||||
tsf: list[int] | t.CPtr = list[int](tp, 64)
|
tsf: list[int]* = list[int](tp, 64)
|
||||||
if tn is None or tv is None or td is None or tsf is None:
|
tbits: list[int]* = list[int](tp, 64)
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表分配失败(表本体池不可用或耗尽)", "MemoryError")
|
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||||
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||||
return
|
return
|
||||||
self.CDefineNames = tn
|
self.CDefineNames = tn
|
||||||
self.CDefineValues = tv
|
self.CDefineValues = tv
|
||||||
self.CDefineDoubleFlags = td
|
self.CDefineDoubleFlags = td
|
||||||
self.CDefineStrFlags = tsf
|
self.CDefineStrFlags = tsf
|
||||||
|
self.CDefineBits = tbits
|
||||||
self._cdef_idx_init(tp)
|
self._cdef_idx_init(tp)
|
||||||
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
|
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||||
if self.CDefineNames is None or self.CDefineValues is None \
|
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||||
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine 字符串表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
|
|
||||||
return
|
return
|
||||||
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
|
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
|
||||||
if ncopy_s is None:
|
if ncopy_s is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串 '{name}' 名称缓冲区分配失败", "MemoryError")
|
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string '{name}' name buffer allocation failed", "MemoryError")
|
||||||
return
|
return
|
||||||
string.strcpy(ncopy_s, name)
|
string.strcpy(ncopy_s, name)
|
||||||
self.CDefineNames.append(ncopy_s)
|
self.CDefineNames.append(ncopy_s)
|
||||||
@@ -294,166 +237,145 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
vlen: sizet = string.strlen(value)
|
vlen: sizet = string.strlen(value)
|
||||||
vcopy: str = pool.alloc(vlen + 1)
|
vcopy: str = pool.alloc(vlen + 1)
|
||||||
if vcopy is None:
|
if vcopy is None:
|
||||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值分配失败: {name}", "MemoryError")
|
self.Gen.TypeGenErr.fatal_error(None, f"CDefine string value allocation failed: {name}", "MemoryError")
|
||||||
return
|
return
|
||||||
string.strcpy(vcopy, value)
|
string.strcpy(vcopy, value)
|
||||||
self.CDefineValues.append(vcopy)
|
self.CDefineValues.append(vcopy)
|
||||||
if self.CDefineDoubleFlags is not None:
|
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||||
self.CDefineDoubleFlags.append(0)
|
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1)
|
||||||
if self.CDefineStrFlags is not None:
|
if self.CDefineBits is not None: self.CDefineBits.append(0)
|
||||||
self.CDefineStrFlags.append(1)
|
# Also set the "last result is a string" flag (read by is_cdefine_str()).
|
||||||
# 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。
|
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
|
||||||
# 跨模块字符串 CDefine 读回只调本函数 + 尾部 set_cdefine_found;不设标志会让
|
|
||||||
# 属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 → IR 生成 inttoptr i32 0
|
|
||||||
# → 运行期按 NULL 字符串处理崩溃。
|
|
||||||
self.CDefineIsStr = 1
|
self.CDefineIsStr = 1
|
||||||
self.CDefineFound = 1
|
self.CDefineFound = 1
|
||||||
# ============================================================
|
# Cross-module CDefine lookup cache (PermPool-allocated, reused across files)
|
||||||
# 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用)
|
def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None:
|
||||||
# ============================================================
|
"""Cache a cross-module CDefine lookup result in the perm_pool global cache.
|
||||||
def register_xmod_cdefine(self, name: str, value_str: str,
|
|
||||||
is_double: int, is_str: int) -> None:
|
|
||||||
"""注册跨模块 CDefine 查找结果到 perm_pool 全局缓存(跨文件复用)
|
|
||||||
|
|
||||||
Args:
|
is_double 沿用 CDefineDoubleFlags 的编码(1=f64,2=f32,0=非浮点),
|
||||||
name: CDefine 常量名(如 CONST_INT)
|
这样跨模块读取 float 声明的常量时也能保持 f32。
|
||||||
value_str: 值文本(十进制/浮点/字符串)
|
|
||||||
is_double: 1=浮点值
|
|
||||||
is_str: 1=字符串值
|
|
||||||
"""
|
"""
|
||||||
if name is None or value_str is None:
|
if name is None or value_str is None: return
|
||||||
return
|
# Dedupe: skip if cached (first lookup wins; CDefines are globally unique)
|
||||||
# 去重:同名已缓存则跳过(首次查找结果为准,CDefine 全局唯一约定)
|
|
||||||
if self.XmodCdefNames is not None:
|
if self.XmodCdefNames is not None:
|
||||||
x_n: sizet = len(self.XmodCdefNames)
|
x_n: sizet = len(self.XmodCdefNames)
|
||||||
x_i: sizet
|
x_i: sizet
|
||||||
for x_i in range(x_n):
|
for x_i in range(int(x_n)):
|
||||||
x_nm: str = self.XmodCdefNames.get(x_i)
|
x_nm: str = self.XmodCdefNames.get(x_i)
|
||||||
if x_nm is not None and string.strcmp(x_nm, name) == 0:
|
if x_nm is not None and string.strcmp(x_nm, name) == 0: return
|
||||||
return
|
|
||||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||||
if self.XmodCdefNames is None:
|
if self.XmodCdefNames is None:
|
||||||
self.XmodCdefNames = list[str](perm, 64)
|
self.XmodCdefNames = list[str](perm, 64)
|
||||||
self.XmodCdefValues = list[str](perm, 64)
|
self.XmodCdefValues = list[str](perm, 64)
|
||||||
self.XmodCdefDoubleFlags = list[int](perm, 64)
|
self.XmodCdefDoubleFlags = list[int](perm, 64)
|
||||||
self.XmodCdefStrFlags = list[int](perm, 64)
|
self.XmodCdefStrFlags = list[int](perm, 64)
|
||||||
# key/value 深拷贝到 perm_pool:入参(pool.alloc 临时缓冲)在
|
# Deep-copy key/value into perm_pool: the inputs (pool.alloc temporaries) dangle after
|
||||||
# reset_to(mark2) 后悬空,append 只存指针 → 必须深拷贝跨 reset 存活
|
# reset_to(mark2), so appending pointers alone would not survive the reset.
|
||||||
klen: sizet = string.strlen(name)
|
klen: sizet = string.strlen(name)
|
||||||
kcopy: str = perm.alloc(klen + 1)
|
kcopy: str = perm.alloc(klen + 1)
|
||||||
if kcopy is None:
|
if kcopy is None: return
|
||||||
return
|
|
||||||
string.strcpy(kcopy, name)
|
string.strcpy(kcopy, name)
|
||||||
vlen: sizet = string.strlen(value_str)
|
vlen: sizet = string.strlen(value_str)
|
||||||
vcopy: str = perm.alloc(vlen + 1)
|
vcopy: str = perm.alloc(vlen + 1)
|
||||||
if vcopy is None:
|
if vcopy is None: return
|
||||||
return
|
|
||||||
string.strcpy(vcopy, value_str)
|
string.strcpy(vcopy, value_str)
|
||||||
self.XmodCdefNames.append(kcopy)
|
self.XmodCdefNames.append(kcopy)
|
||||||
self.XmodCdefValues.append(vcopy)
|
self.XmodCdefValues.append(vcopy)
|
||||||
if self.XmodCdefDoubleFlags is not None:
|
if self.XmodCdefDoubleFlags is not None: self.XmodCdefDoubleFlags.append(is_double)
|
||||||
self.XmodCdefDoubleFlags.append(is_double)
|
if self.XmodCdefStrFlags is not None: self.XmodCdefStrFlags.append(is_str)
|
||||||
if self.XmodCdefStrFlags is not None:
|
|
||||||
self.XmodCdefStrFlags.append(is_str)
|
|
||||||
def lookup_xmod_cdefine(self, name: str) -> str:
|
def lookup_xmod_cdefine(self, name: str) -> str:
|
||||||
"""查找跨模块 CDefine 缓存,命中设置 found/is_double/is_str 标志
|
"""Look up the cross-module CDefine cache and set the found/is_double/is_str flags."""
|
||||||
|
|
||||||
Returns:
|
|
||||||
值文本(命中),未命中返回 None
|
|
||||||
"""
|
|
||||||
self.CDefineFound = 0
|
self.CDefineFound = 0
|
||||||
self.CDefineIsDouble = 0
|
self.CDefineIsDouble = 0
|
||||||
self.CDefineIsStr = 0
|
self.CDefineIsStr = 0
|
||||||
if name is None or self.XmodCdefNames is None:
|
if name is None or self.XmodCdefNames is None: return None
|
||||||
return None
|
|
||||||
n: sizet = len(self.XmodCdefNames)
|
n: sizet = len(self.XmodCdefNames)
|
||||||
i: sizet
|
i: sizet
|
||||||
for i in range(n):
|
for i in range(int(n)):
|
||||||
nm: str = self.XmodCdefNames.get(i)
|
nm: str = self.XmodCdefNames.get(i)
|
||||||
if nm is not None and string.strcmp(nm, name) == 0:
|
if nm is not None and string.strcmp(nm, name) == 0:
|
||||||
val_str: str = self.XmodCdefValues.get(i)
|
val_str: str = self.XmodCdefValues.get(i)
|
||||||
if val_str is not None:
|
if val_str is not None:
|
||||||
self.CDefineFound = 1
|
self.CDefineFound = 1
|
||||||
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1:
|
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1: self.CDefineIsStr = 1
|
||||||
self.CDefineIsStr = 1
|
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) != 0:
|
||||||
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1:
|
self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i))
|
||||||
self.CDefineIsDouble = 1
|
|
||||||
return val_str
|
return val_str
|
||||||
return None
|
return None
|
||||||
def lookup_cdefine_constant(self, name: str) -> int64:
|
def lookup_cdefine_constant(self, name: str) -> int64:
|
||||||
"""查找 CDefine 编译期常量
|
"""Look up an integer CDefine constant; 64-bit exact, found flag distinguishes -1."""
|
||||||
|
|
||||||
返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。
|
|
||||||
由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1),
|
|
||||||
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
|
|
||||||
调用方应在调用后检查 _g_cdefine_found。
|
|
||||||
浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。
|
|
||||||
"""
|
|
||||||
self.CDefineFound = 0
|
self.CDefineFound = 0
|
||||||
self.CDefineIsDouble = 0
|
self.CDefineIsDouble = 0
|
||||||
self.CDefineIsStr = 0
|
self.CDefineIsStr = 0
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None: return 0
|
||||||
return 0
|
|
||||||
n: sizet = len(self.CDefineNames)
|
n: sizet = len(self.CDefineNames)
|
||||||
i: sizet
|
i: sizet
|
||||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0(= 原全表扫描
|
|
||||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
|
||||||
cd_start: sizet = 0
|
cd_start: sizet = 0
|
||||||
cd_end: sizet = n
|
cd_end: sizet = n
|
||||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
|
||||||
cd_hit: int = self._cdef_idx_find(name)
|
cd_hit: int = self._cdef_idx_find(name)
|
||||||
if cd_hit < 0:
|
if cd_hit < 0: return 0
|
||||||
return 0
|
|
||||||
cd_start = sizet(cd_hit)
|
cd_start = sizet(cd_hit)
|
||||||
cd_end = sizet(cd_hit + 1)
|
cd_end = sizet(cd_hit + 1)
|
||||||
for i in range(cd_start, cd_end):
|
for i in range(int(cd_start), int(cd_end)):
|
||||||
nm: str = self.CDefineNames.get(i)
|
nm: str = self.CDefineNames.get(i)
|
||||||
if nm is not None and string.strcmp(nm, name) == 0:
|
if nm is not None and string.strcmp(nm, name) == 0:
|
||||||
val_str: str = self.CDefineValues.get(i)
|
val_str: str = self.CDefineValues.get(i)
|
||||||
if val_str is not None:
|
if val_str is not None:
|
||||||
self.CDefineFound = 1
|
self.CDefineFound = 1
|
||||||
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
||||||
# 字符串 CDefine(如 ZLIB_VERSION="1.3.2"):整数查询路径
|
# String CDefine (e.g. ZLIB_VERSION="1.3.2"): mark is_str on the int path;
|
||||||
# 标记 is_str,调用方改用 lookup_cdefine_string 取字符串。
|
# callers should use lookup_cdefine_string instead.
|
||||||
self.CDefineIsStr = 1
|
self.CDefineIsStr = 1
|
||||||
return 0
|
return 0
|
||||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
|
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
|
||||||
self.CDefineIsDouble = 1
|
# 浮点常量(f64=1 / f32=2):把种类原样带给 is_cdefine_double()/is_cdefine_float32()
|
||||||
|
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
|
||||||
return string.atoll(val_str)
|
return string.atoll(val_str)
|
||||||
return string.atoll(val_str)
|
return string.atoll(val_str)
|
||||||
return 0
|
return 0
|
||||||
def lookup_cdefine_double(self, name: str) -> double:
|
def lookup_cdefine_bits(self, name: str) -> int:
|
||||||
"""查找 CDefine 编译期常量,返回 double 值(浮点/整数统一)
|
"""Look up a CDefine constant's value-expression bit width; 0 = unknown (width inferred by value).
|
||||||
|
|
||||||
与 lookup_cdefine_constant 共享 _g_cdefine_found / _g_cdefine_is_double
|
同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi
|
||||||
(found=1 且 is_double=1 → 浮点 CDefine,应返回浮点常量)。
|
等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录,
|
||||||
|
于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。
|
||||||
"""
|
"""
|
||||||
|
if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0
|
||||||
|
n: sizet = len(self.CDefineNames)
|
||||||
|
# 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值),
|
||||||
|
# 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录,
|
||||||
|
# 而不是"命中即返回"或"第一条非零"——否则早退的 stub 记录会盖掉真实位宽。
|
||||||
|
found: int = 0
|
||||||
|
for i in range(int(n)):
|
||||||
|
nm: str = self.CDefineNames.get(i)
|
||||||
|
if nm is None or string.strcmp(nm, name) != 0: continue
|
||||||
|
if sizet(i) >= len(self.CDefineBits): continue
|
||||||
|
b: int = self.CDefineBits.get(i)
|
||||||
|
if b != 0: found = b
|
||||||
|
return found
|
||||||
|
def lookup_cdefine_double(self, name: str) -> double:
|
||||||
|
"""Look up a CDefine constant and return its double value (float or int)."""
|
||||||
self.CDefineFound = 0
|
self.CDefineFound = 0
|
||||||
self.CDefineIsDouble = 0
|
self.CDefineIsDouble = 0
|
||||||
self.CDefineIsStr = 0
|
self.CDefineIsStr = 0
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None: return 0.0
|
||||||
return 0.0
|
|
||||||
n: sizet = len(self.CDefineNames)
|
n: sizet = len(self.CDefineNames)
|
||||||
i: sizet
|
i: sizet
|
||||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 0.0(= 原全表扫描
|
|
||||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
|
||||||
cd_start: sizet = 0
|
cd_start: sizet = 0
|
||||||
cd_end: sizet = n
|
cd_end: sizet = n
|
||||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
|
||||||
cd_hit: int = self._cdef_idx_find(name)
|
cd_hit: int = self._cdef_idx_find(name)
|
||||||
if cd_hit < 0:
|
if cd_hit < 0: return 0.0
|
||||||
return 0.0
|
|
||||||
cd_start = sizet(cd_hit)
|
cd_start = sizet(cd_hit)
|
||||||
cd_end = sizet(cd_hit + 1)
|
cd_end = sizet(cd_hit + 1)
|
||||||
for i in range(cd_start, cd_end):
|
for i in range(int(cd_start), int(cd_end)):
|
||||||
nm: str = self.CDefineNames.get(i)
|
nm: str = self.CDefineNames.get(i)
|
||||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
|
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs.
|
||||||
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
|
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
|
||||||
val_str: str = self.CDefineValues.get(i)
|
val_str: str = self.CDefineValues.get(i)
|
||||||
if val_str is not None:
|
if val_str is not None:
|
||||||
@@ -461,40 +383,32 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
||||||
self.CDefineIsStr = 1
|
self.CDefineIsStr = 1
|
||||||
return 0.0
|
return 0.0
|
||||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
|
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
|
||||||
self.CDefineIsDouble = 1
|
# 种类原样带回(1=f64 / 2=f32),物化处据此决定出 f64 还是 f32 常量。
|
||||||
|
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
|
||||||
ret_d: double = string.atof(val_str)
|
ret_d: double = string.atof(val_str)
|
||||||
return ret_d
|
return ret_d
|
||||||
ret_i2: double = double(string.atoll(val_str))
|
ret_i2: double = double(string.atoll(val_str))
|
||||||
return ret_i2
|
return ret_i2
|
||||||
return 0.0
|
return 0.0
|
||||||
def lookup_cdefine_string(self, name: str) -> str:
|
def lookup_cdefine_string(self, name: str) -> str:
|
||||||
"""查找 CDefine 编译期字符串值(found=1 且 is_str=1 时有效)
|
"""Look up a string CDefine value (valid when found=1 and is_str=1)."""
|
||||||
|
|
||||||
返回的字符串指针位于 pool 内(当前模块生命周期内有效),
|
|
||||||
调用方应尽快用于 create_global_string 拷贝到 LLVM 常量。
|
|
||||||
"""
|
|
||||||
self.CDefineFound = 0
|
self.CDefineFound = 0
|
||||||
self.CDefineIsDouble = 0
|
self.CDefineIsDouble = 0
|
||||||
self.CDefineIsStr = 0
|
self.CDefineIsStr = 0
|
||||||
if self.CDefineNames is None:
|
if self.CDefineNames is None: return None
|
||||||
return None
|
|
||||||
n: sizet = len(self.CDefineNames)
|
n: sizet = len(self.CDefineNames)
|
||||||
i: sizet
|
i: sizet
|
||||||
# 名索引快速路径:可信(Built==n | 查询名非空)时直接定位唯一候选下标
|
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
|
||||||
# (= 原扫描首个命中)。CDefine 名注册后不再改写、每次注册都同步入索引
|
# (== first scan hit); if unavailable fall back to the full linear scan below.
|
||||||
# (失败即置 Built=-1)⇒ 可信时未命中即"确无",直接返回 None(= 原全表扫描
|
|
||||||
# 结果);索引不可用时回退下方原全表线性扫描。
|
|
||||||
cd_start: sizet = 0
|
cd_start: sizet = 0
|
||||||
cd_end: sizet = n
|
cd_end: sizet = n
|
||||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
|
||||||
cd_hit: int = self._cdef_idx_find(name)
|
cd_hit: int = self._cdef_idx_find(name)
|
||||||
if cd_hit < 0:
|
if cd_hit < 0: return None
|
||||||
return None
|
|
||||||
cd_start = sizet(cd_hit)
|
cd_start = sizet(cd_hit)
|
||||||
cd_end = sizet(cd_hit + 1)
|
cd_end = sizet(cd_hit + 1)
|
||||||
for i in range(cd_start, cd_end):
|
for i in range(int(cd_start), int(cd_end)):
|
||||||
nm: str = self.CDefineNames.get(i)
|
nm: str = self.CDefineNames.get(i)
|
||||||
if nm is not None and string.strcmp(nm, name) == 0:
|
if nm is not None and string.strcmp(nm, name) == 0:
|
||||||
val_str: str = self.CDefineValues.get(i)
|
val_str: str = self.CDefineValues.get(i)
|
||||||
@@ -506,42 +420,40 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
|||||||
return val_str
|
return val_str
|
||||||
return None
|
return None
|
||||||
def is_cdefine_str(self) -> int:
|
def is_cdefine_str(self) -> int:
|
||||||
"""检查最近一次 CDefine 查找命中的是否是字符串常量"""
|
"""Whether the last CDefine hit was a string constant."""
|
||||||
return self.CDefineIsStr
|
return self.CDefineIsStr
|
||||||
def is_cdefine_double(self) -> int:
|
def is_cdefine_double(self) -> int:
|
||||||
"""检查最近一次 CDefine 查找命中的是否是浮点常量"""
|
"""Whether the last CDefine hit was a float constant."""
|
||||||
return self.CDefineIsDouble
|
return self.CDefineIsDouble
|
||||||
|
def is_cdefine_float32(self) -> int:
|
||||||
|
"""Whether the last CDefine hit was an f32 (`float`) constant; 0 = f64/非浮点。
|
||||||
|
|
||||||
|
物化浮点常量时必须先看本函数:`define PI: float = ...` 要直接出 f32 常量,
|
||||||
|
否则与 f32 字面量混算会被提升成 double(现场:33 处 cvtss2sd)。
|
||||||
|
"""
|
||||||
|
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return 1
|
||||||
|
return 0
|
||||||
|
def xmod_float_kind(self) -> int:
|
||||||
|
"""跨模块缓存写入用的浮点种类(1=f64 / 2=f32):把编码留在本模块,写入方不必跨模块引用 define。"""
|
||||||
|
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return CDEFINE_FLOAT_KIND_F32
|
||||||
|
return CDEFINE_FLOAT_KIND_F64
|
||||||
def is_cdefine_found(self) -> int:
|
def is_cdefine_found(self) -> int:
|
||||||
"""检查上次 lookup_cdefine_constant 是否找到值"""
|
"""Whether the last CDefine lookup found a value."""
|
||||||
return self.CDefineFound
|
return self.CDefineFound
|
||||||
def set_cdefine_found(self, val: int) -> None:
|
def set_cdefine_found(self, val: int) -> None:
|
||||||
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
|
"""Set the cdefine_found flag (used by cross-module lookup functions)."""
|
||||||
self.CDefineFound = val
|
self.CDefineFound = val
|
||||||
def clear_cdefine_constants(self) -> None:
|
def clear_cdefine_constants(self) -> None:
|
||||||
"""清空 CDefine 常量表(模块切换时调用)
|
"""Clear the CDefine table body in place, keeping the PermPool table pointers."""
|
||||||
|
if self.CDefineNames is not None: self.CDefineNames.clear()
|
||||||
表本体自首次创建起常驻 PermPool(见 _cdefine_table_pool)⇒ 此处只
|
if self.CDefineValues is not None: self.CDefineValues.clear()
|
||||||
**清空内容**、不丢弃表指针:
|
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
|
||||||
- 丢指针会退回"下一次 register_cdefine_* 按**调用方 pool** 重建",
|
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
|
||||||
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
|
if self.CDefineBits is not None: self.CDefineBits.clear()
|
||||||
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
|
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
|
||||||
初始化内存的根因);
|
# in sync, matching a freshly built table before its first register).
|
||||||
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
|
|
||||||
register 首次 append 即写回第 0 槽。
|
|
||||||
"""
|
|
||||||
if self.CDefineNames is not None:
|
|
||||||
self.CDefineNames.clear()
|
|
||||||
if self.CDefineValues is not None:
|
|
||||||
self.CDefineValues.clear()
|
|
||||||
if self.CDefineDoubleFlags is not None:
|
|
||||||
self.CDefineDoubleFlags.clear()
|
|
||||||
if self.CDefineStrFlags is not None:
|
|
||||||
self.CDefineStrFlags.clear()
|
|
||||||
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
|
|
||||||
# 与"新建表后首次 register"的索引状态逐字一致)
|
|
||||||
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||||
string.memset(self.CDefineNameIdx, 0,
|
string.memset(self.CDefineNameIdx, 0, sizet(self.CDefineNameIdxCap) * 8)
|
||||||
sizet(self.CDefineNameIdxCap) * 8)
|
|
||||||
self.CDefineNameIdxBuilt = 0
|
self.CDefineNameIdxBuilt = 0
|
||||||
else:
|
else:
|
||||||
self.CDefineNameIdx = None
|
self.CDefineNameIdx = None
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
+197
-364
@@ -1,327 +1,209 @@
|
|||||||
import lib.core.Gen.GenTypes as GenTypes
|
import lib.core.Gen.GenTypes as GenTypes
|
||||||
import lib.core.Gen.Generator as Generator
|
|
||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
import ast
|
||||||
import llvmlite
|
import mlirlite
|
||||||
import memhub
|
import memhub
|
||||||
import string
|
import string
|
||||||
import viperlib
|
import viperlib
|
||||||
import stdio
|
import stdio
|
||||||
import stdlib
|
import lib.core.Diag as Diag
|
||||||
import sys
|
|
||||||
import w32.fileio as fileio
|
|
||||||
import lib.core.VLogger as VLogger
|
|
||||||
import lib.Stub.StubMerger as StubMerger
|
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
|
||||||
import lib.core.Gen.Translator as HT
|
import lib.core.Gen.Translator as HT
|
||||||
|
|
||||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# TypeGenErr - error reporting / source line reading / type inference (fatal_error, infer_*, ...)
|
||||||
# TypeGenErr - 错误上报/源行读取/类型推断(fatal_error/infer_* 等)
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||||
"""错误上报/源行读取/类型推断(fatal_error/infer_* 等)"""
|
"""Error reporting / source-line reading / type inference (fatal_error/infer_* etc.)"""
|
||||||
|
|
||||||
def _read_source_line(self, file_path: str, lineno: int) -> str:
|
def _read_source_line(self, file_path: str, lineno: int) -> str:
|
||||||
"""从源文件读取第 lineno 行内容"""
|
"""Read line lineno from the source file (delegates to the Diag module)"""
|
||||||
if file_path is None or lineno <= 0: return None
|
return Diag.read_source_line(file_path, lineno)
|
||||||
f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R)
|
|
||||||
if f.closed: return None
|
|
||||||
# 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 →
|
|
||||||
# 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第
|
|
||||||
# 8390 行)。64MB 上限防御异常超大文件整体读取。
|
|
||||||
fsize: LONGLONG = f.size()
|
|
||||||
if fsize <= 0 or fsize > 67108864:
|
|
||||||
f.close()
|
|
||||||
return None
|
|
||||||
src_size: sizet = sizet(fsize)
|
|
||||||
buf: bytes = stdlib.malloc(src_size + 1)
|
|
||||||
if buf is None:
|
|
||||||
f.close()
|
|
||||||
return None
|
|
||||||
bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1))
|
|
||||||
f.close()
|
|
||||||
if bytes_read <= 0:
|
|
||||||
stdlib.free(buf)
|
|
||||||
return None
|
|
||||||
# 逐行查找第 lineno 行
|
|
||||||
cur_line: int = 1
|
|
||||||
line_start: sizet = 0
|
|
||||||
for i in range(bytes_read):
|
|
||||||
if buf[i] == '\n':
|
|
||||||
if cur_line == lineno:
|
|
||||||
line_len: sizet = i - line_start
|
|
||||||
if line_len > 0 and buf[line_start + line_len - 1] == '\r':
|
|
||||||
line_len -= 1
|
|
||||||
line_buf: bytes = stdlib.malloc(line_len + 1)
|
|
||||||
if line_buf is not None:
|
|
||||||
string.strncpy(line_buf, buf + line_start, line_len)
|
|
||||||
line_buf[line_len] = '\0'
|
|
||||||
stdlib.free(buf)
|
|
||||||
return line_buf
|
|
||||||
stdlib.free(buf)
|
|
||||||
return None
|
|
||||||
cur_line += 1
|
|
||||||
line_start = i + 1
|
|
||||||
stdlib.free(buf)
|
|
||||||
return None
|
|
||||||
def read_source_line(self, file_path: str, lineno: int) -> str:
|
def read_source_line(self, file_path: str, lineno: int) -> str:
|
||||||
"""公开包装:从源文件读取第 lineno 行内容(供 traceback 展示)"""
|
"""Public wrapper: read line lineno from the source file for traceback display"""
|
||||||
return self._read_source_line(file_path, lineno)
|
return self._read_source_line(file_path, lineno)
|
||||||
def dump_parse_traceback(self, file_path: str) -> None:
|
def dump_parse_traceback(self, file_path: str) -> None:
|
||||||
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
|
"""On parse failure, print a Python-style traceback (delegates to the Diag module)"""
|
||||||
err_line: int = ast.get_parse_err_line()
|
Diag.emit_parse(file_path, ast.get_parse_err_line(), ast.get_parse_err_col(), ast.get_parse_err_msg())
|
||||||
err_col: int = ast.get_parse_err_col()
|
# 注:返回类型必须显式给一个**真实类型**(int),不能留空、也不能用 None。
|
||||||
err_msg: str = ast.get_parse_err_msg()
|
# 本方法是"必定终止"的 fail-fast,从不返回;但留空/None 都会让跨模块 call site 解析出
|
||||||
stdio.printf("Traceback (most recent call last, compile-time error):\n")
|
# 退化类型 i0,命中 CallGenCall.vp 的 i0 守卫 → _fail_method_ret_ty("fatal_error") 中止编译。
|
||||||
if file_path is None or file_path[0] == 0:
|
# 给 int 后解析为 i32,守卫不再命中;因从不返回,该类型不会被真正使用。
|
||||||
file_path = "<unknown>"
|
def fatal_error(self, node: ast.AST*, msg: str, err_type: str = "SyntaxError", col_start: int = 0, col_end: int = 0, leak_file: str = None, leak_line: int = 0) -> int:
|
||||||
if err_line > 0:
|
"""Report an error in Python style and exit (single diagnostic carrying its full call chain).
|
||||||
stdio.printf(' File "%s", line %d\n', file_path, err_line)
|
|
||||||
src: str = self._read_source_line(file_path, err_line)
|
|
||||||
if src is not None:
|
|
||||||
stdio.printf(" %s\n", src)
|
|
||||||
# 列标记:在出错 token 起始列下方标 ^(parser 的 col_offset 是 0-based,
|
|
||||||
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
|
|
||||||
if err_col > 0:
|
|
||||||
stdio.printf(" ")
|
|
||||||
for _ in range(err_col):
|
|
||||||
stdio.printf(" ")
|
|
||||||
stdio.printf("^\n")
|
|
||||||
stdlib.free(src)
|
|
||||||
else:
|
|
||||||
stdio.printf(' File "%s"\n', file_path)
|
|
||||||
if err_msg is None or err_msg[0] == 0:
|
|
||||||
err_msg = "语法错误"
|
|
||||||
stdio.printf("SyntaxError: %s\n", err_msg)
|
|
||||||
def fatal_error(self, node: ast.AST*, msg: str,
|
|
||||||
err_type: str = "SyntaxError",
|
|
||||||
col_start: int = 0, col_end: int = 0):
|
|
||||||
"""以 Python 风格报错并退出(支持多帧、错误类型、列范围)
|
|
||||||
|
|
||||||
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
|
leak_line > 0 时额外打印一帧 `File lifetime leak "<file>", line N`(逃逸类错误的泄漏点)。
|
||||||
"""
|
"""
|
||||||
stdio.printf("Traceback (most recent call last, compile-time error):\n")
|
# 当前帧(错误点)位置:优先 node,其次调用栈最内层有行号的帧,最后 current_file。
|
||||||
# 输出调用栈帧(从底到顶)
|
|
||||||
for i in range(self.Gen.TypeGenBase.TraceCount):
|
|
||||||
frame: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(i)
|
|
||||||
if frame is not None and frame.file_name is not None:
|
|
||||||
if frame.lineno > 0:
|
|
||||||
stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno)
|
|
||||||
src: str = self._read_source_line(frame.file_name, frame.lineno)
|
|
||||||
if src is not None:
|
|
||||||
stdio.printf(" %s\n", src)
|
|
||||||
stdlib.free(src)
|
|
||||||
else:
|
|
||||||
stdio.printf(' File "%s"\n', frame.file_name)
|
|
||||||
# 输出当前帧(node 对应的位置)
|
|
||||||
lineno: int = 0
|
lineno: int = 0
|
||||||
if node is not None:
|
if node is not None: lineno = node.lineno
|
||||||
lineno = node.lineno
|
raw_line: int = lineno # [TEMP-DIAG] 兜底前的原始节点行号(定位完删除)
|
||||||
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
||||||
if file_name is None:
|
if file_name is None: file_name = "<unknown>"
|
||||||
file_name = "<unknown>"
|
# 报错位置兜底(“节点位置不可信”时优先用正在翻译的实参位置,精确定位到
|
||||||
# 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的
|
# `&_on_submit` 这类表达式;见 TypeGenBase.set_cur_arg)。判据取宽:节点无行号、
|
||||||
# ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个
|
# 或**没有更深的可用位置**(节点行号不深于栈最内层帧的行号 —— 函数体里的真实
|
||||||
# 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。
|
# 表达式行号必然大于函数定义行,而 FunctionDef 自身恰好等于定义行/更浅),
|
||||||
|
# 或栈为空(顶层函数体翻译时可能没有帧)时,都采用实参位置。
|
||||||
|
deeper_known: int = 0
|
||||||
|
if lineno > 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
||||||
|
tfc: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
|
||||||
|
if tfc is not None and tfc.lineno > 0 and lineno > tfc.lineno: deeper_known = 1
|
||||||
|
if (lineno <= 0 or deeper_known == 0) and self.Gen.TypeGenBase.CurArgLine > 0:
|
||||||
|
if self.Gen.TypeGenBase.CurArgFile is not None:
|
||||||
|
file_name = self.Gen.TypeGenBase.CurArgFile
|
||||||
|
lineno = self.Gen.TypeGenBase.CurArgLine
|
||||||
|
col_start = self.Gen.TypeGenBase.CurArgCol
|
||||||
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
||||||
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
|
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
|
||||||
while ti_b >= 0:
|
while ti_b >= 0:
|
||||||
tfb: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(ti_b)
|
tfb: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(ti_b)
|
||||||
if tfb is not None and tfb.file_name is not None and tfb.lineno > 0:
|
if tfb is not None and tfb.file_name is not None and tfb.lineno > 0:
|
||||||
file_name = tfb.file_name
|
file_name = tfb.file_name
|
||||||
lineno = tfb.lineno
|
lineno = tfb.lineno
|
||||||
break
|
break
|
||||||
ti_b -= 1
|
ti_b -= 1
|
||||||
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
|
|
||||||
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
|
|
||||||
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
|
|
||||||
src_line: str = None
|
|
||||||
line_ok: int = 1
|
|
||||||
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
|
||||||
src_line = self._read_source_line(file_name, lineno)
|
|
||||||
if src_line is None:
|
|
||||||
line_ok = 0
|
|
||||||
if line_ok == 0:
|
|
||||||
stdio.printf(' File "%s", line %d (!! 无法读取该行源码:文件不存在或行号超出范围,错误可能来自被复用的 AST 树,请以错误消息为准)\n',
|
|
||||||
file_name, lineno)
|
|
||||||
else:
|
|
||||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
|
||||||
if src_line is not None:
|
|
||||||
stdio.printf(" %s\n", src_line)
|
|
||||||
# 列范围标记:在源代码行下方用 ^ 标记出错范围
|
|
||||||
if col_start > 0 and col_end >= col_start:
|
|
||||||
stdio.printf(" ")
|
|
||||||
j: int
|
|
||||||
for j in range(col_end):
|
|
||||||
if j >= col_start - 1 and j < col_end:
|
|
||||||
stdio.printf("^")
|
|
||||||
else:
|
|
||||||
stdio.printf(" ")
|
|
||||||
stdio.printf("\n")
|
|
||||||
stdlib.free(src_line)
|
|
||||||
# 输出错误类型 + 消息
|
|
||||||
if err_type is None or err_type[0] == 0:
|
|
||||||
err_type = "SyntaxError"
|
|
||||||
stdio.printf("%s: %s\n", err_type, msg)
|
|
||||||
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,直接终止进程会把整段
|
|
||||||
# traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
|
|
||||||
stdio.fflush(None)
|
|
||||||
# 终止点收敛:REPL 会话内经 VLogger.repl_abort longjmp 回 REPL 循环
|
|
||||||
# (Traceback 已打印,进程存活);批处理下该出口即 sys.exit(1),逐字不变。
|
|
||||||
VLogger.repl_abort()
|
|
||||||
|
|
||||||
def fatal_type_error(self, node: ast.AST*, msg: str,
|
# 单条诊断(自带到错误的完整调用链);打印交给 Diag。
|
||||||
col_start: int = 0, col_end: int = 0):
|
ent: Diag.DiagEntry* = Diag.new_entry(Diag.DIAG_MAX_FRAMES)
|
||||||
"""以 Python 风格报错并退出(TypeError 版本)"""
|
if ent is None:
|
||||||
self.fatal_error(node, msg, "TypeError", col_start, col_end)
|
# 极端情况(分配失败):退回单帧输出,保证错误不被静默吞掉。
|
||||||
def try_extract_callable_ret(self, pool: memhub.MemBuddy*,
|
Diag.emit_header()
|
||||||
annot: ast.AST*,
|
Diag.emit_current(file_name, lineno, col_start, col_end)
|
||||||
imported_modules: str,
|
if leak_line > 0: Diag.emit_leak_frame(leak_file, leak_line)
|
||||||
from_imports: str,
|
Diag.emit_category(err_type, msg)
|
||||||
trans: HT.Translator*) -> llvmlite.LLVMType*:
|
Diag.finish()
|
||||||
"""提取函数指针注解(t.Callable[[A,B], R] / callable[[A,B], R])的返回类型
|
|
||||||
LLVMType;非 Callable 注解或无签名形态([]、[[]])返回 None"""
|
|
||||||
if annot is None or annot.kind() != ast.ASTKind.Subscript:
|
|
||||||
return None
|
|
||||||
sub_cr: ast.Subscript* = ast.Subscript*(annot)
|
|
||||||
# 构造子判据走 TypeGenResolve 单一真源(Attribute 形态 t.Callable 与
|
|
||||||
# 裸名形态 callable / Callable 等价)
|
|
||||||
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0:
|
|
||||||
return None
|
|
||||||
# slice 必须是 Tuple(elts=[args_list, ret_node])
|
|
||||||
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple:
|
|
||||||
return None
|
|
||||||
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
|
|
||||||
if tp_cr.elts is None or len(tp_cr.elts) != 2:
|
|
||||||
return None
|
|
||||||
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
|
|
||||||
if ret_node_cr is None:
|
|
||||||
return None
|
|
||||||
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
|
|
||||||
def has_decorator_marker(self, node: ast.AST*,
|
|
||||||
marker_name: str,
|
|
||||||
depth: int = 0) -> int:
|
|
||||||
"""检测类型注解中是否包含指定的装饰器标记"""
|
|
||||||
if node is None or marker_name is None:
|
|
||||||
return 0
|
return 0
|
||||||
# 递归深度守卫:vpi stub 注入时对每个函数 returns 调用本函数,
|
|
||||||
# vpi tree 深层节点可能被覆盖损坏成回指环(VpiReader L810-813),
|
# [TEMP-DIAG] 定位“报错位置”问题用(定位完删除):把决策输入打出来
|
||||||
# BinOp 递归无守卫会无限递归直至栈耗尽。fail-fast 终止。
|
# node=节点原始行号(0=合成) curarg=当前实参行号 trace=栈深 top=栈最内层行号
|
||||||
if depth > 128:
|
ti_top: int = -1
|
||||||
self.fatal_type_error(node, "装饰器标记检测递归过深(AST 环或嵌套超过 128 层)")
|
if self.Gen.TypeGenBase.TraceCount > 0:
|
||||||
|
tf_d: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
|
||||||
|
if tf_d is not None: ti_top = tf_d.lineno
|
||||||
|
stdio.printf("[TEMP-DIAG] node=%d curarg=%d trace=%d top=%d -> shown=%d\n",
|
||||||
|
raw_line, self.Gen.TypeGenBase.CurArgLine, self.Gen.TypeGenBase.TraceCount, ti_top, lineno)
|
||||||
|
Diag.begin(ent, err_type, msg)
|
||||||
|
# 调用链帧(按"自外向内"顺序)**由本类遍历**(只有它拿得到 TraceFrame),填入 entry。
|
||||||
|
for i in range(int(self.Gen.TypeGenBase.TraceCount)):
|
||||||
|
frame: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(i)
|
||||||
|
if frame is not None and frame.file_name is not None:
|
||||||
|
Diag.add_frame(ent, frame.file_name, frame.lineno)
|
||||||
|
Diag.set_current(ent, file_name, lineno, col_start, col_end)
|
||||||
|
# 泄漏点帧(可选):逃逸类错误指出"值是从被调者哪一行漏出去的"
|
||||||
|
Diag.set_leak(ent, leak_file, leak_line)
|
||||||
|
# 默认"向上":最外层触发点在上、错误点最后(Python most recent call last)。
|
||||||
|
Diag.emit(ent, Diag.DIAG_DIR_UP)
|
||||||
|
Diag.free_entry(ent)
|
||||||
|
# Termination convergence: in REPL, longjmp back to the loop via VLogger.repl_abort (the
|
||||||
|
# traceback is printed and the process survives); in batch this exit is sys.exit(1) unchanged.
|
||||||
|
Diag.finish()
|
||||||
|
# 不可达:finish 要么 longjmp 回 REPL 循环、要么 sys.exit(1)。
|
||||||
|
# 显式 return 只为满足上面声明的 -> int 返回类型(避免"缺返回路径")。
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 同 fatal_error:跨模块调用需要**真实**返回类型,否则解析出 i0 会命中守卫中止编译。
|
||||||
|
def fatal_type_error(self, node: ast.AST*, msg: str, col_start: int = 0, col_end: int = 0) -> int:
|
||||||
|
"""Report an error in Python style and exit (TypeError variant)"""
|
||||||
|
return self.fatal_error(node, msg, "TypeError", col_start, col_end)
|
||||||
|
def try_extract_callable_ret(self, pool: memhub.MemBuddy*, annot: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||||
|
"""Extract the return type of a function-pointer annotation (t.Callable[[A,B], R]); None for non-Callable or signature-less forms."""
|
||||||
|
if annot is None or annot.kind() != ast.ASTKind.Subscript: return None
|
||||||
|
sub_cr: ast.Subscript* = ast.Subscript*(annot)
|
||||||
|
# The subscript test uses TypeGenResolve as the single source of truth (the t.Callable
|
||||||
|
# Attribute form is equivalent to the bare-name callable / Callable forms)
|
||||||
|
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0: return None
|
||||||
|
# slice must be Tuple(elts=[args_list, ret_node]); the one-element form
|
||||||
|
# `callable[[A, B]]` omits the return type and means the default int (i32) — the same
|
||||||
|
# degradation a missing annotation gets elsewhere. It must still register, otherwise the
|
||||||
|
# indirect call fails fast with "Return type ... is not registered".
|
||||||
|
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple: return None
|
||||||
|
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
|
||||||
|
if tp_cr.elts is None: return None
|
||||||
|
# The one-element form `callable[[A, B]]` omits the return type, which means **void**
|
||||||
|
# (no return value). It must still register, so the indirect call `f()` is statically
|
||||||
|
# typed as void and using it as a value (e.g. `v: int = f()`) is rejected instead of
|
||||||
|
# silently assuming int.
|
||||||
|
if len(tp_cr.elts) == 1: return mlirlite.Void(pool)
|
||||||
|
if len(tp_cr.elts) != 2: return None
|
||||||
|
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
|
||||||
|
if ret_node_cr is None: return None
|
||||||
|
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
|
||||||
|
def has_decorator_marker(self, node: ast.AST*, marker_name: str, depth: int = 0) -> int:
|
||||||
|
"""Check whether a type annotation contains the given decorator marker"""
|
||||||
|
if node is None or marker_name is None: return 0
|
||||||
|
# Recursion depth guard: vpi stub injection calls this for every function's returns, and
|
||||||
|
# deep vpi nodes may be corrupted into back-pointing cycles (VpiReader L810-813); fail fast.
|
||||||
|
if depth > 128: self.fatal_type_error(node, "decorator marker detection recursion too deep (AST cycle or nesting over 128 levels)")
|
||||||
|
|
||||||
k: int = node.kind()
|
k: int = node.kind()
|
||||||
|
|
||||||
# Attribute 节点: t.CExtern, t.State, t.CExport, t.CInline
|
# Name node: a bare builtin marker (CExtern / State / CExport / CInline / CStatic / CConst ...)
|
||||||
if k == ast.ASTKind.Attribute:
|
if k == ast.ASTKind.Name:
|
||||||
at: ast.Attribute* = ast.Attribute*(node)
|
nm: ast.Name* = ast.Name*(node)
|
||||||
if at.attr is not None:
|
if nm.id is not None and string.strcmp(nm.id, marker_name) == 0: return 1
|
||||||
if string.strcmp(at.attr, marker_name) == 0:
|
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# BinOp 节点: int | t.CExtern(递归检测左右子节点)
|
# Subscript node: a marker wrapper `CConst[T]` / descriptor chain; the marker may be on this
|
||||||
if k == ast.ASTKind.BinOp:
|
# level or nested inside the wrapped type.
|
||||||
bop: ast.BinOp* = ast.BinOp*(node)
|
if k == ast.ASTKind.Subscript:
|
||||||
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0:
|
sub_m: ast.Subscript* = ast.Subscript*(node)
|
||||||
return 1
|
if sub_m.value is not None and sub_m.value.kind() == ast.ASTKind.Name:
|
||||||
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0:
|
mv: ast.Name* = ast.Name*(sub_m.value)
|
||||||
return 1
|
if mv.id is not None and string.strcmp(mv.id, marker_name) == 0: return 1
|
||||||
|
if sub_m.slice is not None:
|
||||||
|
return self.has_decorator_marker(sub_m.slice, marker_name, depth + 1)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
|
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
|
||||||
"""检测指针注解 rawptr[...] 的下标实参是否含 t.CVolatile 标记,1=是 / 0=否
|
"""Whether a rawptr[...] subscript argument carries the t.CVolatile marker (pointee-side volatile), 1=yes / 0=no."""
|
||||||
|
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
|
||||||
语义:rawptr[t.CVolatile | T] 等价 C 的 volatile T*,即"指针所指对象按
|
|
||||||
volatile 访问"——解引用(p[i] / p.field / c.Deref(p))的 load/store 带
|
|
||||||
LLVM volatile。与 has_decorator_marker 的区别:后者检测变量/字段**自身**
|
|
||||||
的可见性限定(如 `locked: t.CVolatile | t.CInt`),对 Subscript 节点返回 0;
|
|
||||||
本函数专检 rawptr 的 pointee 侧标记(下标实参子树),两者互不串味。
|
|
||||||
标注形态(vast.parse 实证):
|
|
||||||
rawptr[t.CVolatile | t.CInt]
|
|
||||||
→ Subscript(value=Name('rawptr'),
|
|
||||||
slice=BinOp(BitOr, Attribute(t,CVolatile), Attribute(t,CInt)))
|
|
||||||
多级指针 rawptr[rawptr[t.CVolatile|T]] 的 slice 是 Subscript,
|
|
||||||
has_decorator_marker 不递归 Subscript ⇒ 返回 0(明确不支持,见限制清单)。
|
|
||||||
"""
|
|
||||||
if node is None or node.kind() != ast.ASTKind.Subscript:
|
|
||||||
return 0
|
|
||||||
sub: ast.Subscript* = ast.Subscript*(node)
|
sub: ast.Subscript* = ast.Subscript*(node)
|
||||||
if sub.value is None or sub.value.kind() != ast.ASTKind.Name:
|
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||||
return 0
|
|
||||||
rp_nm: ast.Name* = ast.Name*(sub.value)
|
rp_nm: ast.Name* = ast.Name*(sub.value)
|
||||||
if rp_nm.id is None or string.strcmp(rp_nm.id, "rawptr") != 0:
|
if rp_nm.id is None: return 0
|
||||||
return 0
|
if string.strcmp(rp_nm.id, "rawptr") != 0 and string.strcmp(rp_nm.id, "CPtr") != 0: return 0
|
||||||
if sub.slice is None:
|
if sub.slice is None: return 0
|
||||||
return 0
|
|
||||||
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
|
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
|
||||||
|
|
||||||
def is_const_object_marker(self, node: ast.AST*,
|
def is_const_object_marker(self, node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
|
||||||
var_ty: llvmlite.LLVMType*) -> int:
|
"""Whether a variable/field is itself a const object (1=yes / 0=no): annotation carries t.CConst and the declared LLVM type is not a pointer."""
|
||||||
"""变量/字段本身是否 **const 对象**(1=是 / 0=否)。
|
if self.has_decorator_marker(node, "CConst", 0) == 0: return 0
|
||||||
|
if var_ty is None: return 0
|
||||||
判据与 TPC 的 BaseGen._IsConstObject **逐条一致**(见 Debugger/type-descriptors.md):
|
if var_ty.kind() == mlirlite.LLVMType.Ptr: return 0
|
||||||
① 注解子树含 t.CConst(has_decorator_marker,与 TPC 的 _AnnotationHasQualifier 同口径);
|
|
||||||
② 声明类型的 LLVM 形态**不是指针**(TPC 用 PtrCount==0 表达同一件事)。
|
|
||||||
`t.CConst` 在本语言里承载「指向的数据 const」(对齐 TPC 的 DataConst 语义),
|
|
||||||
对非指针类型该限定退化为「该对象自身 const」⇒ 登记为 const 变量/字段;
|
|
||||||
对指针类型(`p: t.CConst | T | t.CPtr`)限定只作用于 pointee ⇒ 指针变量自身
|
|
||||||
仍可重新赋值,不登记。
|
|
||||||
"""
|
|
||||||
if self.has_decorator_marker(node, "CConst", 0) == 0:
|
|
||||||
return 0
|
|
||||||
if var_ty is None:
|
|
||||||
return 0
|
|
||||||
if var_ty.kind() == llvmlite.LLVMType.Ptr:
|
|
||||||
return 0
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def is_pass_body(self, body: list[ast.AST*]*) -> int:
|
def is_pass_body(self, body: list[ast.AST*]*) -> int:
|
||||||
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
|
"""Test whether a function body is pass (empty or only a pass statement)"""
|
||||||
if body is None:
|
if body is None: return 1
|
||||||
return 1
|
|
||||||
bn: sizet = len(body)
|
bn: sizet = len(body)
|
||||||
if bn == 0:
|
if bn == 0: return 1
|
||||||
return 1
|
|
||||||
if bn == 1:
|
if bn == 1:
|
||||||
stmt: ast.AST* = body.get(0)
|
stmt: ast.AST* = body.get(0)
|
||||||
if stmt is not None and stmt.kind() == ast.ASTKind.Pass:
|
if stmt is not None and stmt.kind() == ast.ASTKind.Pass: return 1
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
# 获取 LLVM 整数类型的位宽
|
# Get the LLVM integer type bit width. Delegates to TSpec.SpecTypeBits (the sole implementation
|
||||||
# 实现已收拢到 TSpec.SpecTypeBits(该块在 TypeSpec 模块中的唯一实现),此处只做委托。
|
# in TypeSpec); private call sites (self.get_llvm_type_bits) are unaffected.
|
||||||
# 私有调用点(self.get_llvm_type_bits)保持原样不受影响。
|
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||||
def get_llvm_type_bits(self, ty: llvmlite.LLVMType*) -> int:
|
"""Get an LLVM integer type's bit width, 0 for non-integers (delegates to SpecTypeBits)"""
|
||||||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
|
|
||||||
return TSpec.SpecTypeBits(ty)
|
return TSpec.SpecTypeBits(ty)
|
||||||
def infer_expr_type(self, pool: memhub.MemBuddy*,
|
def infer_expr_type(self, pool: memhub.MemBuddy*, node: ast.AST*, param_types: str) -> mlirlite.LLVMType*:
|
||||||
node: ast.AST*,
|
"""Infer an expression's LLVM type; param_types format is "name:bits,name:bits,..."."""
|
||||||
param_types: str) -> llvmlite.LLVMType*:
|
if node is None: return mlirlite.Int32(pool)
|
||||||
"""推断表达式的 LLVM 类型
|
|
||||||
|
|
||||||
param_types 格式: "name:bits,name:bits,..."
|
|
||||||
"""
|
|
||||||
if node is None:
|
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
|
|
||||||
k: int = node.kind()
|
k: int = node.kind()
|
||||||
|
|
||||||
if k == ast.ASTKind.Constant:
|
if k == ast.ASTKind.Constant: return mlirlite.Int32(pool)
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
|
|
||||||
elif k == ast.ASTKind.Name:
|
elif k == ast.ASTKind.Name:
|
||||||
nm: ast.Name* = ast.Name*(node)
|
nm: ast.Name* = ast.Name*(node)
|
||||||
if nm.id is not None and param_types is not None:
|
if nm.id is not None and param_types is not None:
|
||||||
# 在 param_types 中查找: "name:bits" 格式
|
# Look up "name:bits" in param_types. Allocate the key at exact length: a fixed 64
|
||||||
# 键按精确长度分配:固定 64 缓冲 + snprintf("%s:") 会静默截断超长参数名,
|
# buffer truncated long names and could match another param's prefix, giving wrong bits.
|
||||||
# 截断后的键可能与**别的参数前缀**匹配成功 ⇒ 取到错误的位宽(类型误判)。
|
|
||||||
id_len: sizet = string.strlen(nm.id)
|
id_len: sizet = string.strlen(nm.id)
|
||||||
search_key: str = pool.alloc(id_len + 2)
|
search_key: str = pool.alloc(id_len + 2)
|
||||||
if search_key is not None:
|
if search_key is not None:
|
||||||
@@ -335,158 +217,109 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
|||||||
bits: int = 0
|
bits: int = 0
|
||||||
for bi in range(4):
|
for bi in range(4):
|
||||||
ch: char = bits_str[bi]
|
ch: char = bits_str[bi]
|
||||||
if ch >= '0' and ch <= '9':
|
if ch >= '0' and ch <= '9': bits = bits * 10 + (ch - '0')
|
||||||
bits = bits * 10 + (ch - '0')
|
else: break
|
||||||
else:
|
if bits == 64: return mlirlite.Int64(pool)
|
||||||
break
|
elif bits == 16: return mlirlite.Int16(pool)
|
||||||
if bits == 64:
|
elif bits == 8: return mlirlite.Int8(pool)
|
||||||
return llvmlite.Int64(pool)
|
return mlirlite.Int32(pool)
|
||||||
elif bits == 16:
|
|
||||||
return llvmlite.Int16(pool)
|
|
||||||
elif bits == 8:
|
|
||||||
return llvmlite.Int8(pool)
|
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
|
|
||||||
elif k == ast.ASTKind.BinOp:
|
elif k == ast.ASTKind.BinOp:
|
||||||
binop: ast.BinOp* = ast.BinOp*(node)
|
binop: ast.BinOp* = ast.BinOp*(node)
|
||||||
lhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
|
lhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
|
||||||
rhs_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
|
rhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
|
||||||
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
|
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
|
||||||
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
|
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
|
||||||
if lhs_bits >= rhs_bits:
|
if lhs_bits >= rhs_bits: return lhs_ty
|
||||||
return lhs_ty
|
|
||||||
return rhs_ty
|
return rhs_ty
|
||||||
|
|
||||||
return llvmlite.Int32(pool)
|
return mlirlite.Int32(pool)
|
||||||
def infer_return_type(self, pool: memhub.MemBuddy*,
|
def infer_return_type(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*, param_types: str) -> mlirlite.LLVMType*:
|
||||||
body: list[ast.AST*]*,
|
"""Scan a function body's return statements to infer the return type"""
|
||||||
param_types: str) -> llvmlite.LLVMType*:
|
if body is None: return mlirlite.Int32(pool)
|
||||||
"""扫描函数体中的 return 语句,推断返回类型"""
|
|
||||||
if body is None:
|
|
||||||
return llvmlite.Int32(pool)
|
|
||||||
|
|
||||||
ret_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
ret_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
bn: sizet = len(body)
|
bn: sizet = len(body)
|
||||||
for bi in range(bn):
|
for bi in range(int(bn)):
|
||||||
stmt: ast.AST* = body.get(bi)
|
stmt: ast.AST* = body.get(bi)
|
||||||
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
|
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
|
||||||
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
|
rt: ast.Return* = ast.Return*(stmt)
|
||||||
if rt.value is not None:
|
if rt.value is not None:
|
||||||
expr_ty: llvmlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
|
expr_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
|
||||||
if expr_ty is not None:
|
if expr_ty is not None:
|
||||||
cur_bits: int = self.get_llvm_type_bits(ret_ty)
|
cur_bits: int = self.get_llvm_type_bits(ret_ty)
|
||||||
new_bits: int = self.get_llvm_type_bits(expr_ty)
|
new_bits: int = self.get_llvm_type_bits(expr_ty)
|
||||||
if new_bits > cur_bits:
|
if new_bits > cur_bits: ret_ty = expr_ty
|
||||||
ret_ty = expr_ty
|
|
||||||
return ret_ty
|
return ret_ty
|
||||||
def body_has_value_return(self, pool: memhub.MemBuddy*,
|
def body_has_value_return(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*) -> int:
|
||||||
body: list[ast.AST*]*) -> int:
|
"""Whether the body has a return of a non-None value, recursively checking control-flow statements but skipping nested defs."""
|
||||||
"""扫描函数体,判断是否存在返回非 None 值的 return
|
if body is None: return 0
|
||||||
|
|
||||||
递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过
|
|
||||||
嵌套函数/类定义(FunctionDef/ClassDef 由自身翻译时独立检查,避免外层
|
|
||||||
检查误伤嵌套作用域)。裸 return / return None(CONST_NONE)/ 无 return
|
|
||||||
均不算返回非 None 值。
|
|
||||||
供无返回类型注解函数的 fail-fast 检查使用:返回具体值必须有显式 ->
|
|
||||||
注解,编译器不替用户推断返回类型(推断位宽会掩盖返回契约)。
|
|
||||||
"""
|
|
||||||
if body is None:
|
|
||||||
return 0
|
|
||||||
bn: sizet = len(body)
|
bn: sizet = len(body)
|
||||||
for bi in range(bn):
|
for bi in range(int(bn)):
|
||||||
stmt: ast.AST* = body.get(bi)
|
stmt: ast.AST* = body.get(bi)
|
||||||
if stmt is None:
|
if stmt is None: continue
|
||||||
continue
|
|
||||||
k: int = stmt.kind()
|
k: int = stmt.kind()
|
||||||
if k == ast.ASTKind.Return:
|
if k == ast.ASTKind.Return:
|
||||||
rt: rawptr[ast.Return] = rawptr[ast.Return](stmt)
|
rt: ast.Return* = ast.Return*(stmt)
|
||||||
if rt.value is not None:
|
if rt.value is not None:
|
||||||
# return None(CONST_NONE)不算返回非 None 值
|
# return None (CONST_NONE) does not count as returning a non-None value
|
||||||
if rt.value.kind() == ast.ASTKind.Constant:
|
if rt.value.kind() == ast.ASTKind.Constant:
|
||||||
cn: ast.Constant* = ast.Constant*(rt.value)
|
cn: ast.Constant* = ast.Constant*(rt.value)
|
||||||
if cn.const_kind == ast.CONST_NONE:
|
if cn.const_kind == ast.CONST_NONE: continue
|
||||||
continue
|
|
||||||
return 1
|
return 1
|
||||||
elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef:
|
elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef: continue
|
||||||
continue
|
elif k == ast.ASTKind.If or k == ast.ASTKind.While or k == ast.ASTKind.For or k == ast.ASTKind.Try or k == ast.ASTKind.With or k == ast.ASTKind.Match:
|
||||||
elif k == ast.ASTKind.If or k == ast.ASTKind.While or \
|
|
||||||
k == ast.ASTKind.For or k == ast.ASTKind.Try or \
|
|
||||||
k == ast.ASTKind.With or k == ast.ASTKind.Match:
|
|
||||||
sub: list[ast.AST*]* = stmt.children
|
sub: list[ast.AST*]* = stmt.children
|
||||||
if sub is not None and len(sub) > 0:
|
if sub is not None and len(sub) > 0:
|
||||||
if self.body_has_value_return(pool, sub) != 0:
|
if self.body_has_value_return(pool, sub) != 0: return 1
|
||||||
return 1
|
|
||||||
return 0
|
return 0
|
||||||
def build_param_types_str(self, pool: memhub.MemBuddy*,
|
def build_param_types_str(self, pool: memhub.MemBuddy*, args_node: ast.AST*) -> str:
|
||||||
args_node: ast.AST*) -> str:
|
"""Build the param type encoding string "name:bits,name:bits,..." with upper-bound sizing and single-pass in-place concatenation."""
|
||||||
"""构建参数类型编码字符串 "name:bits,name:bits,..."
|
if args_node is None: return None
|
||||||
|
|
||||||
[D5·截断纪律] 旧实现按 `an * 32 + 1` 预估缓冲,再用 `_strappend` 以
|
|
||||||
`remain - 1` 截断且**不报错**:参数名偏长(或参数偏多)时编码串被静默
|
|
||||||
截断 ⇒ `infer_expr_type` 的 `strstr(param_types, "name:")` 查找失配 /
|
|
||||||
命中他人前缀 ⇒ 静默回落 Int32(类型误判)。现改为**上界预估 + 单遍就地
|
|
||||||
拼接**:段一是只扫参数名的上界(name + ':' + 位宽 + ',' + NUL),段二是
|
|
||||||
解析类型后逐条目就地格式化并立即拼接 —— 缓冲恒够,不存在截断;
|
|
||||||
`_strappend` 一并删除(唯一调用点在本函数)。
|
|
||||||
"""
|
|
||||||
if args_node is None:
|
|
||||||
return None
|
|
||||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||||
if ags.args is None:
|
if ags.args is None: return None
|
||||||
return None
|
|
||||||
alist: list[ast.AST*]* = ags.args
|
alist: list[ast.AST*]* = ags.args
|
||||||
an: sizet = len(alist)
|
an: sizet = len(alist)
|
||||||
if an == 0:
|
if an == 0: return None
|
||||||
return None
|
|
||||||
|
|
||||||
# 1) 上界估长(**只扫参数名**,不解析类型,也不暂存结果指针):
|
# 1) Upper-bound sizing (scan only param names, no type resolution): each entry is
|
||||||
# 每条 = name + ':' + 位宽(int32 十进制最长 11 字符含符号)+ ',';再加 NUL。
|
# name + ':' + bits + ',', plus NUL. Use an upper bound, not an exact size.
|
||||||
# 必须"上界"而非"精确":把条目指针存进 sizet 数组再取出转回指针 strcat 会出错;
|
bound: sizet = 1 # trailing NUL
|
||||||
# 上界缓冲不需要数组往返,每条目就地格式化、立即拼接。
|
|
||||||
bound: sizet = 1 # 末尾 NUL
|
|
||||||
pre_i: sizet
|
pre_i: sizet
|
||||||
for pre_i in range(an):
|
for pre_i in range(int(an)):
|
||||||
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
|
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
|
||||||
if pre_arg is None or pre_arg.arg is None:
|
if pre_arg is None or pre_arg.arg is None: continue
|
||||||
continue
|
|
||||||
bound += string.strlen(pre_arg.arg) + 1 + 12 + 1
|
bound += string.strlen(pre_arg.arg) + 1 + 12 + 1
|
||||||
buf: str = pool.alloc(bound)
|
buf: str = pool.alloc(bound)
|
||||||
if buf is None:
|
if buf is None: return None
|
||||||
return None
|
|
||||||
buf[0] = '\0'
|
buf[0] = '\0'
|
||||||
|
|
||||||
# 2) 单遍:解析类型 → 就地格式化条目 → 立即拼接(缓冲为上界,不会截断)
|
# 2) Single pass: resolve type -> format entry in place -> append immediately (no truncation)
|
||||||
cnt: sizet = 0
|
cnt: sizet = 0
|
||||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||||
for ai in range(an):
|
for ai in range(int(an)):
|
||||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||||
if arg is None or arg.arg is None:
|
if arg is None or arg.arg is None: continue
|
||||||
continue
|
param_ty: mlirlite.LLVMType* = i32_ty
|
||||||
param_ty: llvmlite.LLVMType* = i32_ty
|
|
||||||
if arg.annotation is not None:
|
if arg.annotation is not None:
|
||||||
# 统一口径:CTypedef 动态表优先,miss 回退 map_t_type
|
# Unified: prefer the CTypedef dynamic table, fall back to map_builtin_type
|
||||||
# (阶段 2:删除 map_t_type 硬编码别名后,BYTE 等靠库 typedef)
|
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
|
||||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||||
at: ast.Attribute* = ast.Attribute*(arg.annotation)
|
at: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||||
if at.attr is not None:
|
if at.attr is not None:
|
||||||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
|
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
|
||||||
if resolved is not None:
|
if resolved is not None: param_ty = resolved
|
||||||
param_ty = resolved
|
|
||||||
bits: int = self.get_llvm_type_bits(param_ty)
|
bits: int = self.get_llvm_type_bits(param_ty)
|
||||||
if bits == 0:
|
if bits == 0: bits = 32
|
||||||
bits = 32
|
|
||||||
nlen: sizet = string.strlen(arg.arg)
|
nlen: sizet = string.strlen(arg.arg)
|
||||||
# 条目缓冲:name + ':' + 位宽(预留 16 字节,足够 %d 任意取值)+ NUL
|
# Entry buffer: name + ':' + bits (16 bytes reserved, enough for any %d) + NUL
|
||||||
ent: str = pool.alloc(nlen + 1 + 16 + 1)
|
ent: str = pool.alloc(nlen + 1 + 16 + 1)
|
||||||
if ent is None:
|
if ent is None: continue
|
||||||
continue
|
|
||||||
string.strcpy(ent, arg.arg)
|
string.strcpy(ent, arg.arg)
|
||||||
ent[nlen] = ':'
|
ent[nlen] = ':'
|
||||||
viperlib.snprintf(ent + nlen + 1, 16, "%d", bits)
|
viperlib.snprintf(ent + nlen + 1, 16, "%d", bits)
|
||||||
if cnt > 0:
|
if cnt > 0: string.strcat(buf, ",")
|
||||||
string.strcat(buf, ",")
|
|
||||||
string.strcat(buf, ent)
|
string.strcat(buf, ent)
|
||||||
cnt += 1
|
cnt += 1
|
||||||
if cnt == 0:
|
if cnt == 0: return None
|
||||||
return None
|
|
||||||
return buf
|
return buf
|
||||||
@@ -1,37 +1,16 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
from stdint import *
|
||||||
import ast
|
|
||||||
import llvmlite
|
|
||||||
import memhub
|
|
||||||
import string
|
|
||||||
import stdio
|
|
||||||
import sys
|
|
||||||
import stdlib
|
|
||||||
import viperlib
|
|
||||||
import w32.fileio as fileio
|
|
||||||
import lib.core.CompilerFactory as CompilerFactory
|
|
||||||
import lib.core.VLogger as VLogger
|
|
||||||
import lib.core.Gen.Translator as HT
|
|
||||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# TypeGenMixin - type-mapping/parse mixin entry (@t.NoVTable: static binding, no vtable).
|
||||||
# TypeGenMixin - 类型映射与解析 Mixin(Generator 继承链入口)
|
# Methods live in TypeGenBase/CDef/CType/Resolve/Err; this file keeps only the class decl.
|
||||||
#
|
|
||||||
# 方法已全部拆至 TypeGenBase/CDef/CType/Resolve/Err,本文件保留类声明。
|
|
||||||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# Constants (kept in sync with HandlesType).
|
||||||
# 常量(与 HandlesType 保持一致)
|
|
||||||
# ============================================================
|
|
||||||
define TRACE_MAX = 32
|
define TRACE_MAX = 32
|
||||||
define MAX_CTYPEDEF_DEPTH = 10
|
define MAX_CTYPEDEF_DEPTH = 10
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# TypeGenMixin - type mapping, annotation parsing, compile-time constants, error reporting.
|
||||||
# TypeGenMixin - 类型映射/注解解析/编译期常量表/错误上报
|
|
||||||
# ============================================================
|
|
||||||
novirtual class TypeGenMixin(TypeGenBase.TypeGenBase):
|
novirtual class TypeGenMixin(TypeGenBase.TypeGenBase):
|
||||||
"""类型映射与解析 Mixin(阶段 D 拆分后顶层,方法全部在子 Mixin)"""
|
"""Type mapping and parsing mixin; all methods live in the sub-mixins."""
|
||||||
+2449
-3815
File diff suppressed because it is too large.
Load diff
+464
-867
File diff suppressed because it is too large.
Load diff
+1579
-1626
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,2 @@
|
|||||||
# ============================================================
|
# Gen - objectified generator (P1 skeleton + P2 TypeGenMixin)
|
||||||
# Gen - 对象化生成器(P1 骨架 + P2 TypeGenMixin)
|
# Generator: translation state container
|
||||||
# Generator: 翻译状态容器
|
|
||||||
# ============================================================
|
|
||||||
+404
-507
@@ -1,507 +1,404 @@
|
|||||||
import t, c
|
from stdint import *
|
||||||
from stdint import *
|
import memhub
|
||||||
import memhub
|
import string
|
||||||
import string
|
import stdlib
|
||||||
import stdio
|
import win32.win32file as win32file
|
||||||
import stdlib
|
import win32.win32base as win32base
|
||||||
import w32.win32file as win32file
|
import win32.fileio as fileio
|
||||||
import w32.win32base as win32base
|
import hashlib
|
||||||
import hashlib
|
import viperlib
|
||||||
import viperlib
|
import lib.Projectrans.Utils as Utils
|
||||||
import lib.Projectrans.Utils as Utils
|
import lib.core.VLogger as VLogger
|
||||||
import lib.core.VLogger as VLogger
|
|
||||||
|
|
||||||
|
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
|
||||||
# ============================================================
|
# FindNextFileA); collects .vp paths and SHA1s for Phase1 stub gen / Phase2 merge.
|
||||||
# IncludesScanner - includes 目录递归扫描(Win32 FindFirstFileA/FindNextFileA)
|
|
||||||
# 递归收集 .vp 文件路径与 SHA1,供 Phase1 stub 生成 / Phase2 stub 合并使用
|
# global mbuddy pointer
|
||||||
# ============================================================
|
_mbuddy: memhub.MemBuddy*
|
||||||
|
|
||||||
# 全局 mbuddy 指针
|
# max file path length
|
||||||
_mbuddy: memhub.MemBuddy*
|
define MAX_PATH_LEN = 512
|
||||||
|
# max files per scan
|
||||||
# 文件路径最大长度
|
define MAX_FILES = 1024
|
||||||
define MAX_PATH_LEN = 512
|
|
||||||
# 单次扫描最大文件数
|
|
||||||
define MAX_FILES = 1024
|
# FileEntry - file entry (path + SHA1)
|
||||||
|
novirtual class FileEntry:
|
||||||
|
"""A scanned file entry."""
|
||||||
# ============================================================
|
Path: str # absolute file path
|
||||||
# FileEntry - 文件条目(路径 + SHA1)
|
Sha1: str # first 16 SHA1 chars (16 bytes + null)
|
||||||
# ============================================================
|
RelPath: str # path relative to the includes root
|
||||||
novirtual class FileEntry:
|
ModuleName: str # module name (e.g. "ast.parser")
|
||||||
"""扫描到的文件条目"""
|
IncDir: str # owning includes root dir (multi-root support)
|
||||||
Path: str # 文件绝对路径
|
|
||||||
Sha1: str # SHA1 前16字符(16字节+null)
|
|
||||||
RelPath: str # 相对于 includes 根目录的路径
|
# ScanResult - scan result
|
||||||
ModuleName: str # 模块名(如 "ast.parser")
|
novirtual class ScanResult:
|
||||||
IncDir: str # 所属 includes 根目录(多 includes 目录支持)
|
"""A collection of scan results."""
|
||||||
|
Entries: FileEntry* # FileEntry array
|
||||||
|
Count: int
|
||||||
# ============================================================
|
Capacity: int
|
||||||
# ScanResult - 扫描结果
|
|
||||||
# ============================================================
|
|
||||||
novirtual class ScanResult:
|
# create_scan_result - create the scan result container
|
||||||
"""扫描结果集合"""
|
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
||||||
Entries: FileEntry* # FileEntry 数组
|
"""Create the scan result container with MAX_FILES preallocated slots."""
|
||||||
Count: int
|
if pool is None: return None
|
||||||
Capacity: int
|
size: sizet = MAX_FILES * FileEntry.__sizeof__()
|
||||||
|
# use stdlib.malloc to avoid exhausting the mbuddy pool (consistent with StubMerger)
|
||||||
|
entries: FileEntry* = stdlib.malloc(size)
|
||||||
# ============================================================
|
if entries is None: return None
|
||||||
# create_scan_result - 创建扫描结果容器
|
string.memset(entries, 0, size)
|
||||||
# ============================================================
|
|
||||||
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
|
||||||
"""创建扫描结果容器,预分配 MAX_FILES 个槽位"""
|
if result is None: return None
|
||||||
if pool is None: return None
|
string.memset(result, 0, ScanResult.__sizeof__())
|
||||||
size: sizet = MAX_FILES * FileEntry.__sizeof__()
|
result.Entries = entries
|
||||||
# 使用 stdlib.malloc 避免 mbuddy 池耗尽(与 StubMerger 保持一致)
|
result.Count = 0
|
||||||
entries: FileEntry* = stdlib.malloc(size)
|
result.Capacity = MAX_FILES
|
||||||
if entries is None: return None
|
return result
|
||||||
string.memset(entries, 0, size)
|
|
||||||
|
|
||||||
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
|
# add_file_entry - append a file entry to the scan result
|
||||||
if result is None: return None
|
def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, rel_path: str, sha1: str, inc_dir: str) -> int:
|
||||||
string.memset(result, 0, ScanResult.__sizeof__())
|
"""Append a file entry to the scan result; inc_dir is the owning includes root."""
|
||||||
result.Entries = entries
|
if result is None or pool is None: return 1
|
||||||
result.Count = 0
|
if result.Count >= result.Capacity: return 1
|
||||||
result.Capacity = MAX_FILES
|
|
||||||
return result
|
# compute module name: '/' -> '.', strip .vp/.py (Utils handles __init__.vp)
|
||||||
|
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
|
||||||
|
if mod_buf is None: return 1
|
||||||
# ============================================================
|
|
||||||
# add_file_entry - 向扫描结果添加文件条目
|
# get the entry address
|
||||||
# ============================================================
|
entry_size: sizet = FileEntry.__sizeof__()
|
||||||
def add_file_entry(result: ScanResult*,
|
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
|
||||||
pool: memhub.MemBuddy*,
|
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||||
abs_path: str, rel_path: str,
|
if entry is None: return 1
|
||||||
sha1: str, inc_dir: str) -> int:
|
|
||||||
"""添加文件条目到扫描结果,返回 0 成功(inc_dir 为所属 includes 根目录)"""
|
# copy the path string
|
||||||
if result is None or pool is None: return 1
|
abs_len: sizet = string.strlen(abs_path)
|
||||||
if result.Count >= result.Capacity: return 1
|
abs_buf: str = stdlib.malloc(abs_len + 1)
|
||||||
|
if abs_buf is None: return 1
|
||||||
# 计算模块名:rel_path 中 / 替换为 .,去掉 .vp/.py 扩展名(__init__.vp 特判由 Utils 统一处理)
|
string.strcpy(abs_buf, abs_path)
|
||||||
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
|
entry.Path = abs_buf
|
||||||
if mod_buf is None: return 1
|
|
||||||
|
# copy the SHA1
|
||||||
# 获取条目地址
|
sha1_buf: str = stdlib.malloc(17)
|
||||||
entry_size: sizet = FileEntry.__sizeof__()
|
if sha1_buf is None: return 1
|
||||||
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
|
string.strcpy(sha1_buf, sha1)
|
||||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
entry.Sha1 = sha1_buf
|
||||||
if entry is None: return 1
|
|
||||||
|
# copy the relative path
|
||||||
# 复制路径字符串
|
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
|
||||||
abs_len: sizet = string.strlen(abs_path)
|
if rel_buf is None: return 1
|
||||||
abs_buf: str = stdlib.malloc(abs_len + 1)
|
string.strcpy(rel_buf, rel_path)
|
||||||
if abs_buf is None: return 1
|
entry.RelPath = rel_buf
|
||||||
string.strcpy(abs_buf, abs_path)
|
|
||||||
entry.Path = abs_buf
|
# copy the module name
|
||||||
|
entry.ModuleName = mod_buf
|
||||||
# 复制 SHA1
|
|
||||||
sha1_buf: str = stdlib.malloc(17)
|
# copy the owning includes root dir (used by Phase2 path joining)
|
||||||
if sha1_buf is None: return 1
|
if inc_dir is not None:
|
||||||
string.strcpy(sha1_buf, sha1)
|
inc_len: sizet = string.strlen(inc_dir)
|
||||||
entry.Sha1 = sha1_buf
|
inc_buf: str = stdlib.malloc(inc_len + 1)
|
||||||
|
if inc_buf is None: return 1
|
||||||
# 复制相对路径
|
string.strcpy(inc_buf, inc_dir)
|
||||||
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
|
entry.IncDir = inc_buf
|
||||||
if rel_buf is None: return 1
|
|
||||||
string.strcpy(rel_buf, rel_path)
|
result.Count += 1
|
||||||
entry.RelPath = rel_buf
|
return 0
|
||||||
|
|
||||||
# 复制模块名
|
|
||||||
entry.ModuleName = mod_buf
|
# compute_file_sha1 - read file content and compute SHA1 (CRLF->LF, matching Projectrans.py)
|
||||||
|
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||||
# 复制所属 includes 根目录(多 includes 目录支持,Phase2 拼路径用)
|
"""Read a file, normalize CRLF to LF, and return the first 16 SHA1 hex chars."""
|
||||||
if inc_dir is not None:
|
if pool is None or file_path is None: return None
|
||||||
inc_len: sizet = string.strlen(inc_dir)
|
|
||||||
inc_buf: str = stdlib.malloc(inc_len + 1)
|
# read file content; BUF_SIZE must match SRC_BUF_SIZE (1MB) or trailing edits won't change SHA1
|
||||||
if inc_buf is None:
|
BUF_SIZE: sizet = 1048576
|
||||||
return 1
|
# Refuse larger files: hashing only the first 1 MiB gives two files that differ later the same
|
||||||
string.strcpy(inc_buf, inc_dir)
|
# SHA1, and the SHA1 keys the stub/cache files (colliding modules would reuse each other's IR).
|
||||||
entry.IncDir = inc_buf
|
sf_sz: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||||
|
if sf_sz.closed: return None
|
||||||
result.Count += 1
|
too_big: int = 0
|
||||||
return 0
|
if sf_sz.size() >= BUF_SIZE: too_big = 1
|
||||||
|
sf_sz.close()
|
||||||
|
if too_big != 0:
|
||||||
# ============================================================
|
VLogger.error(f"source file exceeds the {BUF_SIZE}-byte limit and cannot be hashed completely: {file_path}", "IncludesScanner")
|
||||||
# compute_file_sha1 - 读取文件内容并计算 SHA1(CRLF→LF 后计算,与 Projectrans.py 一致)
|
return None
|
||||||
# ============================================================
|
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
|
||||||
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
if buf is None: return None
|
||||||
"""读取文件内容并计算 SHA1 前16字符(CRLF→LF 后计算,与 Projectrans.py 一致)"""
|
|
||||||
if pool is None or file_path is None: return None
|
# strip \r in place (CRLF -> LF), matching Projectrans.py text-mode reads
|
||||||
|
buf_len: sizet = string.strlen(buf)
|
||||||
# 读取文件内容(stdlib.malloc 分配,避免 mbuddy 池耗尽);BUF_SIZE 须与翻译侧 SRC_BUF_SIZE(1MB)一致,否则尾部变更不改 SHA1 导致缓存永不失效
|
write_pos: sizet = 0
|
||||||
BUF_SIZE: sizet = 1048576
|
for ch in buf:
|
||||||
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
|
if ch != '\r':
|
||||||
if buf is None: return None
|
buf[write_pos] = ch
|
||||||
|
write_pos += 1
|
||||||
# 原地去除 \r(CRLF → LF),与 Projectrans.py 文本模式读取一致
|
|
||||||
buf_len: sizet = string.strlen(buf)
|
# add a null terminator
|
||||||
write_pos: sizet = 0
|
buf[write_pos] = 0
|
||||||
for ch in buf:
|
|
||||||
if ch != '\r':
|
# compute SHA1
|
||||||
buf[write_pos] = ch
|
ctx: hashlib.sha1* = hashlib.sha1()
|
||||||
write_pos += 1
|
if ctx is None:
|
||||||
|
stdlib.free(buf)
|
||||||
# 添加 null 终止符
|
return None
|
||||||
buf[write_pos] = 0
|
ctx.update(buf)
|
||||||
|
|
||||||
# 计算 SHA1
|
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
|
||||||
ctx: rawptr[hashlib.sha1] = hashlib.sha1()
|
if digest is None:
|
||||||
if ctx is None:
|
stdlib.free(buf)
|
||||||
stdlib.free(buf)
|
return None
|
||||||
return None
|
ctx.final(digest)
|
||||||
ctx.update(buf)
|
|
||||||
|
# convert to hex (first 8 bytes = 16 hex chars; stdlib allocation unchanged)
|
||||||
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
|
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
|
||||||
if digest is None:
|
stdlib.free(buf)
|
||||||
stdlib.free(buf)
|
stdlib.free(digest)
|
||||||
return None
|
return hex_buf
|
||||||
ctx.final(digest)
|
|
||||||
|
|
||||||
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符,stdlib 分配保持原行为)
|
# scan_directory_recursive - recursively scan a directory (FindFirstFileA/FindNextFileA)
|
||||||
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
|
def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix: str, result: ScanResult*, inc_dir: str) -> int:
|
||||||
stdlib.free(buf)
|
"""Recursively collect .vp/.py files under a directory; inc_dir is the includes root."""
|
||||||
stdlib.free(digest)
|
if pool is None or root_dir is None or result is None: return 1
|
||||||
return hex_buf
|
# subdir recursion failure count (not ignored; fail-fast at the summary)
|
||||||
|
subdir_failed: int = 0
|
||||||
|
|
||||||
# ============================================================
|
# build search pattern: root_dir/*
|
||||||
# scan_directory_recursive - 递归扫描目录(FindFirstFileA/FindNextFileA 遍历目录树)
|
root_len: sizet = string.strlen(root_dir)
|
||||||
# ============================================================
|
pattern: bytes = stdlib.malloc(root_len + 4)
|
||||||
def scan_directory_recursive(pool: memhub.MemBuddy*,
|
if pattern is None: return 1
|
||||||
root_dir: str,
|
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
|
||||||
rel_prefix: str,
|
|
||||||
result: ScanResult*,
|
# find_data buffer +16 slack: VPC's WIN32_FIND_DATAA.__sizeof__() undercounts Windows writes
|
||||||
inc_dir: str) -> int:
|
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||||
"""递归扫描目录,收集 .vp/.py 文件(inc_dir 为所属 includes 根目录)"""
|
find_data: win32file.WIN32_FIND_DATAA* = stdlib.malloc(fd_size_s + 16)
|
||||||
if pool is None or root_dir is None or result is None:
|
if find_data is None: return 1
|
||||||
return 1
|
string.memset(find_data, 0, fd_size_s + 16)
|
||||||
# 子目录递归失败计数(不静默忽略,汇总处 fail-fast 终止)
|
|
||||||
subdir_failed: int = 0
|
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||||
|
if handle == win32base.INVALID_HANDLE_VALUE: return 1
|
||||||
# 构造搜索模式: root_dir/*
|
|
||||||
root_len: sizet = string.strlen(root_dir)
|
# iterate all files and subdirectories
|
||||||
pattern: bytes = stdlib.malloc(root_len + 4)
|
while True:
|
||||||
if pattern is None:
|
# skip . and ..
|
||||||
return 1
|
fname: str = find_data.cFileName
|
||||||
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
|
if fname is not None:
|
||||||
|
fname0: char = fname[0]
|
||||||
# find_data 缓冲 +16 余量:TPV 的 WIN32_FIND_DATAA.__sizeof__() 小于 Windows 实际写入,越界写会破坏相邻堆块
|
if fname0 == '.':
|
||||||
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
|
fname1: char = fname[1]
|
||||||
find_data: rawptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(fd_size_s + 16)
|
if fname1 == '\0':
|
||||||
if find_data is None:
|
# "."
|
||||||
return 1
|
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||||
string.memset(find_data, 0, fd_size_s + 16)
|
continue
|
||||||
|
elif fname1 == '.':
|
||||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
fname2: char = fname[2]
|
||||||
if handle == win32base.INVALID_HANDLE_VALUE:
|
if fname2 == '\0':
|
||||||
return 1
|
# ".."
|
||||||
|
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||||
# 遍历所有文件和子目录
|
continue
|
||||||
while True:
|
|
||||||
# 跳过 . 和 ..
|
# check whether it is a directory
|
||||||
fname: str = find_data.cFileName
|
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||||
if fname is not None:
|
|
||||||
fname0: char = fname[0]
|
# build the full path
|
||||||
if fname0 == '.':
|
fname_len: sizet = string.strlen(fname)
|
||||||
fname1: char = fname[1]
|
full_path: bytes = Utils.JoinPath(root_dir, fname)
|
||||||
if fname1 == '\0':
|
if full_path is None: break
|
||||||
# "."
|
|
||||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
# build the relative path
|
||||||
break
|
prefix_len: sizet = 0
|
||||||
continue
|
if rel_prefix is not None: prefix_len = string.strlen(rel_prefix)
|
||||||
elif fname1 == '.':
|
rel_path: bytes = None
|
||||||
fname2: char = fname[2]
|
if rel_prefix is not None and prefix_len > 0: rel_path = Utils.JoinPath(rel_prefix, fname)
|
||||||
if fname2 == '\0':
|
else:
|
||||||
# ".."
|
rel_path = stdlib.malloc(fname_len + 1)
|
||||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
if rel_path is None: break
|
||||||
break
|
string.strcpy(rel_path, fname)
|
||||||
continue
|
if rel_path is None: break
|
||||||
|
|
||||||
# 检查是否为目录
|
if is_dir != 0:
|
||||||
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
|
# recurse into the subdirectory
|
||||||
|
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1
|
||||||
# 构造完整路径
|
else:
|
||||||
fname_len: sizet = string.strlen(fname)
|
# check for a .vp source file (Viper uses .vp only; no longer .py)
|
||||||
full_path: bytes = Utils.JoinPath(root_dir, fname)
|
is_py: int = 0
|
||||||
if full_path is None:
|
if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1
|
||||||
break
|
|
||||||
|
if is_py != 0:
|
||||||
# 构造相对路径
|
# skip files under __pycache__
|
||||||
prefix_len: sizet = 0
|
is_pycache: int = 0
|
||||||
if rel_prefix is not None:
|
if rel_prefix is not None:
|
||||||
prefix_len = string.strlen(rel_prefix)
|
if string.strcmp(rel_prefix, "__pycache__") == 0: is_pycache = 1
|
||||||
rel_path: bytes = None
|
|
||||||
if rel_prefix is not None and prefix_len > 0:
|
if is_pycache == 0:
|
||||||
rel_path = Utils.JoinPath(rel_prefix, fname)
|
# compute SHA1
|
||||||
else:
|
sha1: str = compute_file_sha1(pool, full_path)
|
||||||
rel_path = stdlib.malloc(fname_len + 1)
|
if sha1 is not None:
|
||||||
if rel_path is None:
|
# fail-fast: a full table used to drop the file silently, so it never got a
|
||||||
break
|
# SHA1 entry / dependency edge and was therefore never compiled.
|
||||||
string.strcpy(rel_path, fname)
|
if add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir) != 0:
|
||||||
if rel_path is None:
|
VLogger.error(f"includes scan could not register '{full_path}' (the {MAX_FILES}-file capacity or an allocation failed); raise MAX_FILES", "scan")
|
||||||
break
|
VLogger.debug(f"scan: {rel_path} -> {sha1}", "scan")
|
||||||
|
else: VLogger.warning(f"SHA1 computation failed: {full_path}", "scan")
|
||||||
if is_dir != 0:
|
|
||||||
# 递归扫描子目录
|
# continue to the next file
|
||||||
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0:
|
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||||
subdir_failed += 1
|
|
||||||
else:
|
win32file.FindClose(handle)
|
||||||
# 检查是否为 .vp 源文件(Viper 源仅 .vp,不再识别 .py)
|
if subdir_failed > 0: VLogger.error(f"subdirectory scan failed for {subdir_failed} entries", "scan")
|
||||||
is_py: int = 0
|
|
||||||
if Utils.is_vp_at(fname, 0, fname_len) != 0:
|
return 0
|
||||||
is_py = 1
|
|
||||||
|
|
||||||
if is_py != 0:
|
# scan_includes - scan entry point: return all .vp file paths and SHA1s
|
||||||
# 跳过 __pycache__ 目录下的文件
|
def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||||
is_pycache: int = 0
|
"""Scan space-separated includes dirs and return all scanned .vp/.py files."""
|
||||||
if rel_prefix is not None:
|
if pool is None or includes_dirs is None: return None
|
||||||
if string.strcmp(rel_prefix, "__pycache__") == 0:
|
|
||||||
is_pycache = 1
|
result: ScanResult* = create_scan_result(pool)
|
||||||
|
if result is None: return None
|
||||||
if is_pycache == 0:
|
|
||||||
# 计算 SHA1
|
# parse space-separated includes dirs, recursively scanning and merging each
|
||||||
sha1: str = compute_file_sha1(pool, full_path)
|
dirs_total: sizet = string.strlen(includes_dirs)
|
||||||
if sha1 is not None:
|
d_i: sizet = 0
|
||||||
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
|
while d_i < dirs_total:
|
||||||
VLogger.debug(f"扫描: {rel_path} -> {sha1}", "scan")
|
while d_i < dirs_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||||
else:
|
if d_i >= dirs_total: break
|
||||||
VLogger.warning(f"SHA1 计算失败: {full_path}", "scan")
|
d_start: sizet = d_i
|
||||||
|
while d_i < dirs_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||||
# 继续搜索下一个文件
|
d_len: sizet = d_i - d_start
|
||||||
if win32file.FindNextFileA(handle, find_data) == 0:
|
if d_len == 0: continue
|
||||||
break
|
dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||||
|
if dir_buf is None: VLogger.error("failed to allocate the includes directory; aborting scan", "scan")
|
||||||
win32file.FindClose(handle)
|
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
|
||||||
if subdir_failed > 0:
|
dir_buf[d_len] = '\0'
|
||||||
VLogger.error(f"子目录扫描失败 {subdir_failed} 个", "scan")
|
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
|
||||||
|
stdlib.free(dir_buf)
|
||||||
return 0
|
|
||||||
|
return result
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# scan_includes - 扫描 includes 目录入口(返回所有 .vp 文件路径与 SHA1)
|
# find_entry_by_module - find a file entry by module name
|
||||||
# ============================================================
|
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||||
def scan_includes(pool: memhub.MemBuddy*,
|
"""Find a file entry by module name."""
|
||||||
includes_dirs: str) -> ScanResult*:
|
if result is None or module_name is None: return None
|
||||||
"""扫描 includes 目录(支持空格分隔的多目录),返回所有 .vp/.py 文件的扫描结果"""
|
entry_size: sizet = FileEntry.__sizeof__()
|
||||||
if pool is None or includes_dirs is None:
|
for i in range(int(result.Count)):
|
||||||
return None
|
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||||
|
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||||
result: ScanResult* = create_scan_result(pool)
|
if entry is not None:
|
||||||
if result is None:
|
if entry.ModuleName is not None:
|
||||||
return None
|
if string.strcmp(entry.ModuleName, module_name) == 0: return entry
|
||||||
|
return None
|
||||||
# 解析空格分隔的多 includes 目录(如 "../../includes ./includes"),逐目录递归扫描合并
|
|
||||||
dirs_total: sizet = string.strlen(includes_dirs)
|
|
||||||
d_i: sizet = 0
|
# _PathExistsA - path existence check (GetFileAttributesA)
|
||||||
while d_i < dirs_total:
|
def _PathExistsA(path: str) -> int:
|
||||||
while d_i < dirs_total and includes_dirs[d_i] == ' ':
|
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
|
||||||
d_i += 1
|
if path is None: return 0
|
||||||
if d_i >= dirs_total:
|
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||||
break
|
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
||||||
d_start: sizet = d_i
|
return 0
|
||||||
while d_i < dirs_total and includes_dirs[d_i] != ' ':
|
return 1
|
||||||
d_i += 1
|
|
||||||
d_len: sizet = d_i - d_start
|
|
||||||
if d_len == 0:
|
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
|
||||||
continue
|
# out_abs_path/out_rel_path/out_inc_dir are rawptr output slots.
|
||||||
dir_buf: bytes = stdlib.malloc(d_len + 1)
|
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
|
||||||
if dir_buf is None:
|
if inc_dir is None or rel_seg is None or suffix is None: return 0
|
||||||
VLogger.error("includes 目录分配失败,终止扫描", "scan")
|
inc_len: sizet = string.strlen(inc_dir)
|
||||||
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
|
suf_len: sizet = string.strlen(suffix)
|
||||||
dir_buf[d_len] = '\0'
|
# candidate absolute path: {inc}/{rel}{suffix} (hand-built to avoid %. *s)
|
||||||
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
|
cand_len: sizet = inc_len + 1 + rel_len + suf_len
|
||||||
stdlib.free(dir_buf)
|
cand: bytes = stdlib.malloc(cand_len + 1)
|
||||||
|
if cand is None: return 0
|
||||||
return result
|
string.strcpy(cand, inc_dir)
|
||||||
|
cand[inc_len] = '/'
|
||||||
|
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
|
||||||
# ============================================================
|
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
|
||||||
# find_entry_by_module - 按模块名查找文件条目
|
if _PathExistsA(cand) == 0:
|
||||||
# ============================================================
|
stdlib.free(cand)
|
||||||
def find_entry_by_module(result: ScanResult*,
|
return 0
|
||||||
module_name: str) -> FileEntry*:
|
# path relative to the includes root: {rel}{suffix}
|
||||||
"""按模块名查找文件条目"""
|
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
|
||||||
if result is None or module_name is None:
|
if rel_path is None:
|
||||||
return None
|
stdlib.free(cand)
|
||||||
entry_size: sizet = FileEntry.__sizeof__()
|
return 0
|
||||||
for i in range(result.Count):
|
string.strncpy(rel_path, rel_seg, rel_len)
|
||||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
rel_path[rel_len] = '\0'
|
||||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
string.strcat(rel_path, suffix)
|
||||||
if entry is not None:
|
# Deep-copy the owning includes root dir; allocate everything before writing
|
||||||
if entry.ModuleName is not None:
|
# slots to avoid a half-written state when inc allocation fails.
|
||||||
if string.strcmp(entry.ModuleName, module_name) == 0:
|
inc_buf: bytes = None
|
||||||
return entry
|
if out_inc_dir is not None:
|
||||||
return None
|
inc_buf = stdlib.malloc(inc_len + 1)
|
||||||
|
if inc_buf is None:
|
||||||
|
stdlib.free(cand)
|
||||||
# ============================================================
|
stdlib.free(rel_path)
|
||||||
# _PathExistsA - 路径存在性检查(GetFileAttributesA)
|
return 0
|
||||||
# ============================================================
|
string.strcpy(inc_buf, inc_dir)
|
||||||
def _PathExistsA(path: str) -> int:
|
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
|
||||||
"""路径是否存在(1=存在,0=不存在/参数错误)
|
# (sizet*): rawptr* folds to rawptr and [0] becomes a 1-byte access.
|
||||||
|
if out_abs_path is not None:
|
||||||
用 GetFileAttributesA(无输出缓冲)替代 FindFirstFileA:后者写
|
abs_slot: sizet* = (sizet*)(out_abs_path)
|
||||||
WIN32_FIND_DATAA 结构,而 TPV 该结构 sizeof 小于 Windows 实际写入
|
abs_slot[0] = sizet(cand)
|
||||||
(约 592 字节,差距 >270 字节),会越界写坏相邻堆块(BFS 输出槽
|
else: stdlib.free(cand)
|
||||||
0x2e 崩溃根因)。GetFileAttributesA 只返回属性值,无越界风险。
|
if out_rel_path is not None:
|
||||||
"""
|
rel_slot: sizet* = (sizet*)(out_rel_path)
|
||||||
if path is None:
|
rel_slot[0] = sizet(rel_path)
|
||||||
return 0
|
else: stdlib.free(rel_path)
|
||||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
if out_inc_dir is not None and inc_buf is not None:
|
||||||
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
inc_slot: sizet* = (sizet*)(out_inc_dir)
|
||||||
return 0
|
inc_slot[0] = sizet(inc_buf)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ResolveModuleToPath - module name -> file path, resolved on demand (no full scan).
|
||||||
# _TryResolveModuleFile - 尝试单个候选路径并写输出
|
# Tries each includes dir, preferring file modules over packages. Returns 0 found, 1 not.
|
||||||
#
|
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
|
||||||
# Args:
|
if includes_dirs is None or mod_name is None: return 1
|
||||||
# inc_dir: includes 根目录
|
mn_len: sizet = string.strlen(mod_name)
|
||||||
# rel_seg: 模块名转出的相对路径段(ast/parser)
|
if mn_len == 0: return 1
|
||||||
# rel_len: rel_seg 长度
|
# mod_name -> relative path segment ('.' -> '/'): ast.parser -> ast/parser
|
||||||
# suffix: 候选后缀(".vp" 或 "/__init__.vp")
|
rel_seg: bytes = stdlib.malloc(mn_len + 1)
|
||||||
# out_abs_path/out_rel_path/out_inc_dir: 输出槽(t.CPtr 指针)
|
if rel_seg is None: return 1
|
||||||
#
|
rel_len: sizet = 0
|
||||||
# Returns:
|
for mi in range(int(mn_len)):
|
||||||
# 1 命中(已写输出),0 未命中
|
ch_m: char = mod_name[mi]
|
||||||
# ============================================================
|
if ch_m == '.': rel_seg[rel_len] = '/'
|
||||||
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet,
|
else: rel_seg[rel_len] = ch_m
|
||||||
suffix: str,
|
rel_len += 1
|
||||||
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
|
rel_seg[rel_len] = '\0'
|
||||||
out_inc_dir: t.CPtr) -> int:
|
|
||||||
if inc_dir is None or rel_seg is None or suffix is None:
|
found: int = 0
|
||||||
return 0
|
d_total: sizet = string.strlen(includes_dirs)
|
||||||
inc_len: sizet = string.strlen(inc_dir)
|
d_i: sizet = 0
|
||||||
suf_len: sizet = string.strlen(suffix)
|
while d_i < d_total:
|
||||||
# 候选绝对路径: {inc}/{rel}{suffix}(手写拼接,避免 %. *s 精度修饰符)
|
while d_i < d_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||||
cand_len: sizet = inc_len + 1 + rel_len + suf_len
|
if d_i >= d_total: break
|
||||||
cand: bytes = stdlib.malloc(cand_len + 1)
|
d_start: sizet = d_i
|
||||||
if cand is None:
|
while d_i < d_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||||
return 0
|
d_len: sizet = d_i - d_start
|
||||||
string.strcpy(cand, inc_dir)
|
if d_len == 0: continue
|
||||||
cand[inc_len] = '/'
|
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||||
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
|
if inc_dir_buf is None: continue
|
||||||
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
|
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
|
||||||
if _PathExistsA(cand) == 0:
|
inc_dir_buf[d_len] = '\0'
|
||||||
stdlib.free(cand)
|
# candidate 1: {inc}/{rel}.vp (file module)
|
||||||
return 0
|
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||||
# 相对 includes 根目录的路径: {rel}{suffix}
|
found = 1
|
||||||
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
|
stdlib.free(inc_dir_buf)
|
||||||
if rel_path is None:
|
break
|
||||||
stdlib.free(cand)
|
# candidate 2: {inc}/{rel}/__init__.vp (package module)
|
||||||
return 0
|
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||||
string.strncpy(rel_path, rel_seg, rel_len)
|
found = 1
|
||||||
rel_path[rel_len] = '\0'
|
stdlib.free(inc_dir_buf)
|
||||||
string.strcat(rel_path, suffix)
|
if found != 0: break
|
||||||
# 所属 includes 根目录深拷贝:先全部分配就绪再统一写槽,
|
stdlib.free(rel_seg)
|
||||||
# 避免 inc 分配失败时 abs/rel 已转移所有权造成的半写状态
|
if found != 0: return 0
|
||||||
inc_buf: bytes = None
|
return 1
|
||||||
if out_inc_dir is not None:
|
|
||||||
inc_buf = stdlib.malloc(inc_len + 1)
|
|
||||||
if inc_buf is None:
|
|
||||||
stdlib.free(cand)
|
|
||||||
stdlib.free(rel_path)
|
|
||||||
return 0
|
|
||||||
string.strcpy(inc_buf, inc_dir)
|
|
||||||
# 写输出槽(stdlib 深拷贝,调用方负责 free)
|
|
||||||
# 槽联合必须用 (sizet*):t.CPtr|t.CPtr 会被折叠成 t.CPtr,
|
|
||||||
# [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为
|
|
||||||
# 8 字节,写入用 sizet(ptr) 直接转换;读取用 t\.CVoid(u64, t.CPtr)
|
|
||||||
# 转回指针(t\.CVoid 仅配合 t.CPtr 表示 void* 强转)。
|
|
||||||
if out_abs_path is not None:
|
|
||||||
abs_slot: sizet* = (sizet*)(out_abs_path)
|
|
||||||
abs_slot[0] = sizet(cand)
|
|
||||||
else:
|
|
||||||
stdlib.free(cand)
|
|
||||||
if out_rel_path is not None:
|
|
||||||
rel_slot: sizet* = (sizet*)(out_rel_path)
|
|
||||||
rel_slot[0] = sizet(rel_path)
|
|
||||||
else:
|
|
||||||
stdlib.free(rel_path)
|
|
||||||
if out_inc_dir is not None and inc_buf is not None:
|
|
||||||
inc_slot: sizet* = (sizet*)(out_inc_dir)
|
|
||||||
inc_slot[0] = sizet(inc_buf)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# ResolveModuleToPath - 模块名→文件路径(按文件系统按需解析,不做全量目录遍历)
|
|
||||||
#
|
|
||||||
# 解析规则(多 includes 目录逐个尝试,模块优先于包):
|
|
||||||
# {inc_dir}/{mod.replace('.','/')}.vp (文件模块)
|
|
||||||
# {inc_dir}/{mod.replace('.','/')}/__init__.vp (包模块)
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# includes_dirs: 空格分隔的多个 includes 根目录
|
|
||||||
# mod_name: 绝对模块名
|
|
||||||
# out_abs_path: 输出绝对路径(stdlib.malloc,调用方负责 free)
|
|
||||||
# out_rel_path: 输出相对 includes 根目录的路径(同上,如 ast/parser.vp)
|
|
||||||
# out_inc_dir: 输出所属 includes 根目录(同上)
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# 0 找到;1 未找到/参数错误
|
|
||||||
# ============================================================
|
|
||||||
def ResolveModuleToPath(includes_dirs: str, mod_name: str,
|
|
||||||
out_abs_path: t.CPtr, out_rel_path: t.CPtr,
|
|
||||||
out_inc_dir: t.CPtr) -> int:
|
|
||||||
if includes_dirs is None or mod_name is None:
|
|
||||||
return 1
|
|
||||||
mn_len: sizet = string.strlen(mod_name)
|
|
||||||
if mn_len == 0:
|
|
||||||
return 1
|
|
||||||
# mod_name → 相对路径段(. → /):ast.parser → ast/parser
|
|
||||||
rel_seg: bytes = stdlib.malloc(mn_len + 1)
|
|
||||||
if rel_seg is None:
|
|
||||||
return 1
|
|
||||||
rel_len: sizet = 0
|
|
||||||
for mi in range(mn_len):
|
|
||||||
ch_m: char = mod_name[mi]
|
|
||||||
if ch_m == '.':
|
|
||||||
rel_seg[rel_len] = '/'
|
|
||||||
else:
|
|
||||||
rel_seg[rel_len] = ch_m
|
|
||||||
rel_len += 1
|
|
||||||
rel_seg[rel_len] = '\0'
|
|
||||||
|
|
||||||
found: int = 0
|
|
||||||
d_total: sizet = string.strlen(includes_dirs)
|
|
||||||
d_i: sizet = 0
|
|
||||||
while d_i < d_total:
|
|
||||||
while d_i < d_total and includes_dirs[d_i] == ' ':
|
|
||||||
d_i += 1
|
|
||||||
if d_i >= d_total:
|
|
||||||
break
|
|
||||||
d_start: sizet = d_i
|
|
||||||
while d_i < d_total and includes_dirs[d_i] != ' ':
|
|
||||||
d_i += 1
|
|
||||||
d_len: sizet = d_i - d_start
|
|
||||||
if d_len == 0:
|
|
||||||
continue
|
|
||||||
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
|
|
||||||
if inc_dir_buf is None:
|
|
||||||
continue
|
|
||||||
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
|
|
||||||
inc_dir_buf[d_len] = '\0'
|
|
||||||
# 候选 1: {inc}/{rel}.vp(文件模块)
|
|
||||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp",
|
|
||||||
out_abs_path, out_rel_path, out_inc_dir) != 0:
|
|
||||||
found = 1
|
|
||||||
stdlib.free(inc_dir_buf)
|
|
||||||
break
|
|
||||||
# 候选 2: {inc}/{rel}/__init__.vp(包模块)
|
|
||||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp",
|
|
||||||
out_abs_path, out_rel_path, out_inc_dir) != 0:
|
|
||||||
found = 1
|
|
||||||
stdlib.free(inc_dir_buf)
|
|
||||||
if found != 0:
|
|
||||||
break
|
|
||||||
stdlib.free(rel_seg)
|
|
||||||
if found != 0:
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
+2107
-2970
File diff suppressed because it is too large.
Load diff
+246
-275
@@ -1,275 +1,246 @@
|
|||||||
import t, c
|
from stdint import *
|
||||||
from stdint import *
|
import stdio
|
||||||
import stdio
|
import memhub
|
||||||
import stdlib
|
import sys
|
||||||
import memhub
|
import win32.win32console as win32cmd
|
||||||
import sys
|
import win32.win32file as win32file
|
||||||
import w32.win32console as w32cmd
|
import win32.win32base as win32base
|
||||||
import w32.win32file as w32file
|
|
||||||
import w32.win32base as win32base
|
|
||||||
|
# VLogger - native logger with colored Windows console output.
|
||||||
|
# Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py).
|
||||||
# ============================================================
|
|
||||||
# VLogger - 原生日志系统(Windows 控制台彩色输出)
|
# Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point.
|
||||||
# 输出格式: ├ LEVEL: message / ├ LEVEL[category]: message(与 TPC lib/core/VLogger.py 对齐)
|
# mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL).
|
||||||
# ============================================================
|
extern def longjmp(buf: rawptr, val: int)
|
||||||
|
|
||||||
# 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。
|
|
||||||
# mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为
|
# REPL recovery-point pointer: registered at session start, cleared at session end.
|
||||||
# _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。
|
# Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1).
|
||||||
extern def longjmp(buf: rawptr, val: int)
|
_repl_jmpbuf: rawptr = None
|
||||||
|
|
||||||
|
|
||||||
# REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。
|
# repl_abort - the single "terminate after diagnosis" exit in VPC.
|
||||||
# 生命周期 = 整个 REPL 会话:Repl 每轮片段都会重新武装(Repl.run 每轮 _setjmp +
|
# Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1).
|
||||||
# Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll →
|
def repl_abort() -> int:
|
||||||
# llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。
|
"""Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit."""
|
||||||
# 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。
|
if _repl_jmpbuf is not None:
|
||||||
_repl_jmpbuf: rawptr = None
|
stdio.fflush(None)
|
||||||
|
longjmp(_repl_jmpbuf, 1)
|
||||||
|
sys.exit(1)
|
||||||
# ============================================================
|
return 0
|
||||||
# repl_abort - TPV 内"诊断后终止"的唯一出口
|
|
||||||
#
|
|
||||||
# 约定:调用方**已打印**诊断(Traceback / VLogger.error / [OOM] 等),本函数只决定
|
def set_repl_jmpbuf(buf: rawptr) -> int:
|
||||||
# "跳回 REPL 继续"还是"终止进程":
|
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
|
||||||
# * REPL 会话(_repl_jmpbuf 已注册):fflush 后 longjmp 回 Repl.run 的恢复点,
|
global _repl_jmpbuf
|
||||||
# 进程存活、提示符继续(宽容 Traceback);
|
_repl_jmpbuf = buf
|
||||||
# * 批处理 / 未武装:sys.exit(1),与改动前的 fail-fast 行为逐字一致。
|
return 0
|
||||||
# 收敛点:Logger.error、模块级 error、TypeGenErr.fatal_error、
|
|
||||||
# ImportsGenMixin._report_import_error、CallGenBase/VpiReader 的链表哨兵。
|
|
||||||
# ============================================================
|
def clear_repl_jmpbuf() -> int:
|
||||||
def repl_abort() -> int:
|
"""Clear the registered jmp_buf at REPL session end."""
|
||||||
"""诊断后的统一出口:已注册恢复点则 longjmp 回 REPL,否则终止进程。"""
|
global _repl_jmpbuf
|
||||||
if _repl_jmpbuf is not None:
|
_repl_jmpbuf = None
|
||||||
stdio.fflush(None)
|
return 0
|
||||||
longjmp(_repl_jmpbuf, 1)
|
|
||||||
sys.exit(1)
|
# Log levels
|
||||||
return 0
|
enum LogLevel:
|
||||||
|
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
||||||
|
|
||||||
def set_repl_jmpbuf(buf: t.CPtr) -> int:
|
|
||||||
"""注册 REPL 的 jmp_buf(会话开始;每片段入口可幂等重注册)。"""
|
# Win32 console foreground color attributes
|
||||||
global _repl_jmpbuf
|
# 值用 WORD(...) 显式定宽为 uint16(与 _log 的 sym_color/level_color 形参一致)。
|
||||||
_repl_jmpbuf = buf
|
# 注意:仅写 `define X: WORD = 0x0002` 不够 —— define 的宽度由**右侧表达式的类型**
|
||||||
return 0
|
# 决定(win32file.vp 的 `define STD_OUTPUT_HANDLE: DWORD = DWORD(-10)` 同款),
|
||||||
|
# 否则常量仍是 i32 → 传给 WORD 形参即 32→16 隐式窄化。
|
||||||
|
define FOREGROUND_BLUE: WORD = WORD(0x0001)
|
||||||
def clear_repl_jmpbuf() -> int:
|
define FOREGROUND_GREEN: WORD = WORD(0x0002)
|
||||||
"""清除已注册的 jmp_buf(REPL 会话结束时调用;中途不清)。"""
|
define FOREGROUND_RED: WORD = WORD(0x0004)
|
||||||
global _repl_jmpbuf
|
define FOREGROUND_INTENSITY: WORD = WORD(0x0008)
|
||||||
_repl_jmpbuf = None
|
define FOREGROUND_WHITE: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||||
return 0
|
define FOREGROUND_CYAN: WORD = WORD(FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||||
|
define FOREGROUND_YELLOW: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN)
|
||||||
# 日志级别
|
define FOREGROUND_MAGENTA: WORD = WORD(FOREGROUND_RED | FOREGROUND_BLUE)
|
||||||
enum LogLevel:
|
|
||||||
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
# Win32 console background color attributes
|
||||||
|
define BACKGROUND_RED: WORD = WORD(0x0040)
|
||||||
|
define BACKGROUND_GREEN: WORD = WORD(0x0020)
|
||||||
# Win32 控制台前景色属性
|
define BACKGROUND_BLUE: WORD = WORD(0x0010)
|
||||||
define FOREGROUND_BLUE = 0x0001
|
define BACKGROUND_INTENSITY: WORD = WORD(0x0080)
|
||||||
define FOREGROUND_GREEN = 0x0002
|
define BACKGROUND_WHITE: WORD = WORD(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
|
||||||
define FOREGROUND_RED = 0x0004
|
|
||||||
define FOREGROUND_INTENSITY = 0x0008
|
|
||||||
define FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
novirtual class Logger:
|
||||||
define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN
|
_level: int
|
||||||
define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN
|
_console_handle: win32base.HANDLE
|
||||||
define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE
|
_use_color: int
|
||||||
|
jit_flag: bool
|
||||||
# Win32 控制台背景色属性
|
__mbuddy__: memhub.MemBuddy*
|
||||||
define BACKGROUND_RED = 0x0040
|
|
||||||
define BACKGROUND_GREEN = 0x0020
|
def __init__(self, level: int = 1):
|
||||||
define BACKGROUND_BLUE = 0x0010
|
self._level = level
|
||||||
define BACKGROUND_INTENSITY = 0x0080
|
self._console_handle = win32file.GetStdHandle(win32file.STD_OUTPUT_HANDLE)
|
||||||
define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
self._use_color = 1
|
||||||
|
self.jit_flag = False
|
||||||
|
self.__mbuddy__ = _mbuddy
|
||||||
novirtual class Logger:
|
|
||||||
_level: int
|
def _set_color(self, attr: WORD) -> int:
|
||||||
_console_handle: win32base.HANDLE
|
uc: int = self._use_color
|
||||||
_use_color: int
|
if uc:
|
||||||
jit_flag: bool
|
ch: win32base.HANDLE = self._console_handle
|
||||||
__mbuddy__: memhub.MemBuddy*
|
ret: int = win32cmd.SetConsoleTextAttribute(ch, attr)
|
||||||
|
return ret
|
||||||
def __init__(self, level: int = 1):
|
return 0
|
||||||
self._level = level
|
|
||||||
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
|
def _reset_color(self) -> int:
|
||||||
self._use_color = 1
|
if self._use_color: return win32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||||
self.jit_flag = False
|
return 0
|
||||||
self.__mbuddy__ = _mbuddy
|
|
||||||
|
# _log - core log output with segmented coloring (prefix symbol / level tag / message).
|
||||||
def _set_color(self, attr: WORD) -> int:
|
# Format: ├ LEVEL: msg or ├ LEVEL[category]: msg.
|
||||||
uc: int = self._use_color
|
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int:
|
||||||
if uc:
|
# Color the prefix symbol
|
||||||
ch: win32base.HANDLE = self._console_handle
|
self._set_color(sym_color)
|
||||||
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
|
stdio.printf("%s ", prefix_sym)
|
||||||
return ret
|
# Color the level tag
|
||||||
return 0
|
self._set_color(level_color)
|
||||||
|
stdio.printf("%s", level_str)
|
||||||
def _reset_color(self) -> int:
|
self._reset_color()
|
||||||
if self._use_color:
|
# Optional category
|
||||||
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
if category is not None and category[0] != 0: stdio.printf(" [%s]", category)
|
||||||
return 0
|
stdio.printf(": %s\n", msg)
|
||||||
|
return 0
|
||||||
# ============================================================
|
|
||||||
# _log - 核心日志输出(分段着色:前缀符号 ├ / LEVEL 标签 / 消息内容)
|
def debug(self, msg: str, category: str = "") -> int:
|
||||||
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
|
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
||||||
# ============================================================
|
|
||||||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
|
def info(self, msg: str, category: str = "") -> int:
|
||||||
sym_color: WORD, level_color: WORD,
|
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||||
category: str = "") -> int:
|
return ret
|
||||||
# 前缀符号着色
|
|
||||||
self._set_color(sym_color)
|
def warning(self, msg: str, category: str = "") -> int:
|
||||||
stdio.printf("%s ", prefix_sym)
|
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||||
# LEVEL 标签着色
|
|
||||||
self._set_color(level_color)
|
def error(self, msg: str, category: str = "") -> int:
|
||||||
stdio.printf("%s", level_str)
|
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, WORD(FOREGROUND_RED | FOREGROUND_INTENSITY), category)
|
||||||
self._reset_color()
|
# JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit.
|
||||||
# category 可选
|
if self.jit_flag:
|
||||||
if category is not None and category[0] != 0:
|
repl_abort()
|
||||||
stdio.printf(" [%s]", category)
|
return ret
|
||||||
stdio.printf(": %s\n", msg)
|
# Batch mode: terminate immediately on error.
|
||||||
return 0
|
sys.exit(1)
|
||||||
|
return ret
|
||||||
def debug(self, msg: str, category: str = "") -> int:
|
|
||||||
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
def success(self, msg: str, category: str = "") -> int:
|
||||||
|
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||||
def info(self, msg: str, category: str = "") -> int:
|
|
||||||
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
def critical(self, msg: str, category: str = "") -> int:
|
||||||
return ret
|
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
|
||||||
|
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, WORD(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY), category)
|
||||||
def warning(self, msg: str, category: str = "") -> int:
|
|
||||||
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
def set_level(self, level: int) -> int:
|
||||||
|
self._level = level
|
||||||
def error(self, msg: str, category: str = "") -> int:
|
return 0
|
||||||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
|
||||||
# JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程)
|
def set_jit_flag(self, v: bool) -> int:
|
||||||
if self.jit_flag:
|
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
|
||||||
repl_abort()
|
self.jit_flag = v
|
||||||
return ret
|
return 0
|
||||||
# 批处理模式:错误立即终止进程(行为与改动前逐字一致)
|
|
||||||
sys.exit(1)
|
# Advanced log methods.
|
||||||
return ret
|
|
||||||
|
def banner(self, msg: str) -> int:
|
||||||
def success(self, msg: str, category: str = "") -> int:
|
"""Print a blue-highlighted section banner."""
|
||||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||||||
|
stdio.printf("\n=== %s ===\n\n", msg)
|
||||||
def critical(self, msg: str, category: str = "") -> int:
|
self._reset_color()
|
||||||
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
|
return 0
|
||||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category)
|
|
||||||
|
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||||
def set_level(self, level: int) -> int:
|
"""Format a compile error with a red-background header, location and message."""
|
||||||
self._level = level
|
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||||||
return 0
|
stdio.printf(" COMPILE ERROR ")
|
||||||
|
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||||||
def set_jit_flag(self, v: bool) -> int:
|
stdio.printf("\n")
|
||||||
"""设置 JIT/REPL 模式标志:True 时 error 改为 longjmp 回 REPL 循环。"""
|
if file is not None:
|
||||||
self.jit_flag = v
|
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||||
return 0
|
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||||
|
stdio.printf(" error: %s\n", msg)
|
||||||
# ============================================================
|
self._reset_color()
|
||||||
# 高级日志方法
|
return 0
|
||||||
# ============================================================
|
|
||||||
|
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||||
def banner(self, msg: str) -> int:
|
"""Format a compile warning with a yellow background, location and message."""
|
||||||
"""输出分节标题(蓝色高亮,与 TPC banner 风格对齐)。"""
|
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
stdio.printf(" COMPILE WARNING ")
|
||||||
stdio.printf("\n=== %s ===\n\n", msg)
|
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||||
self._reset_color()
|
stdio.printf("\n")
|
||||||
return 0
|
if file is not None:
|
||||||
|
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||||
"""格式化编译错误输出(红底白字标题 + 位置 + 错误信息)。"""
|
stdio.printf(" warning: %s\n", msg)
|
||||||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
self._reset_color()
|
||||||
stdio.printf(" 编译错误 ")
|
return 0
|
||||||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
|
||||||
stdio.printf("\n")
|
|
||||||
if file is not None:
|
# Global mbuddy pointer (injected by lib.InitLib).
|
||||||
if file[0] != 0 and line > 0:
|
_mbuddy: memhub.MemBuddy*
|
||||||
stdio.printf(" 位置: %s:%d\n", file, line)
|
|
||||||
elif file[0] != 0:
|
# Global logger pointer
|
||||||
stdio.printf(" 文件: %s\n", file)
|
logger: Logger*
|
||||||
stdio.printf(" 错误: %s\n", msg)
|
log: Logger*
|
||||||
self._reset_color()
|
|
||||||
return 0
|
def init_logger():
|
||||||
|
"""Initialize the global logger instance."""
|
||||||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
global logger, log
|
||||||
"""格式化编译警告输出(黄底黑字标题 + 位置 + 警告信息)。"""
|
raw: rawptr = _mbuddy.alloc(Logger.__sizeof__())
|
||||||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
# if raw is None: return None (memory management still unclear)
|
||||||
stdio.printf(" 编译警告 ")
|
logger = raw
|
||||||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
logger.__before_init__()
|
||||||
stdio.printf("\n")
|
logger.__init__(LogLevel.INFO)
|
||||||
if file is not None:
|
log = logger
|
||||||
if file[0] != 0 and line > 0:
|
# if fmtbuffer is None: return None
|
||||||
stdio.printf(" 位置: %s:%d\n", file, line)
|
|
||||||
elif file[0] != 0:
|
|
||||||
stdio.printf(" 文件: %s\n", file)
|
# --nowarn-register switch: suppresses Phase A dependency WARN output (output only).
|
||||||
stdio.printf(" 警告: %s\n", msg)
|
_nowarn_register: int = 0
|
||||||
self._reset_color()
|
|
||||||
return 0
|
# Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed).
|
||||||
|
|
||||||
|
def info(msg: str, category: str = "") -> int:
|
||||||
# 全局 mbuddy 指针(由 lib.InitLib 注入)
|
"""Log at INFO level."""
|
||||||
_mbuddy: memhub.MemBuddy*
|
if log is not None: return log.info(msg, category)
|
||||||
|
return 0
|
||||||
# 全局 logger 指针
|
|
||||||
logger: Logger*
|
|
||||||
log: Logger*
|
def warning(msg: str, category: str = "") -> int:
|
||||||
|
"""Log at WARN level."""
|
||||||
def init_logger():
|
if log is not None: return log.warning(msg, category)
|
||||||
"""初始化全局 logger 实例。"""
|
return 0
|
||||||
global logger, log
|
|
||||||
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
|
|
||||||
# if raw is None: return None 存疑,如何去管理内存?
|
def error(msg: str, category: str = "") -> int:
|
||||||
logger = raw
|
"""Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL."""
|
||||||
logger.__before_init__()
|
if log is not None: return log.error(msg, category)
|
||||||
logger.__init__(LogLevel.INFO)
|
# When the logger is unavailable, still use the unified exit (REPL can recover if registered)
|
||||||
log = logger
|
repl_abort()
|
||||||
# if fmtbuffer is None: return None
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
def success(msg: str, category: str = "") -> int:
|
||||||
# --nowarn-register 开关:抑制 Phase A 依赖类 WARN 输出(仅抑制输出,不影响内部逻辑)
|
"""Log at SUCCESS level."""
|
||||||
# ============================================================
|
if log is not None: return log.success(msg, category)
|
||||||
_nowarn_register: int = 0
|
return 0
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐,无需先 get_logger())
|
def critical(msg: str, category: str = "") -> int:
|
||||||
# ============================================================
|
"""Log at CRITICAL level (fatal, red background)."""
|
||||||
|
if log is not None: return log.critical(msg, category)
|
||||||
def info(msg: str, category: str = "") -> int:
|
return 0
|
||||||
"""输出 INFO 级别日志。"""
|
|
||||||
if log is not None: return log.info(msg, category)
|
|
||||||
return 0
|
def debug(msg: str, category: str = "") -> int:
|
||||||
|
"""Log at DEBUG level."""
|
||||||
|
if log is not None: return log.debug(msg, category)
|
||||||
def warning(msg: str, category: str = "") -> int:
|
return 0
|
||||||
"""输出 WARN 级别日志。"""
|
|
||||||
if log is not None: return log.warning(msg, category)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def error(msg: str, category: str = "") -> int:
|
|
||||||
"""输出 ERROR 级别日志:批处理模式终止进程;JIT/REPL 模式 longjmp 回 REPL。"""
|
|
||||||
if log is not None: return log.error(msg, category)
|
|
||||||
# logger 不可用时也走统一出口(REPL 已注册恢复点时仍可恢复)
|
|
||||||
repl_abort()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def success(msg: str, category: str = "") -> int:
|
|
||||||
"""输出 SUCCESS 级别日志。"""
|
|
||||||
if log is not None: return log.success(msg, category)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def critical(msg: str, category: str = "") -> int:
|
|
||||||
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
|
|
||||||
if log is not None: return log.critical(msg, category)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def debug(msg: str, category: str = "") -> int:
|
|
||||||
"""输出 DEBUG 级别日志。"""
|
|
||||||
if log is not None: return log.debug(msg, category)
|
|
||||||
return 0
|
|
||||||
@@ -1,3 +1 @@
|
|||||||
# ============================================================
|
# core package entry
|
||||||
# core 包入口
|
|
||||||
# ============================================================
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
|||||||
|
# builtin package entry - compiler-side implementation of builtin libs (e.g. viperbuild).
|
||||||
+480
-710
File diff suppressed because it is too large.
Load diff
@@ -619,7 +619,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||||||
If the program does terminal interaction, make it output a short
|
If the program does terminal interaction, make it output a short
|
||||||
notice like this when it starts in an interactive mode:
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
TransPyV Copyright (C) 2026 TermiNexus
|
ViperC Copyright (C) 2026 TermiNexus
|
||||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions; type `show c' for details.
|
under certain conditions; type `show c' for details.
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Viper's original sin is that the person who invented it wasn't smart enough; its abilities don't match its ideals.
|
||||||
@@ -1,223 +1,10 @@
|
|||||||
# TransPyV
|
This is a request to remove the Chinese portions from the ViperC README. Here is the resulting document with all Chinese sections stripped, keeping only the English content.
|
||||||
|
|
||||||
[English](#english) | [中文](#中文)
|
|
||||||
|
|
||||||
The **Viper language compiler, written in Viper itself** — and it is **self-hosted**: TransPyV compiles its own source and produces `TransPyV.exe`. It translates Viper sources to LLVM IR, then drives `llc` + `clang++` to link a native Windows executable. The first-generation CPython compiler ([TransPyC](https://git.gvsds.com/GVSDS/TransPyC)) is now only kept as the bootstrapping **seed**.
|
|
||||||
|
|
||||||
**用 Viper 语言编写的 Viper 编译器**,且**已完成自举**:TransPyV 能编译自身源码并产出 `TransPyV.exe`。它把 Viper 源码翻译为 LLVM IR,再调用 `llc` + `clang++` 链接为 Windows 原生可执行文件。第一代 CPython 编译器([TransPyC](https://git.gvsds.com/GVSDS/TransPyC))现在只作为自举**种子**保留。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<a id="中文"></a>
|
# ViperC
|
||||||
|
|
||||||
## 中文
|
The **Viper language compiler, written in Viper itself** — and it is **self-hosted**: ViperC compiles its own source and produces `viperc.exe`. It translates Viper sources to LLVM IR, then drives `llc` + `clang++` to link a native Windows executable. The first-generation CPython compiler ([TransPyC](https://git.gvsds.com/GVSDS/TransPyC)) is now only kept as the bootstrapping **seed**.
|
||||||
|
|
||||||
### 这是什么
|
|
||||||
|
|
||||||
`TransPyV` 是 Viper 的第二代编译器:**用 Viper 自己写、也被 Viper 自己编译**。整条链路上没有任何 Python 运行时参与编译 —— 词法/语法分析走 `includes/ast`(自研解析器,9 个文件),IR 生成走 `App/lib/core/Gen/`,目标代码由 `llc` 与 `clang++` 产出。
|
|
||||||
|
|
||||||
自举链路:
|
|
||||||
|
|
||||||
```text
|
|
||||||
TransPyC(种子,CPython 实现)
|
|
||||||
│ 只做一次:把 TransPyV 源码编译成 TransPyV.exe
|
|
||||||
▼
|
|
||||||
TransPyV.exe(第一阶段产物)
|
|
||||||
│ 自举:用这个 exe 再编译 TransPyV 自身
|
|
||||||
▼
|
|
||||||
TransPyV.exe(自举产物,当前仓库随包/随 exe\ 提供的那份)
|
|
||||||
│
|
|
||||||
└─ 用它编译任意 Viper 工程(ViperApps\*、Test\* …)
|
|
||||||
```
|
|
||||||
|
|
||||||
现状与规模(本机实测):
|
|
||||||
|
|
||||||
| 项 | 值 |
|
|
||||||
| --- | --- |
|
|
||||||
| 编译器源码 | `App/` 下 **76 个 `.vp` 文件,64,656 行** |
|
|
||||||
| 其中翻译器核心 | `App/lib/core/Gen/` **52 个文件,49,978 行** |
|
|
||||||
| 阶段与合并 | `App/lib/Phase/` 6 文件 5,309 行;`App/lib/Stub/` 9 文件 6,877 行 |
|
|
||||||
| 工程与工具 | `App/lib/Projectrans/` 3 文件 1,008 行 |
|
|
||||||
| 自测用例 | `Test/` 下 **39 个 `.vp`**(`App/` 36 + `NegativeTest/` 2 + `Sha1Test/` 1) |
|
|
||||||
| 平台 | Windows x64(源码直接 `import w32.*`) |
|
|
||||||
|
|
||||||
> 自举可行性分析与历史数据见 [`wiki/13-bootstrapping.md`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)(该文标注:Phase A → Phase B/C 已跑通,编译器已脱离 Python 运行时)。
|
|
||||||
|
|
||||||
### 目录结构
|
|
||||||
|
|
||||||
```text
|
|
||||||
TransPyV/
|
|
||||||
├── project.vpj # 工程配置(source_dir=./App, build_dir=./.tpv_build, 输出 TransPyV.exe)
|
|
||||||
├── App/ # 编译器源码(Viper)
|
|
||||||
│ ├── main.vp # CLI 入口:内存池初始化、单例注入、参数解析、Phase 调度
|
|
||||||
│ └── lib/
|
|
||||||
│ ├── Projectrans/
|
|
||||||
│ │ ├── Config.vp # project.vpj 加载与路径解析(含 includes 缓存目录)
|
|
||||||
│ │ └── Utils.vp # SHA1 / 目录清理 / 杂项
|
|
||||||
│ ├── core/
|
|
||||||
│ │ ├── VLogger.vp # 日志(info/warn/error/banner/success)
|
|
||||||
│ │ ├── IncludesScanner.vp # includes 目录扫描(决定哪些需重编译)
|
|
||||||
│ │ ├── CompilerFactory.vp # 工厂:持有 mb + perm_pool 两个 MemBuddy 单例
|
|
||||||
│ │ └── Gen/ # 翻译器(52 文件)—— 把 AST 变为 LLVM IR
|
|
||||||
│ │ ├── Generator.vp / Translator.vp / GenApi.vp / GenTypes.vp
|
|
||||||
│ │ ├── BaseGenMixin / AssignGenMixin / VarGenMixin / ImportsGenMixin
|
|
||||||
│ │ ├── MatchGenMixin / AsyncGenMixin / StructGenMixin / TranslatorGenMixin
|
|
||||||
│ │ ├── Call/ (12) 调用生成:普通调用 / C 内建 / 构造 / 虚表 / 变参 / 内联汇编 …
|
|
||||||
│ │ ├── Class/ (6) 类:定义 / 泛型 / 虚表 / 枚举 / Mixin
|
|
||||||
│ │ ├── Expr/ (9) 表达式:名字 / 常量 / 运算 / 属性 / f-string / CDef …
|
|
||||||
│ │ ├── Func/ (3) 函数定义
|
|
||||||
│ │ ├── Stmt/ (3) 语句:声明 / C-if / Mixin
|
|
||||||
│ │ └── Type/ (6) 类型解析与 C 类型映射
|
|
||||||
│ ├── Phase/
|
|
||||||
│ │ ├── BuildPipeline.vp # .ll → llc → .obj → clang++ → .exe(含 ASan 插桩分支)
|
|
||||||
│ │ ├── Phase1/ # Phase 1:按需翻译 includes + stub 分离(.stub.ll / .text.ll)
|
|
||||||
│ │ └── Phase2/ # Phase 2:多文件工程翻译、IR 组合、编译
|
|
||||||
│ └── Stub/
|
|
||||||
│ ├── StubGen/ # .vpi 读取 + 声明转换
|
|
||||||
│ └── StubMerger/ # 多份 .stub.ll / .text.ll 依赖排序与合并
|
|
||||||
└── Test/ # 编译器自测(Viper)
|
|
||||||
├── project.vpj
|
|
||||||
├── App/ # 36 个 .vp(多为 *_test.vp),入口 test_main.vp 聚合全部用例
|
|
||||||
├── NegativeTest/ # 负向测试(故意编译失败)
|
|
||||||
└── Sha1Test/ # SHA1 命名空间测试
|
|
||||||
```
|
|
||||||
|
|
||||||
### 前提条件
|
|
||||||
|
|
||||||
| 工具 | 说明 |
|
|
||||||
| --- | --- |
|
|
||||||
| Windows 10/11 x64 | 编译器源码直接 `import w32.*`,当前仅 Windows |
|
|
||||||
| `llc` | LLVM 静态编译器(本机 22.1.8),由 `project.vpj` 的 `compiler.cmd` 指定 |
|
|
||||||
| `clang++` | mingw-w64 工具链,`-fuse-ld=lld` 链接(本机 `D:/msys64/mingw64/bin/clang++.exe`) |
|
|
||||||
| ≥ 1.5 GiB 空闲内存 | 启动即 `malloc` 1 GiB 主池 + 512 MiB 常驻池 |
|
|
||||||
| [TransPyC](https://git.gvsds.com/GVSDS/TransPyC) | **仅当需要重建自举种子时**才需要 |
|
|
||||||
|
|
||||||
目录布局(编译时需要能找到 `includes/`,`project.vpj` 里写的是相对路径 `../includes`):
|
|
||||||
|
|
||||||
```text
|
|
||||||
TransPyC\ # 工作区根
|
|
||||||
├── TransPyC\ # 第一代编译器(种子)
|
|
||||||
├── TransPyV\ # 本仓库
|
|
||||||
└── includes\ # Viper 标准库 + includes/ast 解析器
|
|
||||||
```
|
|
||||||
|
|
||||||
### 快速开始
|
|
||||||
|
|
||||||
```bat
|
|
||||||
:: 日常:直接用自举产物编译任意工程
|
|
||||||
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean
|
|
||||||
|
|
||||||
:: 编译并立即运行
|
|
||||||
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
|
|
||||||
|
|
||||||
:: 只有在需要重建编译器本体(自举种子)时,才动用 TransPyC
|
|
||||||
python TransPyC\Projectrans.py --project TransPyV\project.vpj --clean
|
|
||||||
```
|
|
||||||
|
|
||||||
产物位置 = `<工程目录>\<build_dir>\output\<linker.output>`,即默认 `<工程>\.tpv_build\output\app.exe`;
|
|
||||||
TransPyV 自身因 `linker.output` 为 `TransPyV.exe`,产物是 `TransPyV\.tpv_build\output\TransPyV.exe`。
|
|
||||||
|
|
||||||
跑编译器自测套件:
|
|
||||||
|
|
||||||
```bat
|
|
||||||
exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
|
|
||||||
```
|
|
||||||
|
|
||||||
### 命令行参数
|
|
||||||
|
|
||||||
| 参数 | 含义 |
|
|
||||||
| --- | --- |
|
|
||||||
| `--project <path>` | `project.vpj` 路径(不给则在当前目录、上级目录找) |
|
|
||||||
| `--src <dir>` | 单文件模式源目录(覆盖配置;入口取 `<dir>/main.vp`) |
|
|
||||||
| `--temp <dir>` | 声明接口临时目录(覆盖配置,默认 `<build_dir>/temp`) |
|
|
||||||
| `--output <dir>` | 输出目录(覆盖配置,默认 `<build_dir>/output`) |
|
|
||||||
| `--phase 1\|2\|all` | 1=只做 stub 分离;2=只做 IR 组合+编译;all=全部(默认) |
|
|
||||||
| `--cc <cmd>` | LLVM 编译器命令(覆盖配置) |
|
|
||||||
| `--build-dir <dir>` | 覆盖 `project.vpj` 的 `build_dir`(相对路径仍基于工程目录解析) |
|
|
||||||
| `--clean` | 清理 temp / output / includes 预编译缓存 |
|
|
||||||
| `--rebuild-includes` | 只删 includes 预编译缓存(`includes.binary`),强制重编 includes |
|
|
||||||
| `--clear-cache` | 只清 temp 缓存(`.vpi` / `.stub.ll` / `.text.ll`),强制重生成 stub |
|
|
||||||
| `--run` | 编译成功后立即执行产物 |
|
|
||||||
| `--repl` | 交互式 REPL(M1:表达式片段 → DLL → 调用回显);未给 `--project` 时按 cwd → `TransPyV.exe` 同目录查找 `repl.vpj` 模板工程(找不到则报错并提示 `--project`);与 `--asan` / `--phase2command` 互斥 |
|
|
||||||
| `--asan` | ASan 模式:先 `opt -passes=asan` 插桩;运行库须由 `project.vpj` 的 `linker.static_libs` 声明,否则 fail-fast |
|
|
||||||
| `--phase2command <bat>` | 不执行 `llc`/链接,只把命令写进指定 `.bat` |
|
|
||||||
| `--nowarn-register` | 抑制 Phase A 依赖类 WARN(跨模块 `text.ll` 未生成,下遍收敛) |
|
|
||||||
| `--no-llvmpeephole` | 关闭 FunctionPrint 的本地窥孔(T2 store/load 转发、T3 空 bitcast) |
|
|
||||||
| `--detection` | 开启 MemBuddy 分配检测(`0xCDCD` 标记 + 记录表探针,查越界写) |
|
|
||||||
| `--show-args` | 打印全部解析结果 |
|
|
||||||
| `--version`, `-v` | 打印 Viper / VRTE / TransPyV 版本、提交号、目标三元组与 LLVM 版本 |
|
|
||||||
| `--help`, `-h` | 打印帮助 |
|
|
||||||
|
|
||||||
### 架构
|
|
||||||
|
|
||||||
```text
|
|
||||||
source_dir 下全部 .vp/.py(多文件模式:递归扫描所有源文件)
|
|
||||||
│
|
|
||||||
├─ Phase 1(stub 分离)
|
|
||||||
│ IncludesScanner 按需翻译 includes → Phase1/*
|
|
||||||
│ Translator + Gen/* 生成 IR,再分别落盘:
|
|
||||||
│ <sha1>.stub.ll 声明
|
|
||||||
│ <sha1>.text.ll 实现
|
|
||||||
│
|
|
||||||
├─ Phase 2(组合 + 编译)
|
|
||||||
│ StubMerger* 按拓扑序合并 本地 stub + 依赖 stub + 本地 text → 完整 IR
|
|
||||||
│ BuildPipeline:完整 IR → llc → .obj → clang++(附加 includes 预编译 .obj)→ .exe
|
|
||||||
│
|
|
||||||
└─ <build_dir>/output/<linker.output>
|
|
||||||
```
|
|
||||||
|
|
||||||
**核心模块分工:**
|
|
||||||
|
|
||||||
| 模块 | 职责 |
|
|
||||||
| --- | --- |
|
|
||||||
| `App/main.vp` | CLI 入口:内存池与单例初始化、参数解析、Phase 调度、`--run` |
|
|
||||||
| `lib/Projectrans/Config.vp` | `project.vpj` 加载、路径解析、includes 缓存目录 |
|
|
||||||
| `lib/core/CompilerFactory.vp` | 持有 `mb`(主池)与 `perm_pool`(常驻池)两个 `MemBuddy` |
|
|
||||||
| `lib/core/Gen/Generator.vp` | 项目级 `Generator` 单例,Phase1/Phase2/BuildPipeline 全程复用 |
|
|
||||||
| `lib/core/Gen/Translator.vp` | 翻译器主体,`translate()` 与 `dump_ir()`(`.stub.ll` / `.text.ll` / 完整 IR) |
|
|
||||||
| `lib/core/Gen/GenApi.vp` | 翻译器对外接口与全局状态(如 temp 目录) |
|
|
||||||
| `lib/Phase/Phase1/` | 扫描 includes、拓扑排序、按需生成声明 |
|
|
||||||
| `lib/Phase/Phase2/` | 多文件工程:递归扫描源文件、翻译、IR 组合、调度编译 |
|
|
||||||
| `lib/Phase/BuildPipeline.vp` | `.ll` → `llc` → `.obj` → `clang++` → `.exe`(ASan 走插桩分支) |
|
|
||||||
| `lib/Stub/StubGen/` | 读取 `.vpi`,转换为 stub 声明 |
|
|
||||||
| `lib/Stub/StubMerger/` | `stub.ll` / `text.ll` 的收集、依赖拓扑、IR 拼接与落盘 |
|
|
||||||
| `lib/core/IncludesScanner.vp` | 扫描 `includes/`,决定哪些文件需要重编译 |
|
|
||||||
| `lib/core/VLogger.vp` | 日志系统(含 `--nowarn-register` 开关) |
|
|
||||||
|
|
||||||
> 自举相关注意点:自举后模块级全局变量会带 **SHA1 前缀**(模块隔离),因此像 `_g_gen`
|
|
||||||
> 这类单例必须**逐模块赋值**(`GenApi._g_gen = gen` …),不能只在 `main.vp` 里赋裸名 ——
|
|
||||||
> 否则部分模块读到自己的 `None`,表现为"全局名判定恒为 0"之类的误报。
|
|
||||||
|
|
||||||
### 内存模型
|
|
||||||
|
|
||||||
全程无系统 `malloc` 抖动,只有两个 `MemBuddy` 内存池(`lib/memhub` 的伙伴分配器):
|
|
||||||
|
|
||||||
- `POOL_SIZE = 1 GiB`(`1073741824`)—— 主池,`mb`,翻译过程的常规分配。
|
|
||||||
- `PERM_POOL_SIZE = 536871176`(≈512 MiB 可用)—— 常驻池,`perm_pool`,跨阶段存活的对象。
|
|
||||||
- 两者由 `CompilerFactory.factory` 单例持有;`mb` 另以 `sys._mbuddy` / `argparse._mbuddy` /
|
|
||||||
`ast._mbuddy` / `Config._mbuddy` … 的形式逐模块注入。
|
|
||||||
|
|
||||||
`--detection` 会把两个池都切到检测模式(`0xCDCD` 填充 + 记录表探针),用于排查越界写。
|
|
||||||
|
|
||||||
### 与 TransPyC 的关系
|
|
||||||
|
|
||||||
| 维度 | TransPyC | TransPyV |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 实现语言 | CPython | Viper |
|
|
||||||
| 定位 | 自举**种子**(只用来生成稳定的 TransPyV) | **现行编译器** |
|
|
||||||
| 自举状态 | — | ✅ 已完成,能编译自身 |
|
|
||||||
| 入口 | `TransPyC/Projectrans.py` | `App/main.vp` |
|
|
||||||
| AST 解析 | Python `ast` / 自研 vast | `includes/ast` 自研(9 文件) |
|
|
||||||
| 工程配置 | `project.json` | `project.vpj` |
|
|
||||||
| 平台 | Windows / Linux | Windows(`w32.*`) |
|
|
||||||
|
|
||||||
### 进一步阅读
|
|
||||||
|
|
||||||
- [TransPyC 编译器仓库(种子)](https://git.gvsds.com/GVSDS/TransPyC)
|
|
||||||
- [Viper 语言文档(wiki 目录)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
|
|
||||||
- [自举可行性分析与进展](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
|
|
||||||
- [工程配置 `project.vpj` 说明](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
|
|
||||||
- [ViperOS 操作系统(用 Viper 写的 OS)](https://git.gvsds.com/GVSDS/ViperOS)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -227,18 +14,18 @@ source_dir 下全部 .vp/.py(多文件模式:递归扫描所有源文件)
|
|||||||
|
|
||||||
### What is this
|
### What is this
|
||||||
|
|
||||||
`TransPyV` is the second-generation Viper compiler: **written in Viper and compiled by Viper**. No Python runtime takes part in the compilation path — lexing/parsing goes through `includes/ast` (a self-implemented parser, 9 files), IR generation through `App/lib/core/Gen/`, and machine code through `llc` + `clang++`.
|
`ViperC` is the second-generation Viper compiler: **written in Viper and compiled by Viper**. No Python runtime takes part in the compilation path — lexing/parsing goes through `includes/ast` (a self-implemented parser, 9 files), IR generation through `App/lib/core/Gen/`, and machine code through `llc` + `clang++`.
|
||||||
|
|
||||||
The bootstrap chain:
|
The bootstrap chain:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
TransPyC (seed, implemented in CPython)
|
TransPyC (seed, implemented in CPython)
|
||||||
│ one-time only: compile TransPyV sources into TransPyV.exe
|
│ one-time only: compile ViperC sources into viperc.exe
|
||||||
▼
|
▼
|
||||||
TransPyV.exe (stage-one artifact)
|
viperc.exe (stage-one artifact)
|
||||||
│ bootstrap: this exe compiles TransPyV itself
|
│ bootstrap: this exe compiles ViperC itself
|
||||||
▼
|
▼
|
||||||
TransPyV.exe (self-hosted artifact — the one shipped in exe\)
|
viperc.exe (self-hosted artifact — the one shipped in exe\)
|
||||||
│
|
│
|
||||||
└─ used to build any Viper project (ViperApps\*, Test\* …)
|
└─ used to build any Viper project (ViperApps\*, Test\* …)
|
||||||
```
|
```
|
||||||
@@ -247,18 +34,18 @@ Current state and size (measured on the dev machine):
|
|||||||
|
|
||||||
| Item | Value |
|
| Item | Value |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Compiler sources | **76 `.vp` files, 64,656 lines** under `App/` |
|
| Compiler sources | **82 `.vp` files, 63,783 lines** under `App/` |
|
||||||
| Translator core | `App/lib/core/Gen/` — **52 files, 49,978 lines** |
|
| Translator core | `App/lib/core/Gen/` — **54 files, 47,297 lines** |
|
||||||
| Phases & merging | `App/lib/Phase/` 6 files 5,309 lines; `App/lib/Stub/` 9 files 6,877 lines |
|
| Phases & merging | `App/lib/Phase/` 6 files 5,339 lines; `App/lib/Stub/` 9 files 6,207 lines |
|
||||||
| Project & utils | `App/lib/Projectrans/` 3 files 1,008 lines |
|
| Project & utils | `App/lib/Projectrans/` 3 files 712 lines |
|
||||||
| Self-tests | **39 `.vp`** under `Test/` (`App/` 36 + `NegativeTest/` 2 + `Sha1Test/` 1) |
|
| Self-tests | workspace root `Test\` (**separate repo**): **97 projects, 129 `.vp`**, driven by `Test\SequentialRegressionTest.py` |
|
||||||
| Platform | Windows x64 (sources `import w32.*` directly) |
|
| Platform | Windows x64 (sources `import win32.*` directly) |
|
||||||
|
|
||||||
### Directory layout
|
### Directory layout
|
||||||
|
|
||||||
```text
|
```text
|
||||||
TransPyV/
|
ViperC/
|
||||||
├── project.vpj # project config (source_dir=./App, build_dir=./.tpv_build, output TransPyV.exe)
|
├── project.vpj # project config (source_dir=./App, build_dir=./__vpcbuild__, output viperc.exe)
|
||||||
├── App/ # compiler sources (Viper)
|
├── App/ # compiler sources (Viper)
|
||||||
│ ├── main.vp # CLI entry: pools, singleton injection, arg parsing, phase dispatch
|
│ ├── main.vp # CLI entry: pools, singleton injection, arg parsing, phase dispatch
|
||||||
│ └── lib/
|
│ └── lib/
|
||||||
@@ -286,18 +73,17 @@ TransPyV/
|
|||||||
│ └── Stub/
|
│ └── Stub/
|
||||||
│ ├── StubGen/ # .vpi reader + declaration converter
|
│ ├── StubGen/ # .vpi reader + declaration converter
|
||||||
│ └── StubMerger/ # dependency-ordered merge of .stub.ll / .text.ll
|
│ └── StubMerger/ # dependency-ordered merge of .stub.ll / .text.ll
|
||||||
└── Test/ # compiler self-tests (Viper)
|
|
||||||
├── project.vpj
|
|
||||||
├── App/ # 36 .vp (mostly *_test.vp); entry point test_main.vp aggregates them all
|
|
||||||
├── NegativeTest/ # negative tests (must fail to compile)
|
|
||||||
└── Sha1Test/ # SHA1 namespace test
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> The self-test/regression suite is not in this repo: it lives at the workspace root `Test\`
|
||||||
|
> (a separate repo, 97 projects, 129 `.vp`), driven by `Test\SequentialRegressionTest.py`
|
||||||
|
> (`--exe exe\viperc.exe`).
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
| Tool | Notes |
|
| Tool | Notes |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Windows 10/11 x64 | sources `import w32.*`; Windows-only for now |
|
| Windows 10/11 x64 | sources `import win32.*`; Windows-only for now |
|
||||||
| `llc` | LLVM static compiler (22.1.8 here), set by `compiler.cmd` in `project.vpj` |
|
| `llc` | LLVM static compiler (22.1.8 here), set by `compiler.cmd` in `project.vpj` |
|
||||||
| `clang++` | mingw-w64 toolchain, `-fuse-ld=lld` (here `D:/msys64/mingw64/bin/clang++.exe`) |
|
| `clang++` | mingw-w64 toolchain, `-fuse-ld=lld` (here `D:/msys64/mingw64/bin/clang++.exe`) |
|
||||||
| ≥ 1.5 GiB free RAM | allocates a 1 GiB main pool + 512 MiB permanent pool at startup |
|
| ≥ 1.5 GiB free RAM | allocates a 1 GiB main pool + 512 MiB permanent pool at startup |
|
||||||
@@ -308,7 +94,9 @@ Required layout (`project.vpj` refers to `../includes`):
|
|||||||
```text
|
```text
|
||||||
TransPyC\ # workspace root
|
TransPyC\ # workspace root
|
||||||
├── TransPyC\ # first-generation compiler (seed)
|
├── TransPyC\ # first-generation compiler (seed)
|
||||||
├── TransPyV\ # this repo
|
├── ViperC\ # this repo
|
||||||
|
├── Test\ # regression suite (separate repo)
|
||||||
|
├── exe\ # shipped self-hosted artifact (viperc.exe)
|
||||||
└── includes\ # Viper standard library + includes/ast parser
|
└── includes\ # Viper standard library + includes/ast parser
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -316,23 +104,23 @@ TransPyC\ # workspace root
|
|||||||
|
|
||||||
```bat
|
```bat
|
||||||
:: everyday use: build any project with the self-hosted compiler
|
:: everyday use: build any project with the self-hosted compiler
|
||||||
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean
|
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean
|
||||||
|
|
||||||
:: build and run immediately
|
:: build and run immediately
|
||||||
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
|
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
|
||||||
|
|
||||||
:: only when rebuilding the compiler itself (bootstrap seed), use TransPyC
|
:: only when rebuilding the compiler itself (bootstrap seed), use TransPyC
|
||||||
python TransPyC\Projectrans.py --project TransPyV\project.vpj --clean
|
python TransPyC\Projectrans.py --project ViperC\project.vpj --clean
|
||||||
```
|
```
|
||||||
|
|
||||||
Artifacts land in `<project dir>\<build_dir>\output\<linker.output>` — by default
|
Artifacts land in `<project dir>\<build_dir>\output\<linker.output>` — by default
|
||||||
`<project>\.tpv_build\output\app.exe`. TransPyV itself sets `linker.output` to `TransPyV.exe`,
|
`<project>\__vpcbuild__\output\app.exe`. ViperC itself sets `linker.output` to `viperc.exe`,
|
||||||
so its own product is `TransPyV\.tpv_build\output\TransPyV.exe`.
|
so its own product is `ViperC\__vpcbuild__\output\viperc.exe`.
|
||||||
|
|
||||||
Run the compiler self-test suite:
|
Run the regression suite (workspace root `Test\`, a separate repo):
|
||||||
|
|
||||||
```bat
|
```bat
|
||||||
exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
|
python Test\SequentialRegressionTest.py --exe exe\viperc.exe
|
||||||
```
|
```
|
||||||
|
|
||||||
### CLI flags
|
### CLI flags
|
||||||
@@ -350,14 +138,14 @@ exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
|
|||||||
| `--rebuild-includes` | delete only the includes cache (`includes.binary`), forcing a full includes rebuild |
|
| `--rebuild-includes` | delete only the includes cache (`includes.binary`), forcing a full includes rebuild |
|
||||||
| `--clear-cache` | clear only temp (`.vpi` / `.stub.ll` / `.text.ll`), forcing stub regeneration |
|
| `--clear-cache` | clear only temp (`.vpi` / `.stub.ll` / `.text.ll`), forcing stub regeneration |
|
||||||
| `--run` | execute the produced binary on success |
|
| `--run` | execute the produced binary on success |
|
||||||
| `--repl` | interactive REPL (M1: expression fragment → DLL → call and echo); without `--project` it looks for the `repl.vpj` template project in cwd, then next to `TransPyV.exe` (errors out with a `--project` hint if neither exists); mutually exclusive with `--asan` / `--phase2command` |
|
| `--repl` | interactive REPL (M1: expression fragment → DLL → call and echo); without `--project` it looks for the `repl.vpj` template project in cwd, then next to `viperc.exe` (errors out with a `--project` hint if neither exists); mutually exclusive with `--asan` / `--phase2command` |
|
||||||
| `--asan` | ASan mode: `opt -passes=asan` first; runtimes must be declared in `project.vpj` `linker.static_libs`, else fail-fast |
|
| `--asan` | ASan mode: `opt -passes=asan` first; runtimes must be declared in `project.vpj` `linker.static_libs`, else fail-fast |
|
||||||
| `--phase2command <bat>` | do not run `llc`/link; write the commands into the given `.bat` |
|
| `--phase2command <bat>` | do not run `llc`/link; write the commands into the given `.bat` |
|
||||||
| `--nowarn-register` | suppress Phase A dependency WARNs (cross-module `text.ll` not yet emitted, converges next pass) |
|
| `--nowarn-register` | suppress Phase A dependency WARNs (cross-module `text.ll` not yet emitted, converges next pass) |
|
||||||
| `--no-llvmpeephole` | disable FunctionPrint local peepholes (T2 store/load forwarding, T3 no-op bitcast) |
|
| `--no-llvmpeephole` | disable FunctionPrint local peepholes (T2 store/load forwarding, T3 no-op bitcast) |
|
||||||
| `--detection` | enable MemBuddy allocation detection (`0xCDCD` fill + record-table probe for out-of-bounds writes) |
|
| `--detection` | enable MemBuddy allocation detection (`0xCDCD` fill + record-table probe for out-of-bounds writes) |
|
||||||
| `--show-args` | dump all parsed arguments |
|
| `--show-args` | dump all parsed arguments |
|
||||||
| `--version`, `-v` | print Viper / VRTE / TransPyV versions, commit ids, target triple and LLVM version |
|
| `--version`, `-v` | print Viper / VRTE / ViperC versions, commit ids, target triple and LLVM version |
|
||||||
| `--help`, `-h` | print help |
|
| `--help`, `-h` | print help |
|
||||||
|
|
||||||
### Architecture
|
### Architecture
|
||||||
@@ -415,20 +203,20 @@ out-of-bounds writes.
|
|||||||
|
|
||||||
### Relation to TransPyC
|
### Relation to TransPyC
|
||||||
|
|
||||||
| Dimension | TransPyC | TransPyV |
|
| Dimension | TransPyC | ViperC |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Implementation | CPython | Viper |
|
| Implementation | CPython | Viper |
|
||||||
| Role | bootstrap **seed** (only used to produce a stable TransPyV) | **the current compiler** |
|
| Role | bootstrap **seed** (only used to produce a stable ViperC) | **the current compiler** |
|
||||||
| Self-hosted? | — | ✅ yes, it compiles itself |
|
| Self-hosted? | — | ✅ yes, it compiles itself |
|
||||||
| Entry | `TransPyC/Projectrans.py` | `App/main.vp` |
|
| Entry | `TransPyC/Projectrans.py` | `App/main.vp` |
|
||||||
| AST parsing | Python `ast` / in-house vast | `includes/ast` (9 files, in-house) |
|
| AST parsing | Python `ast` / in-house vast | `includes/ast` (9 files, in-house) |
|
||||||
| Project config | `project.json` | `project.vpj` |
|
| Project config | `project.json` | `project.vpj` |
|
||||||
| Platform | Windows / Linux | Windows (`w32.*`) |
|
| Platform | Windows / Linux | Windows (`win32.*`) |
|
||||||
|
|
||||||
### Further reading
|
### Further reading
|
||||||
|
|
||||||
- [TransPyC compiler repo (the seed)](https://git.gvsds.com/GVSDS/TransPyC)
|
- [TransPyC compiler repo (the seed)](https://git.gvsds.com/GVSDS/TransPyC)
|
||||||
- [Viper language docs (wiki)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
|
- [Viper language docs (wiki)](https://git.gvsds.com/GVSDS/TransPyC/tree/main/wiki)
|
||||||
- [Bootstrapping analysis and progress](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
|
- [Bootstrapping analysis and progress](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)
|
||||||
- [Project config `project.vpj`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-project.md)
|
- [Compilation system (`project.vpj` / `viperbuild` metaprogramming)](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-compilation-system.md)
|
||||||
- [ViperOS (an OS written in Viper)](https://git.gvsds.com/GVSDS/ViperOS)
|
- [ViperOS (an OS written in Viper)](https://git.gvsds.com/GVSDS/ViperOS)
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# c.Asm 内联汇编测试
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
def asm_test() -> int:
|
|
||||||
# 1. 简单无操作数汇编(nop + clobber)
|
|
||||||
c.Asm("nop", op=[t.ASM_DESCR.CLOBBER_MEMORY])
|
|
||||||
stdio.printf("asm: nop ok\n")
|
|
||||||
|
|
||||||
# 2. 仅汇编文本无 clobber
|
|
||||||
c.Asm("nop")
|
|
||||||
stdio.printf("asm: nop2 ok\n")
|
|
||||||
|
|
||||||
# 3. 多 clobber
|
|
||||||
c.Asm("nop", op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_CC])
|
|
||||||
stdio.printf("asm: multi-clobber ok\n")
|
|
||||||
|
|
||||||
# 4. 带输入操作数的汇编(读取 CPU ID)
|
|
||||||
# 使用 f-string 内联 c.AsmInp
|
|
||||||
# mov eax, 输入值; nop(保持简单,不使用会产生异常的指令)
|
|
||||||
val: int = 42
|
|
||||||
c.Asm(f"""mov eax, {c.AsmInp(val, t.ASM_DESCR.REG_ANY)}
|
|
||||||
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
|
|
||||||
stdio.printf("asm: input ok val=%d\n", val)
|
|
||||||
|
|
||||||
# 5. 带输出操作数的汇编
|
|
||||||
# c.AsmOut 获取结果
|
|
||||||
result: int = 0
|
|
||||||
c.Asm(f"""mov {c.AsmOut(result, t.ASM_DESCR.OUTPUT_REG)}, 123
|
|
||||||
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
|
|
||||||
stdio.printf("asm: output result=%d\n", result)
|
|
||||||
|
|
||||||
# 6. 带输入和输出操作数
|
|
||||||
# result2 = val + 1
|
|
||||||
result2: int = 0
|
|
||||||
input_val: int = 10
|
|
||||||
c.Asm(f"""mov eax, {c.AsmInp(input_val, t.ASM_DESCR.REG_ANY)}
|
|
||||||
add eax, 1
|
|
||||||
mov {c.AsmOut(result2, t.ASM_DESCR.OUTPUT_REG)}, eax
|
|
||||||
nop""", op=[t.ASM_DESCR.CLOBBER_RAX])
|
|
||||||
stdio.printf("asm: in+out result2=%d (expect 11)\n", result2)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
|
||||||
import stdio
|
|
||||||
import coro
|
|
||||||
|
|
||||||
|
|
||||||
# TPV-M3~M7 协程综合测试:
|
|
||||||
# M3: For 内 await(range→While)、coro_sleep 挂起、coro_destroy 帧回池
|
|
||||||
# M4: raise 存码、await 子帧异常传播、coro_exception 查询
|
|
||||||
# M5/M6: try/except(链式匹配 + as e 绑定异常码 + 类型码)
|
|
||||||
# M7: try/finally、异常消息 msg 槽、自定义异常类、帧池复用
|
|
||||||
class MyError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
async def leaf(x: int) -> int:
|
|
||||||
return x * 2
|
|
||||||
|
|
||||||
|
|
||||||
async def task(n: int) -> int:
|
|
||||||
s: int = 0
|
|
||||||
for i in range(3): # M3:For 内 await
|
|
||||||
s = s + await leaf(i)
|
|
||||||
if s > 4:
|
|
||||||
await coro.coro_sleep(5) # M3:coro_sleep 挂起(wake 槽 + 轮询)
|
|
||||||
try:
|
|
||||||
raise ValueError("boom") # M4/M6:raise 类型 + M7 消息
|
|
||||||
except ValueError as e: # M5/M6:as e 绑定异常码
|
|
||||||
s = s + e
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
async def raiser() -> int:
|
|
||||||
try:
|
|
||||||
raise MyError("fin_msg") # M7:自定义异常类 + 消息
|
|
||||||
finally:
|
|
||||||
x: int = 99 # M7:finally 正常路径执行
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
tt: t.CPtr = task(3)
|
|
||||||
r: int = coro.coro_run(tt)
|
|
||||||
e: int = coro.coro_exception(tt)
|
|
||||||
stdio.printf("coro result = %d exc = %d\n", r, e)
|
|
||||||
coro.coro_destroy(tt) # M3/M7:destroy 回池
|
|
||||||
t1: t.CPtr = raiser()
|
|
||||||
r1: int = coro.coro_run(t1)
|
|
||||||
e1: int = coro.coro_exception(t1)
|
|
||||||
m1: t.CPtr = coro.coro_exception_msg(t1)
|
|
||||||
stdio.printf("coro result = %d exc = %d msg = %s\n", r1, e1, m1)
|
|
||||||
coro.coro_destroy(t1)
|
|
||||||
t2: t.CPtr = raiser() # 帧池复用
|
|
||||||
r2: int = coro.coro_run(t2)
|
|
||||||
e2: int = coro.coro_exception(t2)
|
|
||||||
stdio.printf("coro result = %d exc = %d\n", r2, e2)
|
|
||||||
coro.coro_destroy(t2)
|
|
||||||
return 0
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
import t, c
|
|
||||||
from stdint import *
|
|
||||||
import stdio
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# c.Attribute 装饰器测试
|
|
||||||
#
|
|
||||||
# 测试 @c.Attribute(...) 对函数属性的设置:
|
|
||||||
# - t.attr.always_inline() -> alwaysinline
|
|
||||||
# - t.attr.noinline() -> noinline
|
|
||||||
# - t.attr.noreturn() -> noreturn
|
|
||||||
# - t.attr.pure() -> readonly (LLVM 函数属性)
|
|
||||||
# - t.attr.llvm.nounwind -> nounwind
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
# Test 1: always_inline 属性
|
|
||||||
@c.Attribute(t.attr.always_inline())
|
|
||||||
def AlwaysInlineFunc(x: int) -> int:
|
|
||||||
return x * 2
|
|
||||||
|
|
||||||
|
|
||||||
# Test 2: noinline 属性
|
|
||||||
@c.Attribute(t.attr.noinline())
|
|
||||||
def NoInlineFunc(x: int) -> int:
|
|
||||||
return x + 100
|
|
||||||
|
|
||||||
|
|
||||||
# Test 3: noreturn 属性(函数确实不返回)
|
|
||||||
@c.Attribute(t.attr.noreturn())
|
|
||||||
def NoReturnFunc() -> int:
|
|
||||||
stdio.printf("NoReturnFunc called (does not return)\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
# Test 4: 普通函数对照(无装饰器)
|
|
||||||
def NormalFunc(x: int) -> int:
|
|
||||||
return x * 3
|
|
||||||
|
|
||||||
|
|
||||||
# Test 5: 多属性组合
|
|
||||||
@c.Attribute(t.attr.always_inline(), t.attr.pure())
|
|
||||||
def MultiAttrFunc(x: int) -> int:
|
|
||||||
return x + 1
|
|
||||||
|
|
||||||
|
|
||||||
# Test 6: 无括号属性引用 t.attr.packed
|
|
||||||
@c.Attribute(t.attr.packed)
|
|
||||||
def PackedAttrFunc(x: int) -> int:
|
|
||||||
return x - 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_always_inline() -> int:
|
|
||||||
stdio.printf("--- Test 1: c.Attribute(always_inline) ---\n")
|
|
||||||
r: int = AlwaysInlineFunc(21)
|
|
||||||
stdio.printf("AlwaysInlineFunc(21)=%d (expect 42)\n", r)
|
|
||||||
if r == 42:
|
|
||||||
stdio.printf("AlwaysInlineFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("AlwaysInlineFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_noinline() -> int:
|
|
||||||
stdio.printf("--- Test 2: c.Attribute(noinline) ---\n")
|
|
||||||
r: int = NoInlineFunc(5)
|
|
||||||
stdio.printf("NoInlineFunc(5)=%d (expect 105)\n", r)
|
|
||||||
if r == 105:
|
|
||||||
stdio.printf("NoInlineFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("NoInlineFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_noreturn() -> int:
|
|
||||||
stdio.printf("--- Test 3: c.Attribute(noreturn) ---\n")
|
|
||||||
stdio.printf("NoReturnFunc declared with noreturn attr\n")
|
|
||||||
stdio.printf("NoReturnFunc OK (not called to avoid UB)\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_normal() -> int:
|
|
||||||
stdio.printf("--- Test 4: normal function (no attr) ---\n")
|
|
||||||
r: int = NormalFunc(7)
|
|
||||||
stdio.printf("NormalFunc(7)=%d (expect 21)\n", r)
|
|
||||||
if r == 21:
|
|
||||||
stdio.printf("NormalFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("NormalFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_multi_attr() -> int:
|
|
||||||
stdio.printf("--- Test 5: c.Attribute(always_inline, pure) ---\n")
|
|
||||||
r: int = MultiAttrFunc(10)
|
|
||||||
stdio.printf("MultiAttrFunc(10)=%d (expect 11)\n", r)
|
|
||||||
if r == 11:
|
|
||||||
stdio.printf("MultiAttrFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("MultiAttrFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_packed_attr() -> int:
|
|
||||||
stdio.printf("--- Test 6: c.Attribute(packed) ---\n")
|
|
||||||
r: int = PackedAttrFunc(10)
|
|
||||||
stdio.printf("PackedAttrFunc(10)=%d (expect 9)\n", r)
|
|
||||||
if r == 9:
|
|
||||||
stdio.printf("PackedAttrFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("PackedAttrFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
# Test 7: t.attr.llvm.nounwind 属性
|
|
||||||
@c.Attribute(t.attr.llvm.nounwind)
|
|
||||||
def NoUnwindFunc(x: int) -> int:
|
|
||||||
return x + 1
|
|
||||||
|
|
||||||
|
|
||||||
# Test 8: t.attr.llvm.noredzone 属性
|
|
||||||
@c.Attribute(t.attr.llvm.noredzone)
|
|
||||||
def NoRedZoneFunc(x: int) -> int:
|
|
||||||
return x + 2
|
|
||||||
|
|
||||||
|
|
||||||
# Test 9: t.attr.llvm.willreturn 属性
|
|
||||||
@c.Attribute(t.attr.llvm.willreturn)
|
|
||||||
def WillReturnFunc(x: int) -> int:
|
|
||||||
return x + 3
|
|
||||||
|
|
||||||
|
|
||||||
# Test 10: t.attr.llvm.mustprogress 属性
|
|
||||||
@c.Attribute(t.attr.llvm.mustprogress)
|
|
||||||
def MustProgressFunc(x: int) -> int:
|
|
||||||
return x + 4
|
|
||||||
|
|
||||||
|
|
||||||
# Test 11: t.attr.const() -> readnone
|
|
||||||
@c.Attribute(t.attr.const())
|
|
||||||
def ConstFunc(x: int) -> int:
|
|
||||||
return x * 0 + 42
|
|
||||||
|
|
||||||
|
|
||||||
# Test 12: 多属性组合(alwaysinline + nounwind + noredzone)
|
|
||||||
@c.Attribute(t.attr.always_inline(), t.attr.llvm.nounwind, t.attr.llvm.noredzone)
|
|
||||||
def TripleAttrFunc(x: int) -> int:
|
|
||||||
return x + 5
|
|
||||||
|
|
||||||
|
|
||||||
def test_nounwind() -> int:
|
|
||||||
stdio.printf("--- Test 7: c.Attribute(llvm.nounwind) ---\n")
|
|
||||||
r: int = NoUnwindFunc(10)
|
|
||||||
stdio.printf("NoUnwindFunc(10)=%d (expect 11)\n", r)
|
|
||||||
if r == 11:
|
|
||||||
stdio.printf("NoUnwindFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("NoUnwindFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_noredzone() -> int:
|
|
||||||
stdio.printf("--- Test 8: c.Attribute(llvm.noredzone) ---\n")
|
|
||||||
r: int = NoRedZoneFunc(10)
|
|
||||||
stdio.printf("NoRedZoneFunc(10)=%d (expect 12)\n", r)
|
|
||||||
if r == 12:
|
|
||||||
stdio.printf("NoRedZoneFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("NoRedZoneFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_willreturn() -> int:
|
|
||||||
stdio.printf("--- Test 9: c.Attribute(llvm.willreturn) ---\n")
|
|
||||||
r: int = WillReturnFunc(10)
|
|
||||||
stdio.printf("WillReturnFunc(10)=%d (expect 13)\n", r)
|
|
||||||
if r == 13:
|
|
||||||
stdio.printf("WillReturnFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("WillReturnFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_mustprogress() -> int:
|
|
||||||
stdio.printf("--- Test 10: c.Attribute(llvm.mustprogress) ---\n")
|
|
||||||
r: int = MustProgressFunc(10)
|
|
||||||
stdio.printf("MustProgressFunc(10)=%d (expect 14)\n", r)
|
|
||||||
if r == 14:
|
|
||||||
stdio.printf("MustProgressFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("MustProgressFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_const_attr() -> int:
|
|
||||||
stdio.printf("--- Test 11: c.Attribute(const) ---\n")
|
|
||||||
r: int = ConstFunc(99)
|
|
||||||
stdio.printf("ConstFunc(99)=%d (expect 42)\n", r)
|
|
||||||
if r == 42:
|
|
||||||
stdio.printf("ConstFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("ConstFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_triple_attr() -> int:
|
|
||||||
stdio.printf("--- Test 12: c.Attribute(always_inline, nounwind, noredzone) ---\n")
|
|
||||||
r: int = TripleAttrFunc(10)
|
|
||||||
stdio.printf("TripleAttrFunc(10)=%d (expect 15)\n", r)
|
|
||||||
if r == 15:
|
|
||||||
stdio.printf("TripleAttrFunc OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("TripleAttrFunc FAIL\n")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def attr_test() -> int:
|
|
||||||
stdio.printf("=== attr_test: c.Attribute 装饰器测试 ===\n\n")
|
|
||||||
test_always_inline()
|
|
||||||
test_noinline()
|
|
||||||
test_noreturn()
|
|
||||||
test_normal()
|
|
||||||
test_multi_attr()
|
|
||||||
test_packed_attr()
|
|
||||||
test_nounwind()
|
|
||||||
test_noredzone()
|
|
||||||
test_willreturn()
|
|
||||||
test_mustprogress()
|
|
||||||
test_const_attr()
|
|
||||||
test_triple_attr()
|
|
||||||
stdio.printf("\n=== attr_test 完成 ===\n")
|
|
||||||
return 0
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# AugAssign 测试:+= -= *= /= %= &= |= ^= <<= >>=
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
g_count: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_local() -> int:
|
|
||||||
x: int = 10
|
|
||||||
x += 5
|
|
||||||
stdio.printf("local +=: %d\n", x)
|
|
||||||
x -= 3
|
|
||||||
stdio.printf("local -=: %d\n", x)
|
|
||||||
x *= 2
|
|
||||||
stdio.printf("local *=: %d\n", x)
|
|
||||||
x /= 4
|
|
||||||
stdio.printf("local /=: %d\n", x)
|
|
||||||
x %= 7
|
|
||||||
stdio.printf("local %%=: %d\n", x)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_global_aug() -> int:
|
|
||||||
global g_count
|
|
||||||
g_count += 1
|
|
||||||
stdio.printf("global +=: %d\n", g_count)
|
|
||||||
g_count += 10
|
|
||||||
stdio.printf("global +=: %d\n", g_count)
|
|
||||||
g_count -= 3
|
|
||||||
stdio.printf("global -=: %d\n", g_count)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def test_bitops() -> int:
|
|
||||||
b: int = 0xFF
|
|
||||||
b &= 0x0F
|
|
||||||
stdio.printf("bit &=: %d\n", b)
|
|
||||||
b |= 0x30
|
|
||||||
stdio.printf("bit |=: %d\n", b)
|
|
||||||
b ^= 0xFF
|
|
||||||
stdio.printf("bit ^=: %d\n", b)
|
|
||||||
b = 1
|
|
||||||
b <<= 4
|
|
||||||
stdio.printf("bit <<=: %d\n", b)
|
|
||||||
b >>= 2
|
|
||||||
stdio.printf("bit >>=: %d\n", b)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def augassign_test() -> int:
|
|
||||||
test_local()
|
|
||||||
test_global_aug()
|
|
||||||
test_bitops()
|
|
||||||
return 0
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import t, c
|
|
||||||
import stdlib
|
|
||||||
import circ_b
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 循环引用测试:模块 A
|
|
||||||
#
|
|
||||||
# circ_a imports circ_b, circ_b imports circ_a — 循环引用
|
|
||||||
# ClassA.MakeB() 返回 circ_b.ClassB 实例(跨模块构造 + 类型注解)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
class ClassA:
|
|
||||||
x: int
|
|
||||||
|
|
||||||
def __new__(self, val: int):
|
|
||||||
r: ClassA | t.CPtr = stdlib.malloc(ClassA.__sizeof__())
|
|
||||||
return r
|
|
||||||
|
|
||||||
def __init__(self, val: int):
|
|
||||||
self.x = val
|
|
||||||
|
|
||||||
def GetValue(self) -> int:
|
|
||||||
return self.x
|
|
||||||
|
|
||||||
def MakeB(self) -> circ_b.ClassB | t.CPtr:
|
|
||||||
b: circ_b.ClassB | t.CPtr = circ_b.ClassB(self.x)
|
|
||||||
return b
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import t, c
|
|
||||||
import stdlib
|
|
||||||
import circ_a
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 循环引用测试:模块 B
|
|
||||||
#
|
|
||||||
# circ_b imports circ_a, circ_a imports circ_b — 循环引用
|
|
||||||
# ClassB.MakeA() 返回 circ_a.ClassA 实例(跨模块构造 + 类型注解)
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
class ClassB:
|
|
||||||
y: int
|
|
||||||
|
|
||||||
def __new__(self, val: int):
|
|
||||||
r: ClassB | t.CPtr = stdlib.malloc(ClassB.__sizeof__())
|
|
||||||
return r
|
|
||||||
|
|
||||||
def __init__(self, val: int):
|
|
||||||
self.y = val
|
|
||||||
|
|
||||||
def GetValue(self) -> int:
|
|
||||||
return self.y
|
|
||||||
|
|
||||||
def MakeA(self) -> circ_a.ClassA | t.CPtr:
|
|
||||||
a: circ_a.ClassA | t.CPtr = circ_a.ClassA(self.y)
|
|
||||||
return a
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import t, c
|
|
||||||
import stdio
|
|
||||||
import circ_a
|
|
||||||
import circ_b
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 循环引用测试入口
|
|
||||||
#
|
|
||||||
# 验证 A → B → A 的循环引用能正确编译和运行:
|
|
||||||
# - Test 1: ClassA.MakeB() 创建 ClassB 实例
|
|
||||||
# - Test 2: ClassB.MakeA() 创建 ClassA 实例
|
|
||||||
# - Test 3: 值传递链 A(20) → B → A
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
def circ_test() -> int:
|
|
||||||
stdio.printf("circ: === Test Start ===\n")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Test 1: 创建 ClassA,调用 MakeB 获取 ClassB 实例
|
|
||||||
# ============================================================
|
|
||||||
stdio.printf("circ: === Test 1: A.MakeB() ===\n")
|
|
||||||
|
|
||||||
a: circ_a.ClassA | t.CPtr = circ_a.ClassA(10)
|
|
||||||
av: int = a.GetValue()
|
|
||||||
stdio.printf("circ: a.GetValue()=%d (expected 10)\n", av)
|
|
||||||
if av != 10:
|
|
||||||
stdio.printf("[FAIL] a.GetValue()=%d expected 10\n", av)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
b: circ_b.ClassB | t.CPtr = a.MakeB()
|
|
||||||
bv: int = b.GetValue()
|
|
||||||
stdio.printf("circ: b.GetValue()=%d (expected 10)\n", bv)
|
|
||||||
if bv != 10:
|
|
||||||
stdio.printf("[FAIL] b.GetValue()=%d expected 10\n", bv)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Test 2: 从 B 调用 MakeA 获取 ClassA 实例
|
|
||||||
# ============================================================
|
|
||||||
stdio.printf("circ: === Test 2: B.MakeA() ===\n")
|
|
||||||
|
|
||||||
a2: circ_a.ClassA | t.CPtr = b.MakeA()
|
|
||||||
av2: int = a2.GetValue()
|
|
||||||
stdio.printf("circ: a2.GetValue()=%d (expected 10)\n", av2)
|
|
||||||
if av2 != 10:
|
|
||||||
stdio.printf("[FAIL] a2.GetValue()=%d expected 10\n", av2)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Test 3: 值传递链 A(20) → B → A
|
|
||||||
# ============================================================
|
|
||||||
stdio.printf("circ: === Test 3: value chain A(20)->B->A ===\n")
|
|
||||||
|
|
||||||
a3: circ_a.ClassA | t.CPtr = circ_a.ClassA(20)
|
|
||||||
b3: circ_b.ClassB | t.CPtr = a3.MakeB()
|
|
||||||
a4: circ_a.ClassA | t.CPtr = b3.MakeA()
|
|
||||||
final: int = a4.GetValue()
|
|
||||||
stdio.printf("circ: final=%d (expected 20)\n", final)
|
|
||||||
if final != 20:
|
|
||||||
stdio.printf("[FAIL] final=%d expected 20\n", final)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
stdio.printf("circ: === All Tests Passed ===\n")
|
|
||||||
return 0
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 闭包 + nonlocal/global 测试
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# 全局变量
|
|
||||||
g_count: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
# global 关键字测试:修改全局变量
|
|
||||||
def test_global() -> int:
|
|
||||||
global g_count
|
|
||||||
g_count = g_count + 1
|
|
||||||
return g_count
|
|
||||||
|
|
||||||
|
|
||||||
# nonlocal 关键字测试:嵌套函数修改外层局部变量
|
|
||||||
def make_counter() -> t.CPtr:
|
|
||||||
count: int = 0
|
|
||||||
|
|
||||||
# 内部函数(闭包)捕获 count
|
|
||||||
def counter() -> int:
|
|
||||||
nonlocal count
|
|
||||||
count = count + 1
|
|
||||||
return count
|
|
||||||
|
|
||||||
return counter
|
|
||||||
|
|
||||||
|
|
||||||
def closure_test() -> int:
|
|
||||||
# global 测试
|
|
||||||
stdio.printf("global: %d\n", test_global())
|
|
||||||
stdio.printf("global: %d\n", test_global())
|
|
||||||
stdio.printf("global: %d\n", test_global())
|
|
||||||
|
|
||||||
# nonlocal/闭包测试
|
|
||||||
f: t.CPtr = make_counter()
|
|
||||||
stdio.printf("closure: %d\n", f())
|
|
||||||
stdio.printf("closure: %d\n", f())
|
|
||||||
stdio.printf("closure: %d\n", f())
|
|
||||||
|
|
||||||
return 0
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# deco_test - t/c 装饰器测试
|
|
||||||
#
|
|
||||||
# 测试 t.CDefine / t.CExport / t.CInline / t.CExtern / t.State
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
# Test 1: t.CDefine - 常量定义
|
|
||||||
MAX_VALUE: t.CDefine = 256
|
|
||||||
PI_APPROX: t.CDefine = 314
|
|
||||||
# Test 10: t.CDefine 作为 t.CArray 的 count
|
|
||||||
ARR_SIZE: t.CDefine = 8
|
|
||||||
|
|
||||||
|
|
||||||
# Test 2: t.CExport - 导出函数标记
|
|
||||||
def ExportedAdd(a: int, b: int) -> int | t.CExport:
|
|
||||||
return a + b
|
|
||||||
|
|
||||||
|
|
||||||
# Test 3: t.CInline - 内联函数标记
|
|
||||||
def InlineSquare(x: int) -> int | t.CInline:
|
|
||||||
return x * x
|
|
||||||
|
|
||||||
|
|
||||||
# Test 4: 普通函数(无装饰器,作为对照)
|
|
||||||
def NormalSub(a: int, b: int) -> int:
|
|
||||||
return a - b
|
|
||||||
|
|
||||||
|
|
||||||
# Test 5: t.CExport 与 t.CInline 组合
|
|
||||||
def ExportedInlineCube(x: int) -> int | t.CExport | t.CInline:
|
|
||||||
return x * x * x
|
|
||||||
|
|
||||||
|
|
||||||
# Test 6: t.CExport 返回指针
|
|
||||||
def ExportedFindChar(s: str, ch: int) -> str | t.CExport:
|
|
||||||
return string.strchr(s, ch)
|
|
||||||
|
|
||||||
|
|
||||||
def test_cdefine():
|
|
||||||
stdio.printf("--- Test 1: t.CDefine ---\n")
|
|
||||||
stdio.printf("MAX_VALUE=%d (expect 256)\n", MAX_VALUE)
|
|
||||||
stdio.printf("PI_APPROX=%d (expect 314)\n", PI_APPROX)
|
|
||||||
if MAX_VALUE == 256:
|
|
||||||
stdio.printf("MAX_VALUE OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("MAX_VALUE FAIL\n")
|
|
||||||
if PI_APPROX == 314:
|
|
||||||
stdio.printf("PI_APPROX OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("PI_APPROX FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_cexport():
|
|
||||||
stdio.printf("--- Test 2: t.CExport ---\n")
|
|
||||||
r: int = ExportedAdd(3, 4)
|
|
||||||
stdio.printf("ExportedAdd(3,4)=%d (expect 7)\n", r)
|
|
||||||
if r == 7:
|
|
||||||
stdio.printf("ExportedAdd OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("ExportedAdd FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_cinline():
|
|
||||||
stdio.printf("--- Test 3: t.CInline ---\n")
|
|
||||||
r: int = InlineSquare(5)
|
|
||||||
stdio.printf("InlineSquare(5)=%d (expect 25)\n", r)
|
|
||||||
if r == 25:
|
|
||||||
stdio.printf("InlineSquare OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("InlineSquare FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_deco_normal():
|
|
||||||
stdio.printf("--- Test 4: normal function ---\n")
|
|
||||||
r: int = NormalSub(10, 3)
|
|
||||||
stdio.printf("NormalSub(10,3)=%d (expect 7)\n", r)
|
|
||||||
if r == 7:
|
|
||||||
stdio.printf("NormalSub OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("NormalSub FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_inline():
|
|
||||||
stdio.printf("--- Test 5: t.CExport | t.CInline ---\n")
|
|
||||||
r: int = ExportedInlineCube(3)
|
|
||||||
stdio.printf("ExportedInlineCube(3)=%d (expect 27)\n", r)
|
|
||||||
if r == 27:
|
|
||||||
stdio.printf("ExportedInlineCube OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("ExportedInlineCube FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_export_ptr():
|
|
||||||
stdio.printf("--- Test 6: t.CExport return ptr ---\n")
|
|
||||||
p: str = ExportedFindChar("Hello", 108) # 'l' = 108
|
|
||||||
if p is not None:
|
|
||||||
stdio.printf("ExportedFindChar found, char=%d (expect 108)\n", c.Deref(p))
|
|
||||||
if c.Deref(p) == 108:
|
|
||||||
stdio.printf("ExportedFindChar OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("ExportedFindChar FAIL\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("ExportedFindChar NOT FOUND (FAIL)\n")
|
|
||||||
|
|
||||||
|
|
||||||
# Test 7: t.CExtern | t.CExport - 外部声明函数(pass 体)
|
|
||||||
# 声明一个外部函数,翻译器应生成 declare 而非 define
|
|
||||||
def ExternalDecl(x: int) -> int | t.CExtern | t.CExport: pass
|
|
||||||
|
|
||||||
|
|
||||||
# Test 8: t.State - 状态声明(等价于 CExtern | CExport)
|
|
||||||
def StateDecl(x: int) -> int | t.State: pass
|
|
||||||
|
|
||||||
|
|
||||||
# Test 9: t.State 返回 void
|
|
||||||
def StateVoidDecl() -> t.State: pass
|
|
||||||
|
|
||||||
|
|
||||||
def test_extern_decl():
|
|
||||||
stdio.printf("--- Test 7: t.CExtern | t.CExport ---\n")
|
|
||||||
# 外部声明函数,不调用(应由链接器解析符号)
|
|
||||||
stdio.printf("ExternalDecl declared (not called)\n")
|
|
||||||
stdio.printf("ExternalDecl OK\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_state_decl():
|
|
||||||
stdio.printf("--- Test 8: t.State ---\n")
|
|
||||||
stdio.printf("StateDecl declared (not called)\n")
|
|
||||||
stdio.printf("StateDecl OK\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_state_void_decl():
|
|
||||||
stdio.printf("--- Test 9: t.State (void) ---\n")
|
|
||||||
stdio.printf("StateVoidDecl declared (not called)\n")
|
|
||||||
stdio.printf("StateVoidDecl OK\n")
|
|
||||||
|
|
||||||
|
|
||||||
# Test 10: t.CDefine 作为 t.CArray count
|
|
||||||
# ARR_SIZE: t.CDefine = 8 已在文件顶部定义
|
|
||||||
def test_cdefine_array_count():
|
|
||||||
stdio.printf("--- Test 10: t.CArray[elem, CDefine] ---\n")
|
|
||||||
# 使用 CDefine 常量名作为数组 count
|
|
||||||
arr: t.CArray[int, ARR_SIZE]
|
|
||||||
i: int
|
|
||||||
# 初始化: arr[i] = i * 10
|
|
||||||
for i in range(ARR_SIZE):
|
|
||||||
arr[i] = i * 10
|
|
||||||
# 求和验证: 0+10+20+...+70 = 280
|
|
||||||
total: int = 0
|
|
||||||
for i in range(ARR_SIZE):
|
|
||||||
total += arr[i]
|
|
||||||
stdio.printf("ARR_SIZE=%d (expect 8)\n", ARR_SIZE)
|
|
||||||
stdio.printf("arr sum=%d (expect 280)\n", total)
|
|
||||||
stdio.printf("arr[0]=%d (expect 0)\n", arr[0])
|
|
||||||
stdio.printf("arr[7]=%d (expect 70)\n", arr[7])
|
|
||||||
if ARR_SIZE == 8 and total == 280 and arr[0] == 0 and arr[7] == 70:
|
|
||||||
stdio.printf("CArray CDefine count OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("CArray CDefine count FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def deco_test() -> int:
|
|
||||||
stdio.printf("=== deco_test: t/c 装饰器测试 ===\n\n")
|
|
||||||
|
|
||||||
test_cdefine()
|
|
||||||
test_cexport()
|
|
||||||
test_cinline()
|
|
||||||
test_deco_normal()
|
|
||||||
test_export_inline()
|
|
||||||
test_export_ptr()
|
|
||||||
test_extern_decl()
|
|
||||||
test_state_decl()
|
|
||||||
test_state_void_decl()
|
|
||||||
test_cdefine_array_count()
|
|
||||||
|
|
||||||
stdio.printf("\n=== deco_test 完成 ===\n")
|
|
||||||
return 0
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
def deref_min_test() -> int:
|
|
||||||
s: str = "hello"
|
|
||||||
v: int = c.Deref(s)
|
|
||||||
stdio.printf("deref: %d\n", v)
|
|
||||||
return 0
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
def deref_test() -> int:
|
|
||||||
s: str = "hello"
|
|
||||||
sp: str = s
|
|
||||||
slen: int = 0
|
|
||||||
while c.Deref(sp) != 0:
|
|
||||||
slen = slen + 1
|
|
||||||
sp = sp + 1
|
|
||||||
stdio.printf("ptr: len(hello)=%d\n", slen)
|
|
||||||
return 0
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
import string
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# eq_test - ==、!=、is、is not、None 比较测试
|
|
||||||
#
|
|
||||||
# 验证比较表达式的翻译,特别是 None 常量和 is/is not 操作符
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
|
|
||||||
def test_int_eq():
|
|
||||||
stdio.printf("--- Test 1: int == / != ---\n")
|
|
||||||
|
|
||||||
a: int = 42
|
|
||||||
b: int = 42
|
|
||||||
c3: int = 99
|
|
||||||
|
|
||||||
if a == b:
|
|
||||||
stdio.printf("int == int (same) OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int == int (same) FAIL\n")
|
|
||||||
|
|
||||||
if a != c3:
|
|
||||||
stdio.printf("int != int (diff) OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int != int (diff) FAIL\n")
|
|
||||||
|
|
||||||
if not (a == c3):
|
|
||||||
stdio.printf("not (int == diff) OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("not (int == diff) FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_int_cmp():
|
|
||||||
stdio.printf("--- Test 2: int < > <= >= ---\n")
|
|
||||||
|
|
||||||
a: int = 5
|
|
||||||
b: int = 10
|
|
||||||
|
|
||||||
if a < b:
|
|
||||||
stdio.printf("int < OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int < FAIL\n")
|
|
||||||
|
|
||||||
if b > a:
|
|
||||||
stdio.printf("int > OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int > FAIL\n")
|
|
||||||
|
|
||||||
if a <= 5:
|
|
||||||
stdio.printf("int <= OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int <= FAIL\n")
|
|
||||||
|
|
||||||
if b >= 10:
|
|
||||||
stdio.printf("int >= OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("int >= FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_ptr_is_none():
|
|
||||||
stdio.printf("--- Test 3: ptr is None / is not None ---\n")
|
|
||||||
|
|
||||||
# strchr 找到字符返回非空指针,找不到返回 None
|
|
||||||
p: str = string.strchr("Hello", 108) # 'l' = 108
|
|
||||||
if p is not None:
|
|
||||||
stdio.printf("strchr found, is not None OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("strchr found, is not None FAIL\n")
|
|
||||||
|
|
||||||
p2: str = string.strchr("Hello", 122) # 'z' = 122, not found
|
|
||||||
if p2 is None:
|
|
||||||
stdio.printf("strchr not found, is None OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("strchr not found, is None FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_ptr_assign_none():
|
|
||||||
stdio.printf("--- Test 4: assign None and compare ---\n")
|
|
||||||
|
|
||||||
p: str = None
|
|
||||||
if p is None:
|
|
||||||
stdio.printf("None assign, is None OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("None assign, is None FAIL\n")
|
|
||||||
|
|
||||||
if p is not None:
|
|
||||||
stdio.printf("None assign, is not None FAIL\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("None assign, is not None OK\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_ptr_eq_none():
|
|
||||||
stdio.printf("--- Test 5: ptr == None / != None ---\n")
|
|
||||||
|
|
||||||
p: str = string.strstr("Hello World", "World")
|
|
||||||
if p != None:
|
|
||||||
stdio.printf("strstr found, != None OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("strstr found, != None FAIL\n")
|
|
||||||
|
|
||||||
p2: str = string.strstr("Hello", "xyz")
|
|
||||||
if p2 == None:
|
|
||||||
stdio.printf("strstr not found, == None OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("strstr not found, == None FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def test_bool_logic():
|
|
||||||
stdio.printf("--- Test 6: bool and/or ---\n")
|
|
||||||
|
|
||||||
a: int = 1
|
|
||||||
b: int = 0
|
|
||||||
|
|
||||||
if a and b:
|
|
||||||
stdio.printf("1 and 0 = true FAIL\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("1 and 0 = false OK\n")
|
|
||||||
|
|
||||||
if a or b:
|
|
||||||
stdio.printf("1 or 0 = true OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("1 or 0 = true FAIL\n")
|
|
||||||
|
|
||||||
if not b:
|
|
||||||
stdio.printf("not 0 = true OK\n")
|
|
||||||
else:
|
|
||||||
stdio.printf("not 0 = true FAIL\n")
|
|
||||||
|
|
||||||
|
|
||||||
def eq_test() -> int:
|
|
||||||
stdio.printf("=== eq_test: ==、!=、is、None 比较测试 ===\n\n")
|
|
||||||
|
|
||||||
test_int_eq()
|
|
||||||
test_int_cmp()
|
|
||||||
test_ptr_is_none()
|
|
||||||
test_ptr_assign_none()
|
|
||||||
test_ptr_eq_none()
|
|
||||||
test_bool_logic()
|
|
||||||
|
|
||||||
stdio.printf("\n=== eq_test 完成 ===\n")
|
|
||||||
return 0
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 联合体(t.CUnion)含浮点字段
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
class DataUnion(t.CUnion):
|
|
||||||
i: int
|
|
||||||
f: t.CFloat
|
|
||||||
l: int64
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 主函数
|
|
||||||
# ============================================================
|
|
||||||
def float_test() -> int:
|
|
||||||
# ============================================================
|
|
||||||
# 测试 1: 基本浮点变量(float / double)
|
|
||||||
# ============================================================
|
|
||||||
f: t.CFloat = 3.14
|
|
||||||
stdio.printf("float: f=%f\n", f)
|
|
||||||
|
|
||||||
d: t.CDouble = 3.141592653589793
|
|
||||||
stdio.printf("double: d=%lf\n", d)
|
|
||||||
|
|
||||||
# 重新赋值
|
|
||||||
f = 2.5
|
|
||||||
stdio.printf("float: f=%f\n", f)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 2: 联合体浮点字段
|
|
||||||
# ============================================================
|
|
||||||
u: DataUnion
|
|
||||||
u.f = 3.14
|
|
||||||
stdio.printf("union: u.f=%f\n", u.f)
|
|
||||||
|
|
||||||
# 写入 i 字段后,f 的值已被覆盖
|
|
||||||
u.i = 42
|
|
||||||
stdio.printf("union: u.i=%d (after f overwritten)\n", u.i)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 3: 联合体共享内存验证(float 与 int 共享)
|
|
||||||
# ============================================================
|
|
||||||
u2: DataUnion
|
|
||||||
u2.i = 0
|
|
||||||
u2.f = 1.0
|
|
||||||
# 写入 f 后,i 的值应不再是 0
|
|
||||||
if u2.i != 0:
|
|
||||||
stdio.printf("union: float overwrites int verified\n")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 4: 浮点算术运算(fadd/fsub/fmul/fdiv/frem)
|
|
||||||
# ============================================================
|
|
||||||
a: t.CFloat = 1.5
|
|
||||||
b: t.CFloat = 2.5
|
|
||||||
stdio.printf("arith: %.2f + %.2f = %.2f\n", a, b, a + b)
|
|
||||||
stdio.printf("arith: %.2f - %.2f = %.2f\n", a, b, a - b)
|
|
||||||
stdio.printf("arith: %.2f * %.2f = %.2f\n", a, b, a * b)
|
|
||||||
stdio.printf("arith: %.2f / %.2f = %.2f\n", a, b, a / b)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 5: double 算术运算
|
|
||||||
# ============================================================
|
|
||||||
x: t.CDouble = 10.0
|
|
||||||
y: t.CDouble = 3.0
|
|
||||||
stdio.printf("arith: %.4lf + %.4lf = %.4lf\n", x, y, x + y)
|
|
||||||
stdio.printf("arith: %.4lf - %.4lf = %.4lf\n", x, y, x - y)
|
|
||||||
stdio.printf("arith: %.4lf * %.4lf = %.4lf\n", x, y, x * y)
|
|
||||||
stdio.printf("arith: %.4lf / %.4lf = %.4lf\n", x, y, x / y)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 6: 浮点取模 (frem)
|
|
||||||
# ============================================================
|
|
||||||
stdio.printf("arith: 10.0 %% 3.0 = %.4lf\n", x % y)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# 测试 7: int 与 float 混合运算(int 自动转 float)
|
|
||||||
# ============================================================
|
|
||||||
n: int = 3
|
|
||||||
stdio.printf("arith: %d + %.2f = %.2f\n", n, a, n + a)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
def flow_test() -> int:
|
|
||||||
i: int = 0
|
|
||||||
total: int = 0
|
|
||||||
while i < 5:
|
|
||||||
total = total + i
|
|
||||||
i = i + 1
|
|
||||||
stdio.printf("while: total=%d i=%d\n", total, i)
|
|
||||||
|
|
||||||
for k in range(10):
|
|
||||||
if k == 3:
|
|
||||||
break
|
|
||||||
stdio.printf("break: k=%d\n", k)
|
|
||||||
|
|
||||||
for m in range(5):
|
|
||||||
if m == 2:
|
|
||||||
continue
|
|
||||||
stdio.printf("continue: m=%d\n", m)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import stdio
|
|
||||||
import t, c
|
|
||||||
|
|
||||||
|
|
||||||
def for_test() -> int:
|
|
||||||
for j in range(5):
|
|
||||||
stdio.printf("for: j=%d\n", j)
|
|
||||||
return 0
|
|
||||||
Loaded 100 of 131 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user