refactor(viperc): 退役 t/c 模块标记,内建类型改由编译器直接识别

This commit is contained in:
TermiNexus committed 2026-10-18 12:56:57 +08:00
1 parent 3242560d02
commit 42b094a575
25 files changed
+437 -660

No files matched your search

+2 -17
View File
@@ -669,21 +669,11 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return buf
return ""
# Attribute: t.ASM_DESCR.XXX or t.XXX
# Attribute: ASM_DESCR.XXX
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
# t.ASM_DESCR.XXX
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
inner: ast.Attribute* = ast.Attribute*(at.value)
if inner.value is not None and inner.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(inner.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0:
if inner.attr is not None and string.strcmp(inner.attr, "ASM_DESCR") == 0: return self._resolve_register(at.attr)
# t.XXX shorthand form
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm2: ast.Name* = ast.Name*(at.value)
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "t") == 0: return self._resolve_register(at.attr)
# Bare ASM_DESCR.XXX: equivalent form without the t. prefix
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "ASM_DESCR") == 0: return self._resolve_register(at.attr)
return ""
@@ -724,12 +714,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if string.strcmp(nm.id, "irinp") == 0: return "LInp"
if string.strcmp(nm.id, "irout") == 0: return "LOut"
return None
if node.func.kind() != ast.ASTKind.Attribute: return None
at: ast.Attribute* = ast.Attribute*(node.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name: return None
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is None or string.strcmp(mod_nm.id, "c") != 0: return None
return at.attr
return None
def _asm_name_is_mangled(self, name: str) -> int:
+23 -154
View File
@@ -248,54 +248,28 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# irinp/irout standalone calls return 0 (meaningful only inside __llvmir__ f-strings)
return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0")
# Detect c.Deref / c.DerefAs / c.Addr special calls (inline IR, no call emitted)
# and coro.* coroutine interception (inline translation, VPC-M1).
# Detect coro.* coroutine interception (inline translation, VPC-M1).
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_node: ast.Attribute* = ast.Attribute*(cl.func)
if at_node.value is not None and at_node.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at_node.value)
is_coro_mod: int = 0
if mod_nm.id is not None and string.strcmp(mod_nm.id, "coro") == 0: is_coro_mod = 1
if mod_nm.id is not None and (string.strcmp(mod_nm.id, "c") == 0 or is_coro_mod != 0):
if string.strcmp(func_name, "Deref") == 0: return self.Gen.CallGenCBuiltin.translate_c_deref(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "DerefAs") == 0: return self.Gen.CallGenCBuiltin.translate_c_derefas(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Addr") == 0: return self.Gen.CallGenCBuiltin.translate_c_addr(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Set") == 0:
# c.Set(a, b) = a = b (assignment, not pointer deref). Treating it as a plain
# call would emit @Set and fail to link (undefined reference to `Set').
return self.Gen.CallGenCBuiltin.translate_c_set(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Load") == 0: return self.Gen.CallGenCBuiltin.translate_c_load(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Asm") == 0:
# translate_c_asm returns None for void: set the hit flag so translate_call
# does not fall through to the external function path.
if hit_box is not None: hit_box[0] = 1
return self.Gen.CallGenCBuiltin.translate_c_asm(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "LLVMIR") == 0:
# c.LLVMIR always returns void (None): set the hit flag
if hit_box is not None: hit_box[0] = 1
return self.Gen.CallGenCBuiltin.translate_c_llvmir(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "LInp") == 0 or string.strcmp(func_name, "LOut") == 0:
# c.LInp/c.LOut standalone calls return 0 (only meaningful in c.LLVMIR f-strings)
return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0")
elif string.strcmp(func_name, "CIf") == 0 or string.strcmp(func_name, "CIfdef") == 0 or string.strcmp(func_name, "CIfndef") == 0 or string.strcmp(func_name, "CElif") == 0 or string.strcmp(func_name, "CElse") == 0 or string.strcmp(func_name, "CEndif") == 0:
# The c.CIf-family spellings were removed: report the replacement.
# (There is no runtime fallback: compile-time conditions use cif/celif/celse.)
self.Gen.TypeGenErr.fatal_error(cl, "c.CIf/c.CIfdef/c.CIfndef/c.CElif/c.CElse/c.CEndif were removed; use the cif/celif/celse statements and the defined(NAME) predicate instead (e.g. `cif defined(NAME):` ... `celse:` ...)", "NameError")
return None
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_run") == 0:
if is_coro_mod != 0:
if string.strcmp(func_name, "coro_run") == 0:
# coro.coro_run(frame): inline translation (resume loop until done, returns the ret slot).
# Note: translate_coro_* take (builder, pool, mod, cl, trans), unlike the c.* branches.
# Note: translate_coro_* take (builder, pool, mod, cl, trans).
return self.Gen.AsyncGen.translate_coro_run(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_destroy") == 0:
elif string.strcmp(func_name, "coro_destroy") == 0:
# coro.coro_destroy(frame): return the frame to the pool (M7 free-list)
return self.Gen.AsyncGen.translate_coro_destroy(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_exception") == 0:
elif string.strcmp(func_name, "coro_exception") == 0:
# coro.coro_exception(frame): return the frame exc slot (M4)
return self.Gen.AsyncGen.translate_coro_exception(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_exception_msg") == 0:
elif string.strcmp(func_name, "coro_exception_msg") == 0:
# coro.coro_exception_msg(frame): return the frame msg slot (M7)
return self.Gen.AsyncGen.translate_coro_exception_msg(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_gather") == 0:
elif string.strcmp(func_name, "coro_gather") == 0:
# coro.coro_gather(frames...): advance all coroutines concurrently (M4)
return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans)
return None
@@ -362,112 +336,15 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""t.XXX / typedef type casts; returns None when unmatched."""
# Detect t.XXX type casts (uint64(ptr), rawptr(val), int(val), ...): when func is t.XXX and
# XXX is a known type, treat it as a cast; once confirmed, never fall through to cross-module calls.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
tc_at: ast.Attribute* = ast.Attribute*(cl.func)
if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name:
tc_mod_nm: ast.Name* = ast.Name*(tc_at.value)
if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0:
# t.CType(value, TypeName, rawptr) casts value to TypeName*; TypeName is a type
# name (not a value), so it must be handled explicitly or it falls through to a FATAL.
if string.strcmp(func_name, "CType") == 0 and can >= 2:
cty_node: ast.AST* = cargs.get(1)
ctype_target: mlirlite.LLVMType* = None
if cty_node is not None:
cti_imp: str = None
cti_fi: str = None
if trans is not None:
cti_imp = trans._imported_modules
cti_fi = trans._from_imports
ctype_target = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cty_node, cti_imp, cti_fi, trans, 0)
if ctype_target is not None:
if can == 3:
cty_arg3: ast.AST* = cargs.get(2)
cty_is_cptr: int = 0
if cty_arg3 is not None:
# Form 1: rawptr (Attribute)
if cty_arg3.kind() == ast.ASTKind.Attribute:
cty_at3: ast.Attribute* = ast.Attribute*(cty_arg3)
if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0:
if cty_at3.value is not None and cty_at3.value.kind() == ast.ASTKind.Name:
cty_nm3: ast.Name* = ast.Name*(cty_at3.value)
if cty_nm3.id is not None and string.strcmp(cty_nm3.id, "t") == 0: cty_is_cptr = 1
# Form 2: bare CPtr
elif cty_arg3.kind() == ast.ASTKind.Name:
cty_bn3: ast.Name* = ast.Name*(cty_arg3)
if cty_bn3.id is not None and string.strcmp(cty_bn3.id, "CPtr") == 0: cty_is_cptr = 1
if cty_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
match ctype_target:
case mlirlite.LLVMType.Void():
ctype_target = mlirlite.Int8(pool)
case _:
pass
ctype_target = mlirlite.Ptr(pool, ctype_target)
cty_arg0: ast.AST* = cargs.get(0)
if cty_arg0 is not None:
cty_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cty_arg0, funcs_ptr, func_count, trans)
if cty_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cty_val, ctype_target, cty_arg0, trans)
# t.CType confirmed: even if type resolution fails, do not fall through to a cross-module FATAL
return None
# CConst is a const marker: CConst(val, rawptr) -> Ptr(i8) (const void*). Without
# this it emits an undefined @CConst; single-arg CConst(x) returns the value unchanged.
if string.strcmp(func_name, "CConst") == 0:
if can >= 2:
cconst_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
cconst_arg: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cconst_arg is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cconst_arg, cconst_ptr_ty, cargs.get(0), trans)
elif can == 1: return self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
return None
target_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
if target_ty is not None:
tc_effective_can: sizet = can
# Two-arg form: int(val, rawptr) -> Ptr(target_ty). void* is illegal in LLVM,
# so CVoid* must be represented as i8*.
if can == 2:
tc_arg2_node: ast.AST* = cargs.get(1)
tc_arg2_is_cptr: int = 0
if tc_arg2_node is not None:
# Form 1: rawptr (Attribute form)
if tc_arg2_node.kind() == ast.ASTKind.Attribute:
tc_arg2_at: ast.Attribute* = ast.Attribute*(tc_arg2_node)
if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0:
if tc_arg2_at.value is not None and tc_arg2_at.value.kind() == ast.ASTKind.Name:
tc_arg2_nm: ast.Name* = ast.Name*(tc_arg2_at.value)
if tc_arg2_nm.id is not None and string.strcmp(tc_arg2_nm.id, "t") == 0: tc_arg2_is_cptr = 1
# Form 2: bare CPtr (usable after `from t import CPtr`, e.g. t.CXXX(x, CPtr))
elif tc_arg2_node.kind() == ast.ASTKind.Name:
tc_arg2_bn: ast.Name* = ast.Name*(tc_arg2_node)
if tc_arg2_bn.id is not None and string.strcmp(tc_arg2_bn.id, "CPtr") == 0: tc_arg2_is_cptr = 1
if tc_arg2_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
match target_ty:
case mlirlite.LLVMType.Void():
target_ty = mlirlite.Int8(pool)
case _:
pass
target_ty = mlirlite.Ptr(pool, target_ty)
tc_effective_can = 1
if tc_effective_can == 1:
tc_arg_node: ast.AST* = cargs.get(0)
if tc_arg_node is not None:
# Pass target_ty as the 8th arg: otherwise constants pick their width from
# the i32 range and uint64(-1) materializes as i32 -1 then zext (N1).
tc_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tc_arg_node, None, 0, trans, target_ty)
if tc_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, tc_arg_val, target_ty, tc_arg_node, trans)
# t.XXX cast confirmed but arg shape unsupported: return None, do not fall through to FATAL
return None
"""Builtin / typedef type casts; returns None when unmatched."""
# Module-qualified typedef cast (posix.mode_t(mode), win32.win32base.HANDLE(v), ...): func is
# Attribute(Name(module), typedef); non-typedef names still go to the module function call.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
mt_at: ast.Attribute* = ast.Attribute*(cl.func)
if mt_at.value is not None and mt_at.value.kind() == ast.ASTKind.Name:
mt_mod_nm: ast.Name* = ast.Name*(mt_at.value)
if mt_mod_nm.id is not None and string.strcmp(mt_mod_nm.id, "t") != 0 and string.strcmp(mt_mod_nm.id, "c") != 0:
mod_td_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
if mt_mod_nm.id is not None:
mod_td_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, func_name)
if mod_td_ty is None:
mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(func_name)
if mod_td_str is not None and self.Gen.TypeGenCType.is_ctypedef_found() != 0:
@@ -483,26 +360,26 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
mt_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans, mod_td_ty)
if mt_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, mt_arg_val, mod_td_ty, cargs.get(0), trans)
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all CTypedef names and t/c primitives
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all CTypedef names and builtin primitives
# are castable. In generic contexts T(x) first resolves T, then goes through resolve_typedef_or_map.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
bare_ty: mlirlite.LLVMType* = None
# Builtin names resolved from generic context (T->"double"/"float"): map_t_type only knows
# t.CDouble, and float must map to t.CFloat (f32), not f64.
# Builtin names resolved from generic context (T->"double"/"float"): float must map to
# f32, not f64.
if string.strcmp(bare_fn_name, "double") == 0: bare_ty = mlirlite.Double(pool)
elif string.strcmp(bare_fn_name, "float") == 0: bare_ty = mlirlite.Float(pool)
else:
# Prefer the CTypedef dynamic table (library typedef casts); on miss fall back to
# map_t_type, which (after phase-2 convergence) no longer contains stdint aliases.
# map_builtin_type.
bare_imp_c: str = None
bare_fi_c: str = None
if trans is not None:
bare_imp_c = trans._imported_modules
bare_fi_c = trans._from_imports
bare_ty = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, bare_fn_name, bare_imp_c, bare_fi_c)
# Names substituted from generic context (T->"int") are unknown to map_t_type, so fall
# back to resolve_annotation_type (supports t.CXxx / struct class names / aliases).
# Names substituted from generic context (T->"int") are unknown to map_builtin_type, so fall
# back to resolve_annotation_type (supports builtin names / struct class names / aliases).
if bare_ty is None and string.strcmp(bare_fn_name, func_name) != 0:
gp_ast: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, bare_fn_name, 0)
if gp_ast is not None:
@@ -518,17 +395,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
bare_arg2_node: ast.AST* = cargs.get(1)
bare_arg2_is_cptr: int = 0
if bare_arg2_node is not None:
# Form 1: rawptr (Attribute form)
if bare_arg2_node.kind() == ast.ASTKind.Attribute:
bare_arg2_at: ast.Attribute* = ast.Attribute*(bare_arg2_node)
if bare_arg2_at.attr is not None and string.strcmp(bare_arg2_at.attr, "CPtr") == 0:
if bare_arg2_at.value is not None and bare_arg2_at.value.kind() == ast.ASTKind.Name:
bare_arg2_nm: ast.Name* = ast.Name*(bare_arg2_at.value)
if bare_arg2_nm.id is not None and string.strcmp(bare_arg2_nm.id, "t") == 0: bare_arg2_is_cptr = 1
# Form 2: bare CPtr (usable after `from t import CPtr`)
elif bare_arg2_node.kind() == ast.ASTKind.Name:
# Builtin pointer marker: CPtr / rawptr
if bare_arg2_node.kind() == ast.ASTKind.Name:
bare_arg2_bn: ast.Name* = ast.Name*(bare_arg2_node)
if bare_arg2_bn.id is not None and string.strcmp(bare_arg2_bn.id, "CPtr") == 0: bare_arg2_is_cptr = 1
if bare_arg2_bn.id is not None and (string.strcmp(bare_arg2_bn.id, "CPtr") == 0 or string.strcmp(bare_arg2_bn.id, "rawptr") == 0): bare_arg2_is_cptr = 1
if bare_arg2_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
match bare_ty:
@@ -668,8 +538,8 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if szf_mod is not None:
string.strncpy(szf_mod, sz_full_name, szf_mod_len)
szf_mod[szf_mod_len] = '\0'
# Exempt t/c builtins (int etc. are already handled by the builtin/size table)
if string.strcmp(szf_mod, "t") != 0 and string.strcmp(szf_mod, "c") != 0:
# Module-qualified builtin names are already handled by the builtin/size table.
if szf_mod is not None:
szf_sha1: str = self.Gen.StructGen._resolve_module_sha1(szf_mod, trans._from_imports if trans is not None else None)
if szf_sha1 is not None:
szf_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(sz_cls_name, szf_sha1)
@@ -1247,8 +1117,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if expr_root is not None and expr_root.kind() == ast.ASTKind.Name:
expr_root_nm: str = ast.Name*(expr_root).id
if expr_root_nm is not None:
if string.strcmp(expr_root_nm, "t") == 0 or string.strcmp(expr_root_nm, "c") == 0: is_mod_path_expr = 1
elif trans._imported_modules is not None:
if trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, expr_root_nm) != 0: is_mod_path_expr = 1
if evk != ast.ASTKind.Name and is_mod_path_expr == 0:
# Translate the receiver expression (the result should be Ptr(Struct))
+2 -2
View File
@@ -1291,8 +1291,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name)
call_name = mangled
else:
# t and c are internal libraries exempt from SHA1 lookup: use bare-name calls.
if string.strcmp(xmod_path, "t") == 0 or string.strcmp(xmod_path, "c") == 0: call_name = func_name
# Only real modules reach this path; a missing module segment falls back to a bare name.
if xmod_path is None: call_name = func_name
else:
# Check whether it is an imported module name (e.g. BuildPipeline.run_pipeline): if imported
# but its SHA1 is unregistered the module was not compiled, so error out instead of a misleading from_imports lookup.