fix(defines): isolate same-name cross-module DefineT lookups

This commit is contained in:
TermiNexus committed 2026-10-26 17:40:06 +08:00
1 parent ebb291daeb
commit a2e9b1848e
10 files changed
+284 -186

No files matched your search

+9 -4
View File
@@ -2309,12 +2309,12 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp
if neg_r != 0: fv_r = -fv_r
# 浮点宽度同样由声明注解裁决(与定义方同一套),
# 否则 `define PI: float = ...` 的使用方会把 f32 当 double
GenApi.register_cdefine_double(pool, cdef_t.id, fv_r, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation))
GenApi.register_cdefine_double(pool, cdef_t.id, fv_r, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation), sha1_r)
elif cn_v.const_kind == ast.CONST_INT:
cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode_r)
# 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度),
# 否则使用方拿 bits=0 只能兜底,跨模块位宽会与定义方不一致
GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, vnode_r))
GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, vnode_r), sha1_r)
# recurse into child imports (cycle guarded by the visited marks)
if sub_imports[0] != '\0': _vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1)
stdlib.free(sub_imports)
@@ -3241,6 +3241,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
if pre_skip_fn_declares == 0:
_inject_method_declare(pool, mod, m_fd1, cls_name_v1, trans, src_sha1, imported_modules, from_imports, i32_ty, i8_ptr_ty, funcs_ptr, max_funcs)
def_mod: str = src_sha1
if trans is not None and trans.ModuleSha1 is not None and string.strcmp(trans.ModuleSha1, src_sha1) == 0:
# This vpi is the current module's own: keep its defines reachable by bare name.
def_mod = None
i: sizet
for i in range(int(n)):
child: ast.AST* = ch.get(i)
@@ -3308,11 +3313,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
# Float DefineT (e.g. FRAGMENTATION_THRESHOLD: t.DefineT = 0.7): register via
# register_cdefine_double (integer extraction would fail fast).
# 注解声明 float 时带上 f32 种类(与定义方同一套裁决)。
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation))
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation), def_mod)
elif cdef_is_str == 0:
cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode)
# 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度)
GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, vnode))
GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, vnode), def_mod)
else:
_inject_global_variable_declare(pool, mod, aa, trans, stub_imported_modules, stub_from_imports, i32_ty, i8_ptr_ty, src_sha1)
continue
+49 -43
View File
@@ -350,46 +350,50 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
# first check whether it is an ordinary variable (higher priority than module attr)
ma_is_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_ma.id)
if ma_is_var is None:
# first check whether it is a DefineT compile-time constant (e.g. ast.CONST_INT /
# win32base.INVALID_HANDLE_VALUE); such constants require a cross-module .vp/.vpi search
cdef_ma_val: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
cd_local: int = self.Gen.TypeGenCDef.is_cdefine_found()
if cd_local == 0:
# determine the real module name: prefer resolving the alias from _from_imports,
# whose entries look like "local_name:real_module" (e.g. win32base:win32.win32base)
real_mod_name: str = nm_ma.id
if trans._from_imports is not None:
resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0)
if resolved_mod is not None:
# copy the module name into a new buffer (truncate at space, null, or ':')
rm_len: sizet = 0
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1
rm_buf: str = pool.alloc(rm_len + 1)
if rm_buf is not None:
for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i]
rm_buf[rm_len] = '\0'
real_mod_name = rm_buf
# even when _from_imports is None (e.g. a plain `import ast`), still attempt the
# cross-module DefineT lookup, else ast.CONST_INT is misread as @CONST_INT (undefined).
if real_mod_name is not None:
ma_fi_len: sizet = string.strlen(at.attr) + string.strlen(real_mod_name) + 2
ma_fi: str = pool.alloc(ma_fi_len)
if ma_fi is not None:
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
cdef_ma_val = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, ma_fi)
# Resolve the receiver module BEFORE any lookup. A module-qualified access
# must never fall back to the bare table: that is how `b.VALUE` used to
# return `a.VALUE` when both modules define VALUE.
real_mod_name: str = None
if trans._from_imports is not None:
resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0)
if resolved_mod is not None:
rm_len: sizet = 0
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1
rm_buf: str = pool.alloc(rm_len + 1)
if rm_buf is not None:
for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i]
rm_buf[rm_len] = '\0'
real_mod_name = rm_buf
if real_mod_name is None and trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, nm_ma.id) != 0: real_mod_name = nm_ma.id
if real_mod_name is None and self._lookup_module_sha1(nm_ma.id) is not None: real_mod_name = nm_ma.id
cd_mod: str = None
cdef_ma_val: int64 = 0
if real_mod_name is not None:
# Qualified access: look up only this module's define.
ma_fi_len: sizet = string.strlen(at.attr) + string.strlen(real_mod_name) + 2
ma_fi: str = pool.alloc(ma_fi_len)
if ma_fi is not None:
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
cdef_ma_val = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, ma_fi)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
cd_mod = self.Gen.TypeGenCDef.get_last_cdefine_module()
else:
# Non-module receiver (class/enum qualified field): bare table only.
cdef_ma_val = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
# cross-module string DefineT (e.g. pyzlib.ZLIB_VERSION = "1.3.2")
# → string global constant (test_pyzlib.vp: ver = pyzlib.ZLIB_VERSION)
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
cdef_str_v: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr)
cdef_str_v: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr, cd_mod)
if cdef_str_v is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_str_v, trans)
# cross-module float DefineT (e.g. vipermath.U_M_PI) → float constant
dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr, cd_mod)
# f32 声明的常量直接出 f32 常量(否则 f32 字面量会被提升成 double)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_ma)
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val, cd_mod)
# check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file);
# for a submodule do not create a global, let the nested attribute path handle it
is_submod: int = 0
@@ -493,23 +497,25 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
case _:
pass
return mlirlite.build_load(builder, load_ty_nested, gv_ref_nested)
# global not found: try a DefineT constant lookup; for nested module attributes
# like win32.win32base.INVALID_HANDLE_VALUE, resolve the DefineT from the .vpi
cdef_val_nested: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# build a temporary from_imports "attr:module" for the cross-module lookup
tmp_fi_len: sizet = string.strlen(at.attr) + string.strlen(qual_buf) + 2
tmp_fi: str = pool.alloc(tmp_fi_len)
if tmp_fi is not None:
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
# DefineT lookup for a nested module attribute: the qualified
# module is known, so never consult the bare table here.
cdef_val_nested: int64 = 0
tmp_fi_len: sizet = string.strlen(at.attr) + string.strlen(qual_buf) + 2
tmp_fi: str = pool.alloc(tmp_fi_len)
if tmp_fi is not None:
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
nst_mod: str = self.Gen.TypeGenCDef.get_last_cdefine_module()
# cross-module float DefineT → float constant (f32 声明直接出 f32)
dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr, nst_mod)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nst)
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested)
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
nst_s: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr, nst_mod)
if nst_s is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, nst_s, trans)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested, nst_mod)
# DefineT not found: fail-fast and stop compiling (no more undefined global
# references, which would make llc report "use of undefined value").
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0:
+37 -81
View File
@@ -127,15 +127,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# clear the found flag at entry so every early return reports "not found",
# avoiding a stale success flag left over from a previous recursive call.
self.Gen.TypeGenCDef.set_cdefine_found(0)
self.Gen.TypeGenCDef.set_last_cdefine_module(None)
if name is None: return -1
if from_imports is None: from_imports = ""
# 1. resolve the module name for `name` from from_imports; try exact match first
# (allow_star_fallback=0) so a star import cannot mislead DefineT constants.
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
if mod_name_raw is None:
# exact match failed: try the star import fallback
mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1)
# 1. resolve the module name for `name` from from_imports. The LAST matching entry
# wins (Python name binding), so `from a import X; from b import X` resolves to b.
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import_last(from_imports, name)
# truncate module name at NUL: lookup_from_import returns a pointer into from_imports
# (not NUL-terminated); match _lookup_sha1_from_modname_raw (space/colon/NUL delimited).
@@ -153,6 +151,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# cross-module DefineT cache (perm_pool, reused across files): key = "name@srcModule"
# keeps modules isolated so same-name constants from different modules do not cross.
# Local table entries are keyed by the source module SHA1 (stronger than a name alias).
sha1_pre: str = None
if mod_name_raw is not None: sha1_pre = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
mod_key: str = sha1_pre if sha1_pre is not None else base_mod
xkey_buf: str = None
if base_mod is not None:
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
@@ -161,55 +163,27 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
xval: str = self.Gen.TypeGenCDef.lookup_xmod_cdefine(xkey_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval)
self.Gen.TypeGenCDef.set_last_cdefine_module(mod_key)
if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval, mod_key)
elif self.Gen.TypeGenCDef.is_cdefine_double() != 0:
xd_v: double = string.atof(xval)
# 缓存里带宽度种类(f32 声明 → 1),否则使用方会把 f32 常量当 double 物化
xd_f32: int = self.Gen.TypeGenCDef.is_cdefine_float32()
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32)
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32, mod_key)
else:
xv_i: int64 = string.atoll(xval)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i, 0, mod_key)
return string.atoll(xval)
# 2. from_imports failed: fall back to the imported_modules walk (step 9.6);
# a star fallback may name a wrong module, so keep scanning imported_modules.
# 2. No module could be resolved from from_imports. Do not guess by scanning every
# imported module: with same-name defines in several modules that picks an arbitrary
# value. Callers must qualify (a.X / b.X) or use an explicit from-import.
if mod_name_raw is None:
# from_imports matched nothing (exact and star both failed):
# go straight to the imported_modules walk (step 9.6).
if imported_modules is None: return -1
# skip reading the from_imports module; go directly to the step 9.6 walk.
# result_found=0 skips steps 3-9 and jumps to 9.6.
self.Gen.TypeGenCDef.set_cdefine_found(0)
# iterate imported_modules directly
im_len: sizet = string.strlen(imported_modules)
im_ci: sizet = 0
while im_ci < im_len:
while im_ci < im_len and imported_modules[im_ci] == ' ': im_ci += 1
if im_ci >= im_len: break
im_start: sizet = im_ci
while im_ci < im_len and imported_modules[im_ci] != ' ': im_ci += 1
im_end: sizet = im_ci
im_nlen: sizet = im_end - im_start
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
for mk in range(int(im_nlen)):
im_buf[mk] = imported_modules[im_start + mk]
im_buf[im_nlen] = '\0'
sub_fi: str = pool.alloc(im_nlen + 3)
if sub_fi is not None:
sub_fi[0] = '*'
sub_fi[1] = ':'
for mk2 in range(int(im_nlen)):
sub_fi[2 + mk2] = im_buf[mk2]
sub_fi[2 + im_nlen] = '\0'
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return sub_val
return -1
# truncate module name, strip leading dots, find SHA1 via exact+suffix (shared logic)
sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
sha1: str = sha1_pre
if sha1 is None: sha1 = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
if sha1 is None: return -1
# 5. get temp_dir
@@ -487,7 +461,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if is_str_val != 0:
# string DefineT (e.g. ZLIB_VERSION = "1.3.2"): register the string value
# (including ""), mark result_found + res_is_str so the tail skips integers.
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf, sha1)
self.Gen.TypeGenCDef.set_last_cdefine_module(sha1)
result_val = 0
result_found = 1
res_is_str = 1
@@ -496,8 +471,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if string.strcmp(val_buf, "True") == 0: lval = 1
elif string.strcmp(val_buf, "False") == 0: lval = 0
else:
# name reference: look up the current DefineT table (e.g. FOREGROUND_COMBO)
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
# name reference: same source module first, then the current module
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf, sha1)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# name not registered: cannot evaluate, skip the line
continue
@@ -608,7 +585,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if string.strcmp(r_buf, "True") == 0: rval = 1
elif string.strcmp(r_buf, "False") == 0: rval = 0
else:
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf, sha1)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# name not registered: cannot evaluate, skip the line
continue
@@ -650,7 +629,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# constant generation; int part serves the int path (is_cdefine_double flag).
# 注解声明为 float 时带上 f32 种类,使用方才能直接出 f32 常量。
res_is_f32 = self._pyi_annot_float_kind(pyi_buf, annot_start, annot_end)
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32)
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32, sha1)
self.Gen.TypeGenCDef.set_last_cdefine_module(sha1)
result_val = lval
result_found = 1
res_is_double = 1
@@ -805,39 +785,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
stdlib.free(pyi_buf)
# 9.6 star-fallback module had no match: fall back to walking imported_modules,
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import win32.fileio`.
if result_found == 0 and imported_modules is not None:
im2_len: sizet = string.strlen(imported_modules)
im2_ci: sizet = 0
while im2_ci < im2_len and result_found == 0:
while im2_ci < im2_len and imported_modules[im2_ci] == ' ': im2_ci += 1
if im2_ci >= im2_len: break
im2_start: sizet = im2_ci
while im2_ci < im2_len and imported_modules[im2_ci] != ' ': im2_ci += 1
im2_end: sizet = im2_ci
im2_nlen: sizet = im2_end - im2_start
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
for mk3 in range(int(im2_nlen)):
im2_buf[mk3] = imported_modules[im2_start + mk3]
im2_buf[im2_nlen] = '\0'
sub_fi2: str = pool.alloc(im2_nlen + 3)
if sub_fi2 is not None:
sub_fi2[0] = '*'
sub_fi2[1] = ':'
for mk4 in range(int(im2_nlen)):
sub_fi2[2 + mk4] = im2_buf[mk4]
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
result_val = sub_val2
result_found = 1
# 9.6 removed: scanning every imported module for a bare define name is not isolation.
# An unqualified name must resolve through an explicit from-import; module-qualified
# access resolves the receiver module before lookup.
# 10. cache into the current module's DefineT table and the cross-module perm_pool;
# float CDefines are already cached as double, so only integers are written here.
if result_found != 0:
if self.Gen.TypeGenCDef.get_last_cdefine_module() is None: self.Gen.TypeGenCDef.set_last_cdefine_module(sha1)
if res_is_str != 0:
# string DefineT: the value was already registered in the match loop (val_buf is
# the hit line's content, valid across the function); only write the cross cache.
@@ -848,7 +803,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
elif res_is_double == 0:
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val, 0, sha1)
self.Gen.TypeGenCDef.set_last_cdefine_module(sha1)
# cross-module cache (perm_pool, reused across files), integers: key matches the
# entry query "name@srcModule" (truncated base_mod, leading dots removed).
if base_mod is not None:
@@ -867,7 +823,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
ckey2: str = pool.alloc(ck_len2)
if ckey2 is not None:
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name)
dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name, sha1)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
cbuf_d: str = pool.alloc(64)
if cbuf_d is not None:
@@ -893,7 +849,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
return has_upper
# ============================================================
# Translate a variable reference (Name node) -> load
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*:
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64, module: str = None) -> mlirlite.Value*:
"""Materialize a DefineT integer constant as an LLVM constant (TPC TypeConvert aligned)
位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits);
@@ -903,7 +859,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
而小值 define 说成 8/16 也在算术提升下不会溢出。
"""
dbits: int = 0
if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name)
if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name, module)
if dbits == 8 or dbits == 16 or dbits == 32 or dbits == 64:
return mlirlite.const_int(pool, dbits, self._cdefine_bits_value(value, dbits))
# 无位宽记录(例如只从 .vpi 跨模块引入、未登记 bits)→ 按**推断位宽**物化:
+4 -3
View File
@@ -77,13 +77,14 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
cdef_val: int64 = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm_id, trans._from_imports, trans._imported_modules, 0, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
xmod: str = self.Gen.TypeGenCDef.get_last_cdefine_module()
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id, xmod)
if cdef_sv2 is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv2, trans)
self.Gen.TypeGenErr.fatal_error(node, f"DefineT string value missing: {nm_id}", "InternalCompilerError")
# Cross-module float DefineT is cached as double; return a float constant
# rather than a truncated integer.
dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id, xmod)
# 同本模块路径:f32 声明的常量直接出 f32 常量(跨模块缓存已带宽度种类)。
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nm)
@@ -91,7 +92,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# Same use-site width rule (cross-module DefineT)
uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
if uw_x is not None: return uw_x
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val, xmod)
# DefineT lookup failed: from-import module-level globals (e.g. `from Phase2 import
# PA_DEPS_MAX_STR`, an i64 global) are located by suffix after confirming the source module.
+3
View File
@@ -448,6 +448,9 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
mod: mlirlite.LLVMModule* = trans.Module
if pool is None or mod is None: return 0
# Pre-register this module's own DefineT constants before any signature/body is
# translated (Translator._preregister_module_defines, before forward declarations).
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
+12 -6
View File
@@ -177,9 +177,9 @@ def is_cdefine_found() -> int:
# ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ----------------
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0):
"""Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits)
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0, module: str = None):
"""Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽; module: source SHA1)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits, module)
# ---------------- cdefine_decl_bits (delegates to AssignGenMixin.cdefine_decl_bits) ----------------
@@ -191,9 +191,9 @@ def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.A
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0):
"""Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32)
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0, module: str = None):
"""Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double; module: source SHA1)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32, module)
# ---------------- cdefine_decl_float_bits (delegates to AssignGenMixin.cdefine_decl_float_bits) ----------------
@@ -204,6 +204,12 @@ def cdefine_decl_float_bits(pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
return 0
# ---------------- register_cdefine_from_ast (delegates to AssignGenMixin._register_cdefine_from_value) ----------------
def register_cdefine_from_ast(pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None):
"""Evaluate and register one module-level define from its AST value."""
if _g_gen is not None: _g_gen.AssignGen._register_cdefine_from_value(pool, name, val_node, annot)
# ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ----------------
def is_cdefine_annotation(annot: ast.AST*) -> int:
"""Delegate to gen.is_cdefine_annotation"""
+40
View File
@@ -264,6 +264,46 @@ novirtual class ImportsGenMixin:
while ci < total_len and cur[ci] != ' ': ci += 1
return None
# lookup_from_import_last - like lookup_from_import, but the LAST matching entry wins
# (Python name binding: a later from-import shadows an earlier one). Used for DefineT
# resolution so `from a import X; from b import X` resolves to b, not a.
def lookup_from_import_last(self, from_imports: str, name: str) -> str:
"""Return the module of the last exact (else last star) from-import for name."""
if name is None or from_imports is None: return None
name_len: sizet = string.strlen(name)
cur: str = from_imports
ci: sizet = 0
total_len: sizet = string.strlen(from_imports)
last_exact: str = None
last_star: str = None
while ci < total_len:
while ci < total_len and cur[ci] == ' ': ci += 1
if ci >= total_len: break
colon_pos: sizet = ci
while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ': colon_pos += 1
if colon_pos >= total_len or cur[colon_pos] != ':': break
entry_name_len: sizet = colon_pos - ci
if entry_name_len == 1 and cur[ci] == '*':
mod_start: sizet = colon_pos + 1
mod_end: sizet = mod_start
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':': mod_end += 1
if mod_end > mod_start: last_star = cur + mod_start
elif entry_name_len == name_len:
matched: int = 1
for ei in range(int(name_len)):
if cur[ci + ei] != name[ei]:
matched = 0
break
if matched != 0:
mod_start2: sizet = colon_pos + 1
mod_end2: sizet = mod_start2
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':': mod_end2 += 1
if mod_end2 > mod_start2: last_exact = cur + mod_start2
ci = colon_pos
while ci < total_len and cur[ci] != ' ': ci += 1
if last_exact is not None: return last_exact
return last_star
# Look up a from-import name and return the module name, or None.
# allow_star_fallback=1 permits a star-import fallback; pass 0 to disable it.
def lookup_from_import(self, from_imports: str, name: str, allow_star_fallback: int = 1) -> str:
+20
View File
@@ -559,6 +559,9 @@ novirtual class Translator:
GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno)
StubMergerBuild.LoadDepVpiIntoModule(pool, mod, self._imported_modules, self._from_imports, GenApi.get_temp_dir(), self)
GenApi.pop_trace()
# Register the module's own DefineT constants before any class/function body is
# translated, so a define may be used above its declaration line.
self._preregister_module_defines(pool, tree)
# File-level class pre-registration already ran at the top of this function.
# Module var pre-registration + forward declarations run only in full translation.
if self._declare_only == 0:
@@ -720,6 +723,23 @@ novirtual class Translator:
if erased != 0: tree.children = kept
return erased
# _preregister_module_defines - evaluate/register all top-level DefineT constants early.
# Runs in declare-only and full translation, so signatures (array sizes) and bodies that
# appear above the define line can use it. Values stay in the current module's namespace.
def _preregister_module_defines(self, pool: memhub.MemBuddy*, tree: ast.AST*):
if tree is None or tree.children is None: return
ch: list[ast.AST*]* = tree.children
for ci in range(int(len(ch))):
child: ast.AST* = ch.get(ci)
if child is None or child.kind() != ast.ASTKind.AnnAssign: continue
aa: ast.AnnAssign* = ast.AnnAssign*(child)
if aa is None or aa.target is None or aa.value is None or aa.annotation is None: continue
if aa.target.kind() != ast.ASTKind.Name: continue
nm: ast.Name* = ast.Name*(aa.target)
if nm.id is None: continue
if GenApi.is_cdefine_annotation(aa.annotation) == 0: continue
GenApi.register_cdefine_from_ast(pool, nm.id, aa.value, aa.annotation)
# Auto container visibility injection (mirrors TPC _inject_auto_imports): mark list/dict classes
# visible so bare annotations/constructors resolve. Detects AnnAssign annotation, Call.func, Subscript.value.
def _inject_auto_container_visibility(self, pool: memhub.MemBuddy*, tree: ast.AST*):
+8
View File
@@ -40,6 +40,9 @@ novirtual class TypeGenBase:
# --- Global DefineT compile-time constant table (was _g_cdefine_*) ---
CDefineNames: list[str]*
# Source module of each entry ("" = declared in the current module). Cross-module values
# are only reachable through the (name, module) pair, so same-name defines stay isolated.
CDefineMods: list[str]*
CDefineValues: list[str]*
# 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。
# 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32:
@@ -61,6 +64,7 @@ novirtual class TypeGenBase:
# 值与 CDefineValues 分开存:名字不变,值是 '\n' 连接的元素串(元素内不含换行),
# 供 `*OTHER_LIST` 解包与后续编译期消费者读取。与 CDefineNames 同池同生命周期。
CDefineListNames: list[str]*
CDefineListMods: list[str]*
CDefineListValues: list[str]*
# --- Cross-module DefineT lookup cache (PermPool-allocated, reused across files) ---
@@ -70,6 +74,8 @@ novirtual class TypeGenBase:
# 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。
XmodCdefDoubleFlags: list[int]*
XmodCdefStrFlags: list[int]*
# Module that produced the last successful cross-module DefineT hit ("" = local/unknown).
CDefineLastModule: str
# --- Current module SHA1 (was _g_current_module_sha1) ---
CurrentModuleSha1: str
@@ -125,6 +131,7 @@ novirtual class TypeGenBase:
self.GenericCtxStack = None
self.GenericCtxDepth = 0
self.CDefineNames = None
self.CDefineMods = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
@@ -139,6 +146,7 @@ novirtual class TypeGenBase:
self.XmodCdefValues = None
self.XmodCdefDoubleFlags = None
self.XmodCdefStrFlags = None
self.CDefineLastModule = None
self.CurrentModuleSha1 = None
self.CTypedefNames = None
self.CTypedefTypeStrs = None
+102 -49
View File
@@ -103,25 +103,51 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
return pool
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None:
def _cdef_module_copy(self, pool: memhub.MemBuddy*, module: str) -> str:
"""Deep-copy the source module for a table entry; None/"" -> empty (current module)."""
if module is None or module[0] == '\0': return ""
n: sizet = string.strlen(module)
c: str = pool.alloc(n + 1)
if c is None: return ""
string.strcpy(c, module)
return c
def _cdef_module_match(self, index: sizet, module: str) -> int:
"""1 when entry index belongs to module ("" / None = the current module)."""
if self.CDefineMods is None or index >= len(self.CDefineMods): return 0
em: str = self.CDefineMods.get(index)
if module is None or module[0] == '\0': return 1 if (em is None or em[0] == '\0') else 0
if em is None or em[0] == '\0': return 0
return 1 if string.strcmp(em, module) == 0 else 0
def set_last_cdefine_module(self, module: str) -> None:
self.CDefineLastModule = module
def get_last_cdefine_module(self) -> str:
return self.CDefineLastModule
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0, module: str = None) -> None:
"""Register an integer DefineT constant; store the value as a decimal string.
bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。
module: source module for a cross-module value; None/"" means the current module.
"""
if name is None: return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tm: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
# Table build failed (pool unusable/exhausted): report and return, never let the
# append below hit None or an uninitialized address (would write NULL/0xCD).
if tn is None or tv is None or td is None or tsf is None or tbits is None:
if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineMods = tm
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
@@ -129,7 +155,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
self._cdef_idx_init(tp)
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
# (assert non-null before append).
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:
if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy: str = pool.alloc(string.strlen(name) + 1)
@@ -138,6 +164,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return
string.strcpy(name_copy, name)
self.CDefineNames.append(name_copy)
self.CDefineMods.append(self._cdef_module_copy(pool, module))
self._cdef_idx_sync_append(name_copy)
val_buf: str = pool.alloc(32)
if val_buf is not None:
@@ -150,31 +177,34 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
if self.CDefineBits is not None: self.CDefineBits.append(bits)
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None:
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0, module: str = None) -> None:
"""Register a float DefineT constant; store the value as %f text and flag it.
is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`),
物化时直接出 f32 常量;0(默认)表示 double。
module: source module for a cross-module value; None/"" means the current module.
"""
if name is None: return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tm: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT float table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineMods = tm
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
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:
if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
@@ -183,6 +213,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return
string.strcpy(name_copy_d, name)
self.CDefineNames.append(name_copy_d)
self.CDefineMods.append(self._cdef_module_copy(pool, module))
self._cdef_idx_sync_append(name_copy_d)
val_buf: str = pool.alloc(32)
if val_buf is not None:
@@ -201,8 +232,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if val_buf is None:
VLogger.error("DefineT float value buffer allocation failed", "CDEF-DBG")
self.Gen.TypeGenErr.fatal_error(None, "DefineT float value buffer allocation failed", "MemoryError")
def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str) -> None:
"""Register a string DefineT constant, deep-copying name and value into pool."""
def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str, module: str = None) -> None:
"""Register a string DefineT constant, deep-copying name and value into pool.
module: source module for a cross-module value; None/"" means the current module.
"""
if pool is None or name is None: return
if value is None:
self.Gen.TypeGenErr.fatal_error(None, f"DefineT string value missing: {name}", "InternalCompilerError")
@@ -210,21 +244,23 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
tn: list[str]* = list[str](tp, 64)
tm: list[str]* = list[str](tp, 64)
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT string table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineMods = tm
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
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:
if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
@@ -233,6 +269,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return
string.strcpy(ncopy_s, name)
self.CDefineNames.append(ncopy_s)
self.CDefineMods.append(self._cdef_module_copy(pool, module))
self._cdef_idx_sync_append(ncopy_s)
vlen: sizet = string.strlen(value)
vcopy: str = pool.alloc(vlen + 1)
@@ -248,7 +285,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
# Cross-module string DefineT reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
self.CDefineIsStr = 1
self.CDefineFound = 1
def register_cdefine_string_list(self, pool: memhub.MemBuddy*, name: str, joined: str) -> None:
def register_cdefine_string_list(self, pool: memhub.MemBuddy*, name: str, joined: str, module: str = None) -> None:
"""Register a macro-list DefineT constant (`define NAME = [...]`, '\n'-joined value).
The list is compile-time only: no LLVM global is created (an unannotated module-level
@@ -260,13 +297,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if tp is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT list table pool unavailable", "MemoryError")
return
if self.CDefineListNames is None or self.CDefineListValues is None:
if self.CDefineListNames is None or self.CDefineListMods is None or self.CDefineListValues is None:
ln_l: list[str]* = list[str](tp, 16)
lm_l: list[str]* = list[str](tp, 16)
lv_l: list[str]* = list[str](tp, 16)
if ln_l is None or lv_l is None:
if ln_l is None or lm_l is None or lv_l is None:
self.Gen.TypeGenErr.fatal_error(None, "DefineT list table allocation failed", "MemoryError")
return
self.CDefineListNames = ln_l
self.CDefineListMods = lm_l
self.CDefineListValues = lv_l
nlen_l: sizet = string.strlen(name)
ncopy_l: str = tp.alloc(nlen_l + 1)
@@ -281,16 +320,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return
string.strcpy(vcopy_l, joined)
self.CDefineListNames.append(ncopy_l)
self.CDefineListMods.append(self._cdef_module_copy(tp, module))
self.CDefineListValues.append(vcopy_l)
def lookup_cdefine_string_list(self, name: str) -> str:
def lookup_cdefine_string_list(self, name: str, module: str = None) -> str:
"""Return the '\n'-joined value of a macro-list DefineT constant; None if absent."""
if name is None or self.CDefineListNames is None or self.CDefineListValues is None: return None
if name is None or self.CDefineListNames is None or self.CDefineListMods is None or self.CDefineListValues is None: return None
n_l: sizet = len(self.CDefineListNames)
for i in range(int(n_l)):
nm_l: str = self.CDefineListNames.get(sizet(i))
if nm_l is not None and string.strcmp(nm_l, name) == 0:
return self.CDefineListValues.get(sizet(i))
if self._cdef_module_match(i, module) != 0:
return self.CDefineListValues.get(sizet(i))
return None
# Cross-module DefineT lookup cache (PermPool-allocated, reused across files)
@@ -347,26 +388,34 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i))
return val_str
return None
def lookup_cdefine_constant(self, name: str) -> int64:
"""Look up an integer DefineT constant; 64-bit exact, found flag distinguishes -1."""
def lookup_cdefine_constant(self, name: str, module: str = None) -> int64:
"""Look up an integer DefineT constant; 64-bit exact, found flag distinguishes -1.
module=None/"" matches only defines declared in the current module; a non-empty module
matches only that source module's entry (strong cross-module isolation).
"""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None: return 0
if self.CDefineNames is None or self.CDefineMods is None: return 0
n: sizet = len(self.CDefineNames)
i: sizet
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
# Name-index fast path: only for current-module (bare) lookups; a module-qualified
# lookup must scan because the same name may exist in several modules.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return 0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
if module is None or module[0] == '\0':
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return 0
# The index points at the first same-name entry, which may belong to another
# module; only narrow when it is a current-module entry, else scan all.
if self._cdef_module_match(sizet(cd_hit), module) != 0:
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(int(cd_start), int(cd_end)):
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 and self._cdef_module_match(i, module) != 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
@@ -381,14 +430,14 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return string.atoll(val_str)
return string.atoll(val_str)
return 0
def lookup_cdefine_bits(self, name: str) -> int:
def lookup_cdefine_bits(self, name: str, module: str = None) -> int:
"""Look up a DefineT constant's value-expression bit width; 0 = unknown (width inferred by value).
同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi
等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录,
于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。
"""
if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0
if self.CDefineNames is None or self.CDefineMods is None or self.CDefineBits is None or name is None: return 0
n: sizet = len(self.CDefineNames)
# 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值),
# 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录,
@@ -397,31 +446,32 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
for i in range(int(n)):
nm: str = self.CDefineNames.get(i)
if nm is None or string.strcmp(nm, name) != 0: continue
if self._cdef_module_match(i, module) == 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:
def lookup_cdefine_double(self, name: str, module: str = None) -> double:
"""Look up a DefineT constant and return its double value (float or int)."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None: return 0.0
if self.CDefineNames is None or self.CDefineMods is None: return 0.0
n: sizet = len(self.CDefineNames)
i: sizet
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return 0.0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
if module is None or module[0] == '\0':
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return 0.0
if self._cdef_module_match(sizet(cd_hit), module) != 0:
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
# 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 and self._cdef_module_match(i, module) != 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
@@ -436,26 +486,26 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
ret_i2: double = double(string.atoll(val_str))
return ret_i2
return 0.0
def lookup_cdefine_string(self, name: str) -> str:
def lookup_cdefine_string(self, name: str, module: str = None) -> str:
"""Look up a string DefineT value (valid when found=1 and is_str=1)."""
self.CDefineFound = 0
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
if self.CDefineNames is None: return None
if self.CDefineNames is None or self.CDefineMods is None: return None
n: sizet = len(self.CDefineNames)
i: sizet
# Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate
# (== first scan hit); if unavailable fall back to the full linear scan below.
cd_start: sizet = 0
cd_end: sizet = n
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return None
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
if module is None or module[0] == '\0':
if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
cd_hit: int = self._cdef_idx_find(name)
if cd_hit < 0: return None
if self._cdef_module_match(sizet(cd_hit), module) != 0:
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(int(cd_start), int(cd_end)):
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 and self._cdef_module_match(i, module) != 0:
val_str: str = self.CDefineValues.get(i)
if val_str is not None:
self.CDefineFound = 1
@@ -491,6 +541,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
def clear_cdefine_constants(self) -> None:
"""Clear the DefineT table body in place, keeping the PermPool table pointers."""
if self.CDefineNames is not None: self.CDefineNames.clear()
if self.CDefineMods is not None: self.CDefineMods.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()
@@ -498,7 +549,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
# Macro-list table is per-file too: a stale entry would let `*NAME` resolve a list that
# belongs to another file's compilation unit.
if self.CDefineListNames is not None: self.CDefineListNames.clear()
if self.CDefineListMods is not None: self.CDefineListMods.clear()
if self.CDefineListValues is not None: self.CDefineListValues.clear()
self.CDefineLastModule = None
# 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).
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: