Compare commits
4
Commits
3242560d02
...
37c15c9aa0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37c15c9aa0 | ||
|
|
850f61dc22 | ||
|
|
82a735c367 | ||
|
|
42b094a575 |
No files matched your search
@@ -440,8 +440,6 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
|
||||
nm[tl] = 0
|
||||
is_last: int = 0
|
||||
if idx + 1 >= token_count: is_last = 1
|
||||
builtin: int = 0
|
||||
if string.strcmp(nm, "t") == 0 or string.strcmp(nm, "c") == 0: builtin = 1
|
||||
dep_sha1: str = _PhaseAResolveDepSha1(nm)
|
||||
txt_ok: int = 0
|
||||
if dep_sha1 is not None: txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1)
|
||||
@@ -449,10 +447,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
|
||||
# stack pointer for a literal -> printf %s crashes and the graph output is swallowed)
|
||||
br_1: str = "|-- "
|
||||
br_2: str = "`-- "
|
||||
if builtin != 0:
|
||||
if is_last != 0: stdio.printf("%s%s%s (builtin)\n", prefix, br_2, nm)
|
||||
else: stdio.printf("%s%s%s (builtin)\n", prefix, br_1, nm)
|
||||
elif dep_sha1 is None:
|
||||
if dep_sha1 is None:
|
||||
if is_last != 0: stdio.printf("%s%s%s (unresolved)\n", prefix, br_2, nm)
|
||||
else: stdio.printf("%s%s%s (unresolved)\n", prefix, br_1, nm)
|
||||
elif txt_ok != 0:
|
||||
@@ -463,7 +458,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
|
||||
else: stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_1, nm, dep_sha1)
|
||||
# Drill down: MISS follows the whole chain (to PA_GRAPH_DEPTH_MAX); OK probes 1 level only
|
||||
can_expand: int = 0
|
||||
if dep_sha1 is not None and builtin == 0:
|
||||
if dep_sha1 is not None:
|
||||
if txt_ok == 0:
|
||||
if depth + 1 < PA_GRAPH_DEPTH_MAX: can_expand = 1
|
||||
else:
|
||||
|
||||
@@ -212,7 +212,20 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
if at.attr is not None: pos = _PyiEmit(buf, buf_size, pos, at.attr)
|
||||
return pos
|
||||
|
||||
# Subscript: subscripted type (e.g. list[int], t.CArray[elem, count])
|
||||
# Call: attribute marker with arguments (e.g. attr.aligned(16), attr.section(".x"))
|
||||
if k == ast.ASTKind.Call:
|
||||
cl_t: ast.Call* = ast.Call*(annot)
|
||||
if cl_t.func is None: return pos
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, cl_t.func, depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
if cl_t.args is not None:
|
||||
for ai_t in range(int(len(cl_t.args))):
|
||||
if ai_t > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, cl_t.args.get(ai_t), depth + 1)
|
||||
pos = _PyiEmit(buf, buf_size, pos, ")")
|
||||
return pos
|
||||
|
||||
# Subscript: subscripted type (e.g. list[int], CArray[elem, count])
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1)
|
||||
@@ -776,27 +789,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
tree_children: list[ast.AST*]* = tree.children
|
||||
n: sizet = len(tree_children)
|
||||
|
||||
# Pass 1: detect import t/c
|
||||
has_import_t: int = 0
|
||||
has_import_c: int = 0
|
||||
for i in range(int(n)):
|
||||
stmt: ast.AST* = tree_children.get(i)
|
||||
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
|
||||
imp: ast.Import* = ast.Import*(stmt)
|
||||
if imp.names is not None:
|
||||
imp_names: list[ast.AST*]* = imp.names
|
||||
an: sizet = len(imp_names)
|
||||
for ai in range(int(an)):
|
||||
al: ast.Alias* = ast.Alias*(imp_names.get(ai))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "t") == 0: has_import_t = 1
|
||||
elif string.strcmp(al.name, "c") == 0: has_import_c = 1
|
||||
|
||||
# Add missing import t/c
|
||||
if has_import_t == 0 and has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import t, c\n\n")
|
||||
elif has_import_t == 0: pos = _PyiEmit(buf, buf_size, pos, "import t\n\n")
|
||||
elif has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import c\n\n")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
# t / c are retired compiler-internal modules: the stub never injects their imports.
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# Pass 2: emit declarations
|
||||
for i in range(int(n)):
|
||||
@@ -886,12 +880,9 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
# If there is a value (e.g. a CDefine constant) emit = value.
|
||||
# CTypedef is special: its value is a type expression, rendered by _PyiTypeStr.
|
||||
is_ctypedef_ann: int = 0
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Attribute:
|
||||
ctd_at: ast.Attribute* = ast.Attribute*(aa.annotation)
|
||||
if ctd_at.attr is not None and string.strcmp(ctd_at.attr, "CTypedef") == 0:
|
||||
if ctd_at.value is not None and ctd_at.value.kind() == ast.ASTKind.Name:
|
||||
ctd_mod: ast.Name* = ast.Name*(ctd_at.value)
|
||||
if ctd_mod.id is not None and string.strcmp(ctd_mod.id, "t") == 0: is_ctypedef_ann = 1
|
||||
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Name:
|
||||
ctd_nm: ast.Name* = ast.Name*(aa.annotation)
|
||||
if ctd_nm.id is not None and string.strcmp(ctd_nm.id, "CTypedef") == 0: is_ctypedef_ann = 1
|
||||
if is_ctypedef_ann != 0 and aa.value is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " = ")
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, aa.value)
|
||||
|
||||
@@ -1631,6 +1631,49 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
|
||||
# vpi caches `-> None` verbatim as "None"; map None to Void so cross-module
|
||||
# `-> None` methods do not fail fast with an unresolvable return type.
|
||||
if string.strcmp(seg, "None") == 0: return mlirlite.Void(pool)
|
||||
# Descriptor / storage / value-reference marker wrappers: CConst[T], State[T], CExport[T], ...
|
||||
# The marker carries no value type, so resolve the wrapped type.
|
||||
if seg_len_ps > 0 and seg[seg_len_ps - 1] == ']':
|
||||
lb_mk: str = string.strchr(seg, '[')
|
||||
if lb_mk is not None:
|
||||
lb_off: sizet = sizet(uint64(lb_mk) - uint64(seg))
|
||||
if lb_off > 0 and seg_len_ps > lb_off + 1:
|
||||
mk_len: sizet = lb_off
|
||||
mk_buf: bytes = stdlib.malloc(mk_len + 1)
|
||||
if mk_buf is not None:
|
||||
string.strncpy(mk_buf, seg, mk_len)
|
||||
mk_buf[mk_len] = '\0'
|
||||
mk_name: str = mk_buf
|
||||
# `attr.<name>` / `attr.<name>(...)` markers are builtin descriptors.
|
||||
is_attr_marker: int = 0
|
||||
if string.strncmp(mk_name, "attr.", 5) == 0 or string.strcmp(mk_name, "attr") == 0: is_attr_marker = 1
|
||||
dot_mk: str = string.strrchr(mk_name, '.')
|
||||
if dot_mk is not None: mk_name = dot_mk + 1
|
||||
is_marker: int = is_attr_marker
|
||||
if string.strcmp(mk_name, "CConst") == 0 or string.strcmp(mk_name, "CVolatile") == 0 \
|
||||
or string.strcmp(mk_name, "BigEndian") == 0 or string.strcmp(mk_name, "LittleEndian") == 0 \
|
||||
or string.strcmp(mk_name, "CStatic") == 0 or string.strcmp(mk_name, "CExport") == 0 \
|
||||
or string.strcmp(mk_name, "CExtern") == 0 or string.strcmp(mk_name, "State") == 0 \
|
||||
or string.strcmp(mk_name, "CDefine") == 0 or string.strcmp(mk_name, "CRef") == 0 \
|
||||
or string.strcmp(mk_name, "CTempRef") == 0 or string.strcmp(mk_name, "CAutoPtr") == 0 \
|
||||
or string.strcmp(mk_name, "attr") == 0:
|
||||
is_marker = 1
|
||||
if is_marker != 0:
|
||||
inner_len_mk: sizet = seg_len_ps - lb_off - 2
|
||||
inner_mk: bytes = stdlib.malloc(inner_len_mk + 1)
|
||||
if inner_mk is not None:
|
||||
string.strncpy(inner_mk, seg + lb_off + 1, inner_len_mk)
|
||||
inner_mk[inner_len_mk] = '\0'
|
||||
inner_ty_mk: mlirlite.LLVMType* = _resolve_single_vpi_type_str(pool, inner_mk)
|
||||
stdlib.free(inner_mk)
|
||||
# Value-reference parameters use the pointer ABI: &T / &temp T -> T*.
|
||||
if inner_ty_mk is not None and (string.strcmp(mk_name, "CRef") == 0 or string.strcmp(mk_name, "CTempRef") == 0):
|
||||
inner_ty_mk = mlirlite.Ptr(pool, inner_ty_mk)
|
||||
elif string.strcmp(mk_name, "CAutoPtr") == 0:
|
||||
inner_ty_mk = i8_ptr_ty
|
||||
stdlib.free(mk_buf)
|
||||
return inner_ty_mk
|
||||
stdlib.free(mk_buf)
|
||||
# rawptr[T] built-in raw pointer (equivalent to t.CPtr[T] -> Ptr(T)); vpi keeps the
|
||||
# Subscript form verbatim. Same as TypeGenResolve's rawptr branch: Ptr(resolve(T)).
|
||||
if string.strncmp(seg, "rawptr[", 7) == 0:
|
||||
@@ -1645,10 +1688,10 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
|
||||
stdlib.free(inner_buf_rp)
|
||||
if inner_ty_rp is not None: return mlirlite.Ptr(pool, inner_ty_rp)
|
||||
return None
|
||||
# t.CPtr[T] / c.CPtr[T] subscript pointers (nesting allowed, e.g. t.CPtr[t.CPtr]
|
||||
# -> Ptr(Ptr(i8))); same as TypeGenResolve's Subscript(CPtr) branch: Ptr(resolve(T)).
|
||||
# CPtr[T] subscript pointers (nesting allowed, e.g. CPtr[CPtr] -> Ptr(Ptr(i8)));
|
||||
# same as TypeGenResolve's Subscript(CPtr) branch: Ptr(resolve(T)).
|
||||
cptr_sub_pfx: sizet = 0
|
||||
if string.strncmp(seg, "t.CPtr[", 7) == 0 or string.strncmp(seg, "c.CPtr[", 7) == 0: cptr_sub_pfx = 7
|
||||
if string.strncmp(seg, "CPtr[", 6) == 0: cptr_sub_pfx = 6
|
||||
if cptr_sub_pfx > 0:
|
||||
seg_len_cp: sizet = string.strlen(seg)
|
||||
if seg_len_cp > cptr_sub_pfx + 1 and seg[seg_len_cp - 1] == ']':
|
||||
@@ -1661,15 +1704,12 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
|
||||
stdlib.free(inner_buf_cp)
|
||||
if inner_ty_cp is not None: return mlirlite.Ptr(pool, inner_ty_cp)
|
||||
return None
|
||||
# t.XXX / c.XXX built-in type mapping
|
||||
if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0:
|
||||
attr_nm: str = seg + 2
|
||||
# t.CExport is an export marker, not a real type; FuncGenDef maps it to Void,
|
||||
# Bare builtin type names (int8 / uint64 / sizet / CPtr / CExport ...)
|
||||
if string.strchr(seg, '.') is None:
|
||||
# CExport is an export marker, not a real type; FuncGenDef maps it to Void,
|
||||
# so normalize it here too or cross-module return types fail fast.
|
||||
if string.strcmp(attr_nm, "CExport") == 0: return mlirlite.Void(pool)
|
||||
# [stage 2] unified: CTypedef dynamic table first (library typedefs analyzed normally),
|
||||
# on miss fall back to map_t_type (t/c library internal special-casing).
|
||||
mapped_ty: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, attr_nm, None, None)
|
||||
if string.strcmp(seg, "CExport") == 0: return mlirlite.Void(pool)
|
||||
mapped_ty: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None)
|
||||
if mapped_ty is not None: return mapped_ty
|
||||
return None
|
||||
# module-qualified class name "memhub.MemBuddy" -> use last segment to look up struct
|
||||
@@ -1689,7 +1729,7 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
|
||||
# bare enum name -> EnumTable fallback (as above: enums are not in StructTable)
|
||||
bare_enum_ty: mlirlite.LLVMType* = GenApi.get_enum_base_type(seg)
|
||||
if bare_enum_ty is not None: return bare_enum_ty
|
||||
# bare typedef name (e.g. VOIDPTR): CTypedef dynamic table first, on miss fall back to map_t_type
|
||||
# bare typedef name (e.g. VOIDPTR): CTypedef dynamic table first, on miss fall back to map_builtin_type
|
||||
mapped_bare: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None)
|
||||
if mapped_bare is not None: return mapped_bare
|
||||
return None
|
||||
@@ -1700,6 +1740,8 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
|
||||
def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: mlirlite.LLVMType*, i8_ptr_ty: mlirlite.LLVMType*) -> mlirlite.LLVMType*:
|
||||
"""Resolve a vpi return-annotation string directly to LLVMType without building AST nodes."""
|
||||
if ret_annot is None or pool is None: return None
|
||||
# `T1 | T2` unions were retired; a stub containing one must be regenerated.
|
||||
if string.strchr(ret_annot, '|') is not None: return None
|
||||
|
||||
# tuple[T1, T2, ...] -> anonymous struct {T1, T2, ...} (multi-return packing),
|
||||
# matching TypeGenMixin._resolve_generic_subscript_ty's tuple branch.
|
||||
@@ -1793,7 +1835,7 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m
|
||||
seg_buf[seg_len2] = '\0'
|
||||
# CPtr subscript segments must be recognized first: the definition side treats
|
||||
# `X*[T]` as "CPtr side contributes N pointer levels, the other side the base".
|
||||
if string.strncmp(seg_buf, "t.CPtr[", 7) == 0 or string.strncmp(seg_buf, "c.CPtr[", 7) == 0:
|
||||
if string.strncmp(seg_buf, "CPtr[", 6) == 0:
|
||||
seg_ty_cs: mlirlite.LLVMType* = _resolve_single_vpi_type_str(pool, seg_buf)
|
||||
if seg_ty_cs is not None:
|
||||
# Count outer Ptr nesting (same as the definition side). Do not update the
|
||||
@@ -1813,10 +1855,10 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m
|
||||
if lvl_cs > cptr_sub_lvl:
|
||||
cptr_sub_lvl = lvl_cs
|
||||
cptr_sub_ty = seg_ty_cs
|
||||
elif string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1
|
||||
elif string.strcmp(seg_buf, "t.CRef") == 0 or string.strcmp(seg_buf, "CRef") == 0: has_cref = 1
|
||||
elif string.strcmp(seg_buf, "t.CTempRef") == 0 or string.strcmp(seg_buf, "CTempRef") == 0: has_cref = 1
|
||||
elif string.strcmp(seg_buf, "void") == 0 or string.strcmp(seg_buf, "CVoid") == 0 or string.strcmp(seg_buf, "void") == 0:
|
||||
elif string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1
|
||||
elif string.strcmp(seg_buf, "CRef") == 0: has_cref = 1
|
||||
elif string.strcmp(seg_buf, "CTempRef") == 0: has_cref = 1
|
||||
elif string.strcmp(seg_buf, "void") == 0 or string.strcmp(seg_buf, "CVoid") == 0:
|
||||
# Bare void is an alias of t.CVoid and must be handled the same, else the
|
||||
# else branch sets first_td and produces the illegal Ptr(Void) (void* = i8*).
|
||||
has_cvoid = 1
|
||||
@@ -2614,9 +2656,9 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int:
|
||||
if deco is None: continue
|
||||
if deco.kind() == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue
|
||||
at0: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
|
||||
at0: ast.Name* = ast.Name*(cl.func)
|
||||
if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(int(an)):
|
||||
@@ -3527,12 +3569,7 @@ def _repl_stub_deco_flags(deco_list: list[ast.AST*]*) -> int:
|
||||
if deco is None: continue
|
||||
nm: str = None
|
||||
k: int = deco.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(deco)
|
||||
if at.attr is not None and at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is not None and string.strcmp(vn.id, "t") == 0: nm = at.attr
|
||||
elif k == ast.ASTKind.Name:
|
||||
if k == ast.ASTKind.Name:
|
||||
nn: ast.Name* = ast.Name*(deco)
|
||||
nm = nn.id
|
||||
if nm is None: continue
|
||||
|
||||
+163
-124
@@ -23,19 +23,20 @@ novirtual class AssignGenMixin:
|
||||
|
||||
RecoverFound: int
|
||||
|
||||
# Check whether an annotation is t.CDefine (module-level function)
|
||||
# Check whether an annotation is the CDefine marker (module-level function)
|
||||
def is_cdefine_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is t.CDefine, else 0."""
|
||||
"""Return 1 if the annotation is the CDefine marker (bare or CDefine[T]), else 0."""
|
||||
if annot is None: return 0
|
||||
k: int = annot.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(annot)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0: return 1
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
if self.is_cdefine_annotation(bop.left) != 0: return 1
|
||||
if self.is_cdefine_annotation(bop.right) != 0: return 1
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cd: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_cd.value is not None and sub_cd.value.kind() == ast.ASTKind.Name:
|
||||
mk_cd: ast.Name* = ast.Name*(sub_cd.value)
|
||||
if mk_cd.id is not None and string.strcmp(mk_cd.id, "CDefine") == 0: return 1
|
||||
return 0
|
||||
|
||||
# extract_cdefine_int_value - extract an integer constant from AnnAssign.value
|
||||
@@ -325,26 +326,28 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
|
||||
|
||||
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
|
||||
"""Return 1 if the node is a t.CDefine marker node."""
|
||||
"""Return 1 if the node is the CDefine marker node (bare or CDefine[T])."""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cm: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_cm.value is not None and sub_cm.value.kind() == ast.ASTKind.Name:
|
||||
mk_cm: ast.Name* = ast.Name*(sub_cm.value)
|
||||
if mk_cm.id is not None and string.strcmp(mk_cm.id, "CDefine") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _define_explicit_annot(self, annot: ast.AST*) -> ast.AST*:
|
||||
"""Return the explicit type T of a `T | t.CDefine` annotation, or None."""
|
||||
"""Return the explicit type T of a `CDefine[T]` annotation, or None."""
|
||||
if annot is None: return None
|
||||
if annot.kind() != ast.ASTKind.BinOp: return None
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
if bop.left is None: return None
|
||||
if self._is_cdefine_marker_node(bop.left) != 0: return None
|
||||
return bop.left
|
||||
if annot.kind() != ast.ASTKind.Subscript: return None
|
||||
sub_d: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_d.value is None or sub_d.value.kind() != ast.ASTKind.Name: return None
|
||||
mk_d: ast.Name* = ast.Name*(sub_d.value)
|
||||
if mk_d.id is None or string.strcmp(mk_d.id, "CDefine") != 0: return None
|
||||
return sub_d.slice
|
||||
|
||||
def _annot_int_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""Return the bit width of an integer annotation, or 0 if unknown."""
|
||||
@@ -357,13 +360,17 @@ novirtual class AssignGenMixin:
|
||||
elif k == ast.ASTKind.Name:
|
||||
n2: ast.Name* = ast.Name*(annot)
|
||||
nm = n2.id
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
# Descriptor wrapper (CConst[T] etc.): the width comes from the wrapped type.
|
||||
sub_ib: ast.Subscript* = ast.Subscript*(annot)
|
||||
return self._annot_int_bits(pool, sub_ib.slice)
|
||||
if nm is None: return 0
|
||||
return self._int_bits_of_type_name(pool, nm, 0)
|
||||
|
||||
def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
|
||||
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_t_type)、
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_builtin_type)、
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef
|
||||
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
|
||||
`define X: WORD = ...` 的声明宽度不参与校验/物化。
|
||||
@@ -380,7 +387,7 @@ novirtual class AssignGenMixin:
|
||||
if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0: return 32
|
||||
if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0: return 64
|
||||
if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0: return 64
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm)
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, nm)
|
||||
if ty is not None:
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Int(bits):
|
||||
@@ -426,13 +433,16 @@ novirtual class AssignGenMixin:
|
||||
elif k == ast.ASTKind.Name:
|
||||
n2: ast.Name* = ast.Name*(annot)
|
||||
nm = n2.id
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
sub_fk: ast.Subscript* = ast.Subscript*(annot)
|
||||
return self._annot_float_kind(pool, sub_fk.slice)
|
||||
if nm is None: return 0
|
||||
return self._float_kind_of_type_name(pool, nm, 0)
|
||||
|
||||
def _float_kind_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
|
||||
"""类型名 → 1 表示它是 32 位 float(f32),0 表示 double 或非浮点/未知。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_t_type)、用户/include typedef(递归展开)。
|
||||
覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_builtin_type)、用户/include typedef(递归展开)。
|
||||
"""
|
||||
if nm is None or depth > 10: return 0
|
||||
# 去掉模块/类限定前缀:`t.CFloat` → `CFloat`;`stdint.float32` → `float32`
|
||||
@@ -441,7 +451,7 @@ novirtual class AssignGenMixin:
|
||||
return self._float_kind_of_type_name(pool, dot + 1, depth + 1)
|
||||
if string.strcmp(nm, "float") == 0: return 1
|
||||
if string.strcmp(nm, "float32") == 0: return 1
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm)
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, nm)
|
||||
if ty is not None:
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Float(bits):
|
||||
@@ -914,28 +924,20 @@ novirtual class AssignGenMixin:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None and string.strcmp(nm.id, name) == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
if self._annotation_contains_name(bop.left, name) != 0: return 1
|
||||
if self._annotation_contains_name(bop.right, name) != 0: return 1
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cn: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_cn.slice is not None and self._annotation_contains_name(sub_cn.slice, name) != 0: return 1
|
||||
return 0
|
||||
|
||||
def _annotation_has_cptr_marker(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation union carries a CPtr marker (the star form `<base> | rawptr`)."""
|
||||
"""Return 1 if the annotation is a CPtr pointer wrapper (the `T*` form)."""
|
||||
if annot is None: return 0
|
||||
k: int = annot.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(annot)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CPtr") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
if self._annotation_has_cptr_marker(bop.left) != 0: return 1
|
||||
if self._annotation_has_cptr_marker(bop.right) != 0: return 1
|
||||
if annot.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub_cp: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_cp.value is not None and sub_cp.value.kind() == ast.ASTKind.Name:
|
||||
nm_cp: ast.Name* = ast.Name*(sub_cp.value)
|
||||
if nm_cp.id is not None and (string.strcmp(nm_cp.id, "CPtr") == 0 or string.strcmp(nm_cp.id, "rawptr") == 0): return 1
|
||||
if sub_cp.slice is not None: return self._annotation_has_cptr_marker(sub_cp.slice)
|
||||
return 0
|
||||
|
||||
# _is_ptr_element_annotation - check whether an annotation is bytes* or str*
|
||||
@@ -943,34 +945,36 @@ novirtual class AssignGenMixin:
|
||||
def _is_ptr_element_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is a bytes* or str* form."""
|
||||
if annot is None: return 0
|
||||
k: int = annot.kind()
|
||||
if k != ast.ASTKind.BinOp: return 0
|
||||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||||
if bop.op != ast.OpKind.BitOr: return 0
|
||||
# str/bytes are already i8*; only the union WITH a pointer marker (str*/bytes*) is i8** and
|
||||
# needs 8-byte element slots. A union that merely carries them (e.g. `const str`, which is
|
||||
# t.CConst | str and stays i8*) must keep the 1-byte stride.
|
||||
if self._annotation_has_cptr_marker(annot) != 0:
|
||||
if self._annotation_contains_name(annot, "str") != 0: return 1
|
||||
if self._annotation_contains_name(annot, "bytes") != 0: return 1
|
||||
# Generic type param / nested union | t.CPtr: scalars use their own size, pointers use 8-byte slots
|
||||
if self._is_generic_tp_or_nested(bop.left) != 0 or self._is_generic_tp_or_nested(bop.right) != 0:
|
||||
gp_nm: str = None
|
||||
if bop.left is not None and bop.left.kind() == ast.ASTKind.Name:
|
||||
gp_left_nm: ast.Name* = ast.Name*(bop.left)
|
||||
gp_nm = gp_left_nm.id
|
||||
elif bop.right is not None and bop.right.kind() == ast.ASTKind.Name:
|
||||
gp_right_nm: ast.Name* = ast.Name*(bop.right)
|
||||
gp_nm = gp_right_nm.id
|
||||
if annot.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub_pe: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_pe.value is None or sub_pe.value.kind() != ast.ASTKind.Name: return 0
|
||||
pe_nm: ast.Name* = ast.Name*(sub_pe.value)
|
||||
if pe_nm.id is None: return 0
|
||||
# Descriptor / storage / ref wrapper: the pointer form lives in the wrapped type.
|
||||
if string.strcmp(pe_nm.id, "CConst") == 0 or string.strcmp(pe_nm.id, "CVolatile") == 0 \
|
||||
or string.strcmp(pe_nm.id, "BigEndian") == 0 or string.strcmp(pe_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CStatic") == 0 or string.strcmp(pe_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CExtern") == 0 or string.strcmp(pe_nm.id, "State") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CDefine") == 0 or string.strcmp(pe_nm.id, "CRef") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CTempRef") == 0 or string.strcmp(pe_nm.id, "CAutoPtr") == 0:
|
||||
return self._is_ptr_element_annotation(sub_pe.slice)
|
||||
if string.strcmp(pe_nm.id, "CPtr") != 0 and string.strcmp(pe_nm.id, "rawptr") != 0: return 0
|
||||
inner_pe: ast.AST* = sub_pe.slice
|
||||
if inner_pe is None: return 0
|
||||
# str/bytes are already i8*; the pointer layer makes them i8** and needs 8-byte slots.
|
||||
if self._annotation_contains_name(inner_pe, "str") != 0: return 1
|
||||
if self._annotation_contains_name(inner_pe, "bytes") != 0: return 1
|
||||
# Generic type param: a scalar-typed param uses its own size, a pointer/aggregate uses 8 bytes.
|
||||
if inner_pe.kind() == ast.ASTKind.Name:
|
||||
inner_nm_pe: ast.Name* = ast.Name*(inner_pe)
|
||||
gp_nm: str = inner_nm_pe.id
|
||||
if gp_nm is not None:
|
||||
# Resolve the generic param via the global gen (direct field access breaks static binding)
|
||||
gp_resolved: str = self.Gen.TypeGenBase.resolve_generic_type_name(gp_nm)
|
||||
if gp_resolved is not None and string.strcmp(gp_resolved, gp_nm) != 0:
|
||||
# Generic param resolved to a scalar => use scalar size, not an 8-byte pointer slot
|
||||
if self._is_scalar_ty_name(gp_resolved) != 0: return 0
|
||||
return 1
|
||||
# 64-bit scalar | CPtr: element is 8 bytes, so use an 8-byte stride (else GEP truncation)
|
||||
if self._is_64bit_scalar_ty(bop.left) != 0 or self._is_64bit_scalar_ty(bop.right) != 0: return 1
|
||||
if gp_resolved is not None: return 1
|
||||
# 64-bit scalar pointee: element is 8 bytes (else GEP truncation).
|
||||
if self._is_64bit_scalar_ty(inner_pe) != 0: return 1
|
||||
return 0
|
||||
|
||||
def _stg_ty_equal(self, pool: memhub.MemBuddy*, ta: mlirlite.LLVMType*, tb: mlirlite.LLVMType*) -> int:
|
||||
@@ -1502,34 +1506,22 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
def _is_addrlike_call(self, node: ast.AST*) -> int:
|
||||
"""Return 1 if the rhs is an explicit pointer expression (c.Addr/c.Deref/c.Cast/c.Ptr call, or new-syntax `&x` / `*p`)."""
|
||||
"""Return 1 if the rhs is an explicit pointer expression (`&x` / `*p`).
|
||||
"""
|
||||
if node is None: return 0
|
||||
# New syntax (R3/R4): `&x` and `*p` parse as UnaryOp(AddrOf) / UnaryOp(Deref) and replace the
|
||||
# c.Addr / c.Deref call forms. A pointer-valued RHS must not take the module-global
|
||||
# "object with static storage -> copy the object" branch, so both forms count as addrlike.
|
||||
# `&x` and `*p` parse as UnaryOp(AddrOf) / UnaryOp(Deref). A pointer-valued RHS must not
|
||||
# take the module-global "object with static storage -> copy the object" branch.
|
||||
if node.kind() == ast.ASTKind.UnaryOp:
|
||||
uo_al: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uo_al.op == ast.OpKind.AddrOf: return 1
|
||||
if uo_al.op == ast.OpKind.Deref: return 1
|
||||
return 0
|
||||
if node.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at.value is None or at.value.kind() != ast.ASTKind.Name: return 0
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is None or string.strcmp(vn.id, "c") != 0: return 0
|
||||
if at.attr is None: return 0
|
||||
if string.strcmp(at.attr, "Addr") == 0: return 1
|
||||
if string.strcmp(at.attr, "Deref") == 0: return 1
|
||||
if string.strcmp(at.attr, "Cast") == 0: return 1
|
||||
if string.strcmp(at.attr, "Ptr") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _is_addr_of_expr(self, node: ast.AST*) -> int:
|
||||
"""Return 1 only for an explicit address-of expression (`&x` / `c.Addr(x)`).
|
||||
"""Return 1 only for an explicit address-of expression (`&x`).
|
||||
|
||||
Distinct from _is_addrlike_call, which also counts deref/cast forms: assigning a *deref*
|
||||
Distinct from _is_addrlike_call, which also counts deref forms: assigning a *deref*
|
||||
result (`*p`) to a value-typed variable is a legitimate value copy, so only `&x` indicates
|
||||
a missing `*` in the annotation.
|
||||
"""
|
||||
@@ -1538,14 +1530,6 @@ novirtual class AssignGenMixin:
|
||||
uo_ao: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uo_ao.op == ast.OpKind.AddrOf: return 1
|
||||
return 0
|
||||
if node.kind() != ast.ASTKind.Call: return 0
|
||||
cl_ao: ast.Call* = ast.Call*(node)
|
||||
if cl_ao.func is None or cl_ao.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at_ao: ast.Attribute* = ast.Attribute*(cl_ao.func)
|
||||
if at_ao.value is None or at_ao.value.kind() != ast.ASTKind.Name: return 0
|
||||
vn_ao: ast.Name* = ast.Name*(at_ao.value)
|
||||
if vn_ao.id is None or string.strcmp(vn_ao.id, "c") != 0: return 0
|
||||
if at_ao.attr is not None and string.strcmp(at_ao.attr, "Addr") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _DerefPunBits(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int:
|
||||
@@ -1577,20 +1561,65 @@ novirtual class AssignGenMixin:
|
||||
if string.strcmp(ty_nm, "CUInt64T") == 0: return 1
|
||||
if string.strcmp(ty_nm, "CInt64T") == 0: return 1
|
||||
if string.strcmp(ty_nm, "CSizeT") == 0: return 1
|
||||
# builtin 64-bit short names
|
||||
if string.strcmp(ty_nm, "sizet") == 0: return 1
|
||||
if string.strcmp(ty_nm, "size_t") == 0: return 1
|
||||
if string.strcmp(ty_nm, "int64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "uint64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "intfast64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "uintfast64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "intleast64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "uintleast64") == 0: return 1
|
||||
if string.strcmp(ty_nm, "longlong") == 0: return 1
|
||||
if string.strcmp(ty_nm, "ulonglong") == 0: return 1
|
||||
if string.strcmp(ty_nm, "ptrdifft") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _is_scalar_ty_name(self, ty_name: str) -> int:
|
||||
"""Return 1 if the type name is a known pure scalar (whitelist)."""
|
||||
if ty_name is None: return 0
|
||||
# Strip the t./c. namespace prefix so int and bare aliases are judged the same
|
||||
if ty_name[0] == 't' and ty_name[1] == '.': ty_name += 2
|
||||
elif ty_name[0] == 'c' and ty_name[1] == '.': ty_name += 2
|
||||
if string.strchr(ty_name, '|') is not None: return 0
|
||||
if string.strchr(ty_name, '*') is not None: return 0
|
||||
if string.strcmp(ty_name, "str") == 0: return 0
|
||||
if string.strcmp(ty_name, "bytes") == 0: return 0
|
||||
if string.strcmp(ty_name, "CPtr") == 0: return 0
|
||||
if string.strstr(ty_name, "Ptr") is not None: return 0
|
||||
# builtin short type names
|
||||
if string.strcmp(ty_name, "sizet") == 0: return 1
|
||||
if string.strcmp(ty_name, "size_t") == 0: return 1
|
||||
if string.strcmp(ty_name, "int8") == 0: return 1
|
||||
if string.strcmp(ty_name, "int16") == 0: return 1
|
||||
if string.strcmp(ty_name, "int32") == 0: return 1
|
||||
if string.strcmp(ty_name, "int64") == 0: return 1
|
||||
if string.strcmp(ty_name, "uint8") == 0: return 1
|
||||
if string.strcmp(ty_name, "uint16") == 0: return 1
|
||||
if string.strcmp(ty_name, "uint32") == 0: return 1
|
||||
if string.strcmp(ty_name, "uint64") == 0: return 1
|
||||
if string.strcmp(ty_name, "intfast8") == 0: return 1
|
||||
if string.strcmp(ty_name, "intfast16") == 0: return 1
|
||||
if string.strcmp(ty_name, "intfast32") == 0: return 1
|
||||
if string.strcmp(ty_name, "intfast64") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintfast8") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintfast16") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintfast32") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintfast64") == 0: return 1
|
||||
if string.strcmp(ty_name, "intleast8") == 0: return 1
|
||||
if string.strcmp(ty_name, "intleast16") == 0: return 1
|
||||
if string.strcmp(ty_name, "intleast32") == 0: return 1
|
||||
if string.strcmp(ty_name, "intleast64") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintleast8") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintleast16") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintleast32") == 0: return 1
|
||||
if string.strcmp(ty_name, "uintleast64") == 0: return 1
|
||||
if string.strcmp(ty_name, "uchar") == 0: return 1
|
||||
if string.strcmp(ty_name, "short") == 0: return 1
|
||||
if string.strcmp(ty_name, "ushort") == 0: return 1
|
||||
if string.strcmp(ty_name, "uint") == 0: return 1
|
||||
if string.strcmp(ty_name, "long") == 0: return 1
|
||||
if string.strcmp(ty_name, "ulong") == 0: return 1
|
||||
if string.strcmp(ty_name, "longlong") == 0: return 1
|
||||
if string.strcmp(ty_name, "ulonglong") == 0: return 1
|
||||
if string.strcmp(ty_name, "ptrdifft") == 0: return 1
|
||||
# Whitelist: known pure scalars (base names + stdint uppercase aliases)
|
||||
if string.strcmp(ty_name, "int") == 0: return 1
|
||||
if string.strcmp(ty_name, "char") == 0: return 1
|
||||
@@ -1913,7 +1942,7 @@ novirtual class AssignGenMixin:
|
||||
def const_assign_msg(self, kind: str, name: str, src_name: str) -> str:
|
||||
"""Build the const-assignment error message used by const_assign_fail."""
|
||||
decl: str = "(annotation contains t.CConst; the source name at the declaration was not recorded)"
|
||||
if src_name is not None: decl = f"t.CConst | {src_name}"
|
||||
if src_name is not None: decl = f"CConst | {src_name}"
|
||||
return GenApi.escape_fstring_copy(f"cannot assign to const variable '{name}' ({kind}); " f"declaration type (including const): {decl}" f"; fix: remove the const descriptor / t.CConst from the declaration, or assign once at the declaration " f"and afterwards use only non-const copies/temporaries; a const object forbids assignment, " f"augmented assignment and element assignment within its scope, and this never degrades to a warning")
|
||||
|
||||
def const_assign_fail(self, node: ast.AST*, kind: str, name: str, src_name: str) -> int:
|
||||
@@ -2242,18 +2271,12 @@ novirtual class AssignGenMixin:
|
||||
if annot is None or annot.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub.value is None or sub.slice is None: return 0
|
||||
if sub.value.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(sub.value)
|
||||
if at.attr is None or string.strcmp(at.attr, "CArray") != 0: return 0
|
||||
if at.value is None or at.value.kind() != ast.ASTKind.Name: return 0
|
||||
mv: ast.Name* = ast.Name*(at.value)
|
||||
if mv.id is None or string.strcmp(mv.id, "t") != 0: return 0
|
||||
elif sub.value.kind() == ast.ASTKind.Name:
|
||||
# Bare CArray / builtin rawarray / builtin array are all equivalent to t.CArray
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
# Bare CArray / builtin rawarray / builtin array are all equivalent.
|
||||
bn: ast.Name* = ast.Name*(sub.value)
|
||||
if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 0): return 0
|
||||
else: return 0
|
||||
# Single-param form: slice is not a Tuple (e.g. t.CArray[u64])
|
||||
# Single-param form: slice is not a Tuple (e.g. CArray[u64])
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple: return 1
|
||||
# Two-param form with count=None is equivalent to single-param
|
||||
tup: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
@@ -2405,14 +2428,16 @@ novirtual class AssignGenMixin:
|
||||
def _prespecialize_generic_annotation(self, trans: HT.Translator*, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""Pre-specialize a generic annotation; return 1 if attempted."""
|
||||
if annot is None or trans is None: return 0
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript of A*
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by CPtr[T].
|
||||
ann_ps: ast.AST* = annot
|
||||
if ann_ps.kind() == ast.ASTKind.Subscript:
|
||||
sub_wrap_ps: ast.Subscript* = ast.Subscript*(ann_ps)
|
||||
if sub_wrap_ps.value is not None and sub_wrap_ps.value.kind() == ast.ASTKind.Name:
|
||||
wk_nm_ps: ast.Name* = ast.Name*(sub_wrap_ps.value)
|
||||
if wk_nm_ps.id is not None and (string.strcmp(wk_nm_ps.id, "CPtr") == 0 or string.strcmp(wk_nm_ps.id, "rawptr") == 0):
|
||||
if sub_wrap_ps.slice is not None: ann_ps = sub_wrap_ps.slice
|
||||
spec_annot: ast.Subscript* = None
|
||||
if annot.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(annot)
|
||||
elif annot.kind() == ast.ASTKind.BinOp:
|
||||
bop_ps: ast.BinOp* = ast.BinOp*(annot)
|
||||
if bop_ps.op == ast.OpKind.BitOr:
|
||||
if bop_ps.left is not None and bop_ps.left.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(bop_ps.left)
|
||||
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(bop_ps.right)
|
||||
if ann_ps.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(ann_ps)
|
||||
if spec_annot is None: return 0
|
||||
sv_ps: ast.AST* = spec_annot.value
|
||||
if sv_ps is None or sv_ps.kind() != ast.ASTKind.Name: return 0
|
||||
@@ -2429,14 +2454,16 @@ novirtual class AssignGenMixin:
|
||||
def _fail_on_degenerated_generic_annotation(self, aa: ast.AnnAssign*, var_name: str, annot: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Fail fast when a generic annotation degrades to a bare base pointer."""
|
||||
if aa is None or annot is None or var_ty is None: return 0
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript of A*
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by CPtr[T].
|
||||
ann_ff: ast.AST* = annot
|
||||
if ann_ff.kind() == ast.ASTKind.Subscript:
|
||||
sub_wrap_ff: ast.Subscript* = ast.Subscript*(ann_ff)
|
||||
if sub_wrap_ff.value is not None and sub_wrap_ff.value.kind() == ast.ASTKind.Name:
|
||||
wk_nm_ff: ast.Name* = ast.Name*(sub_wrap_ff.value)
|
||||
if wk_nm_ff.id is not None and (string.strcmp(wk_nm_ff.id, "CPtr") == 0 or string.strcmp(wk_nm_ff.id, "rawptr") == 0):
|
||||
if sub_wrap_ff.slice is not None: ann_ff = sub_wrap_ff.slice
|
||||
ann_sub: ast.Subscript* = None
|
||||
if annot.kind() == ast.ASTKind.Subscript: ann_sub = ast.Subscript*(annot)
|
||||
elif annot.kind() == ast.ASTKind.BinOp:
|
||||
bop_ff: ast.BinOp* = ast.BinOp*(annot)
|
||||
if bop_ff.op == ast.OpKind.BitOr:
|
||||
if bop_ff.left is not None and bop_ff.left.kind() == ast.ASTKind.Subscript: ann_sub = ast.Subscript*(bop_ff.left)
|
||||
elif bop_ff.right is not None and bop_ff.right.kind() == ast.ASTKind.Subscript: ann_sub = ast.Subscript*(bop_ff.right)
|
||||
if ann_ff.kind() == ast.ASTKind.Subscript: ann_sub = ast.Subscript*(ann_ff)
|
||||
if ann_sub is None or ann_sub.value is None or ann_sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
base_nm_ff: ast.Name* = ast.Name*(ann_sub.value)
|
||||
if base_nm_ff.id is None: return 0
|
||||
@@ -2818,11 +2845,13 @@ novirtual class AssignGenMixin:
|
||||
ann_gen_base: str = None
|
||||
if annot is not None:
|
||||
ag_side: ast.AST* = annot
|
||||
if ag_side.kind() == ast.ASTKind.BinOp:
|
||||
ag_bop: ast.BinOp* = ast.BinOp*(ag_side)
|
||||
if ag_bop.op == ast.OpKind.BitOr:
|
||||
if ag_bop.left is not None and ag_bop.left.kind() == ast.ASTKind.Subscript: ag_side = ag_bop.left
|
||||
elif ag_bop.right is not None and ag_bop.right.kind() == ast.ASTKind.Subscript: ag_side = ag_bop.right
|
||||
# Unwrap a CPtr[T] pointer layer to inspect the generic base.
|
||||
if ag_side.kind() == ast.ASTKind.Subscript:
|
||||
ag_wrap: ast.Subscript* = ast.Subscript*(ag_side)
|
||||
if ag_wrap.value is not None and ag_wrap.value.kind() == ast.ASTKind.Name:
|
||||
ag_wnm: ast.Name* = ast.Name*(ag_wrap.value)
|
||||
if ag_wnm.id is not None and (string.strcmp(ag_wnm.id, "CPtr") == 0 or string.strcmp(ag_wnm.id, "rawptr") == 0):
|
||||
if ag_wrap.slice is not None: ag_side = ag_wrap.slice
|
||||
if ag_side.kind() == ast.ASTKind.Subscript:
|
||||
ag_sub: ast.Subscript* = ast.Subscript*(ag_side)
|
||||
if ag_sub.value is not None and ag_sub.value.kind() == ast.ASTKind.Name:
|
||||
@@ -3677,6 +3706,11 @@ novirtual class AssignGenMixin:
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(seg)
|
||||
if se is not None: return seg
|
||||
start = i + 1
|
||||
@@ -3690,6 +3724,11 @@ novirtual class AssignGenMixin:
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(seg)
|
||||
if se is not None: return seg
|
||||
return None
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1059,15 +1059,16 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if trans.CurAssignNode is not None and uint64(cl) == uint64(trans.CurAssignNode): ctx_annot = trans.CurAssignAnnot
|
||||
if ctx_annot is None and trans.CurRetNode is not None and uint64(cl) == uint64(trans.CurRetNode): ctx_annot = trans.CurFuncRetAnnot
|
||||
if ctx_annot is None: return None
|
||||
# Take the generic Subscript side of the annotation (same as _prespecialize_generic_annotation:
|
||||
# supports both `X[T]` and `X[T]*`).
|
||||
# Take the generic Subscript side of the annotation: `X[T]` or the `CPtr[X[T]]` pointer form.
|
||||
ctx_ps: ast.AST* = ctx_annot
|
||||
if ctx_ps.kind() == ast.ASTKind.Subscript:
|
||||
sub_wrap_ctx: ast.Subscript* = ast.Subscript*(ctx_ps)
|
||||
if sub_wrap_ctx.value is not None and sub_wrap_ctx.value.kind() == ast.ASTKind.Name:
|
||||
wk_nm_ctx: ast.Name* = ast.Name*(sub_wrap_ctx.value)
|
||||
if wk_nm_ctx.id is not None and (string.strcmp(wk_nm_ctx.id, "CPtr") == 0 or string.strcmp(wk_nm_ctx.id, "rawptr") == 0):
|
||||
if sub_wrap_ctx.slice is not None: ctx_ps = sub_wrap_ctx.slice
|
||||
spec_annot: ast.Subscript* = None
|
||||
if ctx_annot.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(ctx_annot)
|
||||
elif ctx_annot.kind() == ast.ASTKind.BinOp:
|
||||
bop_ctx: ast.BinOp* = ast.BinOp*(ctx_annot)
|
||||
if bop_ctx.op == ast.OpKind.BitOr:
|
||||
if bop_ctx.left is not None and bop_ctx.left.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(bop_ctx.left)
|
||||
elif bop_ctx.right is not None and bop_ctx.right.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(bop_ctx.right)
|
||||
if ctx_ps.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(ctx_ps)
|
||||
if spec_annot is None: return None
|
||||
# The annotation must be the same generic class as this construction (otherwise unrelated)
|
||||
sv_ctx: ast.AST* = spec_annot.value
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -324,16 +324,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
viperlib.snprintf(gt_err, 256, "Generic class '%s': template not found; the defining module may not be imported", gen_class_name)
|
||||
self.Gen.TypeGenErr.fatal_error(gen_nm_node, gt_err)
|
||||
self.Gen.TypeGenErr.fatal_error(gen_nm_node, "Generic class template not found")
|
||||
if func_name is None:
|
||||
# BinOp as the Call func: a union-type annotation used as a type cast, (A*)(value).
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.BinOp and trans is not None:
|
||||
binop_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if binop_ty is not None:
|
||||
binop_args: list[ast.AST*]* = cl.args
|
||||
if binop_args is not None and len(binop_args) == 1:
|
||||
binop_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, binop_args.get(0), funcs_ptr, func_count, trans)
|
||||
if binop_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, binop_arg_val, binop_ty)
|
||||
return None
|
||||
# Union type casts were retired; only Subscript / Name callees remain.
|
||||
if func_name is None: return None
|
||||
|
||||
# c.Asm/c.LLVMIR special calls return None when void but are handled, so use hit_box to
|
||||
# separate "missed"; otherwise they fall through to the external-function path and mis-report.
|
||||
@@ -1291,8 +1283,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.
|
||||
|
||||
@@ -147,11 +147,7 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1
|
||||
if og_len_ce == 5 and string.strncmp(alias_orig, "CEnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.CEnum) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CEnum") == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a CEnum (subclasses t.CEnum or uses @t.CEnum)."""
|
||||
@@ -185,11 +181,7 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1
|
||||
if og_len_cu == 6 and string.strncmp(alias_orig, "CUnion", 6) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.CUnion) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "CUnion") == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a CUnion (subclasses t.CUnion or uses @t.CUnion)."""
|
||||
@@ -223,11 +215,7 @@ novirtual class ClassGenBase:
|
||||
while alias_orig[og_len_cr] != ' ' and alias_orig[og_len_cr] != '\0': og_len_cr += 1
|
||||
if og_len_cr == 5 and string.strncmp(alias_orig, "REnum", 5) == 0: return 1
|
||||
return 0
|
||||
# Attribute(t.REnum) form.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(base_node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "REnum") == 0: return 1
|
||||
return 0
|
||||
# Attribute form was retired together with the t module prefix.
|
||||
return 0
|
||||
def _is_renum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the ClassDef is a REnum (subclasses t.REnum or uses @t.REnum)."""
|
||||
@@ -245,7 +233,7 @@ novirtual class ClassGenBase:
|
||||
if self._is_crenum_base(base_node, trans) == 1: return 1
|
||||
return 0
|
||||
def _has_decorator(self, decorator_list: list[ast.AST*]*, deco_name: str) -> int:
|
||||
"""Return 1 if decorator_list contains the given decorator name."""
|
||||
"""Return 1 if decorator_list contains the given bare builtin decorator name."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
@@ -253,16 +241,7 @@ novirtual class ClassGenBase:
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
# @t.NoVTable form (Attribute).
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(deco)
|
||||
if at.attr is not None and string.strcmp(at.attr, deco_name) == 0:
|
||||
# Check that value is Name('t').
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is not None and string.strcmp(vn.id, "t") == 0: return 1
|
||||
return 0
|
||||
# @NoVTable form (Name; from t import NoVTable).
|
||||
# @NoVTable / @CVTable / @TLS / @CStatic form (bare builtin Name).
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(deco)
|
||||
if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1
|
||||
@@ -290,10 +269,10 @@ novirtual class ClassGenBase:
|
||||
k: int = deco.kind()
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
# @c.Attribute(t.attr.xxx) form: func is Attribute(c, "Attribute").
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue
|
||||
at0: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
# @Attribute(attr.xxx) form: func is the bare builtin Name("Attribute").
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
|
||||
at0: ast.Name* = ast.Name*(cl.func)
|
||||
if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(int(an)):
|
||||
@@ -319,7 +298,7 @@ novirtual class ClassGenBase:
|
||||
return int(cn.int_val)
|
||||
return 0
|
||||
def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int:
|
||||
"""Return the alignment bytes from @c.Attribute(t.attr.aligned(N)); 0 when absent."""
|
||||
"""Return the alignment bytes from @Attribute(attr.aligned(N)); 0 when absent."""
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
@@ -328,9 +307,9 @@ novirtual class ClassGenBase:
|
||||
if deco is None: continue
|
||||
if deco.kind() == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(deco)
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue
|
||||
at0: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
|
||||
at0: ast.Name* = ast.Name*(cl.func)
|
||||
if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(int(an)):
|
||||
@@ -366,9 +345,6 @@ novirtual class ClassGenBase:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
is_prop_name: int = 0
|
||||
if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1
|
||||
# @t.TLS one-time init marker (mirrors TPC HandlesFunctions):
|
||||
# value must be Name('t'); bare @TLS is handled by the Name branch.
|
||||
if vn.id is not None and string.strcmp(vn.id, "t") == 0 and string.strcmp(at.attr, "TLS") == 0: meta |= META_TLS
|
||||
# Recognize @property.setter and @x.setter/getter/deleter.
|
||||
if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0:
|
||||
if string.strcmp(at.attr, "setter") == 0: meta |= GenTypes.META_PROP_SETTER
|
||||
|
||||
@@ -42,7 +42,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
# [enum bool rule] `t.CBool` and bare `bool` both materialize as 8 bits in enum member annotations,
|
||||
# else the same member would split into i1 / i8; the global `t.CBool` stays i1 (only enums differ).
|
||||
if string.strcmp(at.attr, "CBool") == 0: return mlirlite.Int8(pool)
|
||||
# Other t.CXxx types: unified (CTypedef dynamic table first, fall back to map_t_type)
|
||||
# Other t.CXxx types: unified (CTypedef dynamic table first, fall back to map_builtin_type)
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, imported_modules, from_imports)
|
||||
if ty is not None: return ty
|
||||
# [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback
|
||||
@@ -71,23 +71,24 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {nm.id}", "NameError")
|
||||
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
||||
|
||||
# BinOp node: t.State | int8 (recurse to find the concrete type)
|
||||
# Marker wrapper (CConst[T] / CVolatile[T] / ...): resolve the wrapped type.
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_em: ast.Subscript* = ast.Subscript*(ann_node)
|
||||
if sub_em.value is not None and sub_em.value.kind() == ast.ASTKind.Name:
|
||||
em_nm: ast.Name* = ast.Name*(sub_em.value)
|
||||
if em_nm.id is not None and (string.strcmp(em_nm.id, "CConst") == 0 or string.strcmp(em_nm.id, "CVolatile") == 0 \
|
||||
or string.strcmp(em_nm.id, "BigEndian") == 0 or string.strcmp(em_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(em_nm.id, "State") == 0 or string.strcmp(em_nm.id, "CStatic") == 0 \
|
||||
or string.strcmp(em_nm.id, "CExport") == 0 or string.strcmp(em_nm.id, "CExtern") == 0 \
|
||||
or string.strcmp(em_nm.id, "CRef") == 0 or string.strcmp(em_nm.id, "CTempRef") == 0):
|
||||
return self._resolve_enum_member_type(pool, sub_em.slice, imported_modules, from_imports)
|
||||
self.Gen.TypeGenErr.fatal_type_error(ann_node, "unsupported enum member type: only scalar types and their markers are allowed")
|
||||
return mlirlite.Int32(pool)
|
||||
|
||||
# BinOp nodes are never enum member types (`T1 | T2` unions retired).
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(ann_node)
|
||||
if bop.op == ast.OpKind.BitOr:
|
||||
left_ty: mlirlite.LLVMType* = self._resolve_enum_member_type(pool, bop.left, imported_modules, from_imports)
|
||||
# Skip t.State (the i32 default); prefer a concrete type
|
||||
left_bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(left_ty)
|
||||
if left_bits != 0 and not self._is_state_annotation(bop.left): return left_ty
|
||||
right_ty: mlirlite.LLVMType* = self._resolve_enum_member_type(pool, bop.right, imported_modules, from_imports)
|
||||
if right_ty is not None and not self._is_state_annotation(bop.right): return right_ty
|
||||
# Both sides are State or undecidable; return the wider one
|
||||
right_bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(right_ty)
|
||||
if left_bits >= right_bits: return left_ty
|
||||
return right_ty
|
||||
# fail-fast: BinOp but not BitOr (unknown union op); no silent i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {self.Gen.TypeGenResolve.annotation_to_str(ann_node)}", "TypeError")
|
||||
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
||||
self.Gen.TypeGenErr.fatal_type_error(ann_node, "enum member type uses the retired `T1 | T2` union; write the concrete type directly")
|
||||
return mlirlite.Int32(pool)
|
||||
|
||||
# fail-fast: unknown annotation node kind; no silent i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {self.Gen.TypeGenResolve.annotation_to_str(ann_node)}", "TypeError")
|
||||
@@ -133,17 +134,12 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
return 0
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is not None: return self._enum_annotation_name_is_ptr(sub.value)
|
||||
if sub.value is not None and self._enum_annotation_name_is_ptr(sub.value) != 0: return 1
|
||||
if sub.slice is not None: return self._enum_annotation_name_is_ptr(sub.slice)
|
||||
return 0
|
||||
return 0
|
||||
def _enum_member_annotation_is_ptr(self, ann_node: ast.AST*) -> int:
|
||||
"""Whether an enum member annotation (checking each union side) contains a pointer type, 1=yes / 0=no"""
|
||||
if ann_node is None: return 0
|
||||
if ann_node.kind() == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(ann_node)
|
||||
if bop.op != ast.OpKind.BitOr: return 0
|
||||
if self._enum_member_annotation_is_ptr(bop.left) == 1: return 1
|
||||
return self._enum_member_annotation_is_ptr(bop.right)
|
||||
"""Whether an enum member annotation contains a pointer type, 1=yes / 0=no"""
|
||||
return self._enum_annotation_name_is_ptr(ann_node)
|
||||
def _extract_int_value(self, node: ast.AST*) -> int64:
|
||||
"""Extract an integer value from an AST node, 0 on failure"""
|
||||
@@ -266,7 +262,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
tgt: ast.Name* = ast.Name*(aa.target)
|
||||
if tgt.id is None: continue
|
||||
|
||||
# [enum spec fail-fast] Members may not be pointer types: t.CPtr / CRef /
|
||||
# [enum spec fail-fast] Members may not be pointer types: rawptr / CRef /
|
||||
# CTempRef / CAutoPtr / rawptr[...] / str / bytes and unions carrying a pointer. Checked before type resolution.
|
||||
if self._enum_member_annotation_is_ptr(aa.annotation) == 1:
|
||||
self.Gen.TypeGenErr.fatal_error(aa, f"enum member '{tgt.id}' cannot be a pointer type (enum values only support integer member types)", "TypeError")
|
||||
@@ -601,6 +597,18 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
# analysis; the hardcoded size table below is only a fallback for t/c core primitives.
|
||||
dyn_sz: int = self.Gen.TypeGenResolve.get_type_size_bytes(pool, type_name, None, None)
|
||||
if dyn_sz > 0: return dyn_sz
|
||||
# Builtin short names
|
||||
if string.strcmp(type_name, "sizet") == 0 or string.strcmp(type_name, "size_t") == 0: return 8
|
||||
if string.strcmp(type_name, "int8") == 0 or string.strcmp(type_name, "uint8") == 0: return 1
|
||||
if string.strcmp(type_name, "uchar") == 0: return 1
|
||||
if string.strcmp(type_name, "int16") == 0 or string.strcmp(type_name, "uint16") == 0: return 2
|
||||
if string.strcmp(type_name, "short") == 0 or string.strcmp(type_name, "ushort") == 0: return 2
|
||||
if string.strcmp(type_name, "int32") == 0 or string.strcmp(type_name, "uint32") == 0: return 4
|
||||
if string.strcmp(type_name, "int") == 0 or string.strcmp(type_name, "uint") == 0: return 4
|
||||
if string.strcmp(type_name, "long") == 0 or string.strcmp(type_name, "ulong") == 0: return 4
|
||||
if string.strcmp(type_name, "int64") == 0 or string.strcmp(type_name, "uint64") == 0: return 8
|
||||
if string.strcmp(type_name, "longlong") == 0 or string.strcmp(type_name, "ulonglong") == 0: return 8
|
||||
if string.strcmp(type_name, "ptrdifft") == 0: return 8
|
||||
# Builtin types
|
||||
if string.strcmp(type_name, "int") == 0: return 4
|
||||
if string.strcmp(type_name, "CInt") == 0: return 4
|
||||
@@ -793,7 +801,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
|
||||
# Register into HandlesStruct.
|
||||
# A forward-declared placeholder for THIS class may have been created *during the variant walk*
|
||||
# above: a variant field like `Pointee: LLVMType*` is `BinOp(LLVMType | t.CPtr)` (parser's
|
||||
# above: a variant field like `Pointee: LLVMType*` is `BinOp(LLVMType | rawptr)` (parser's
|
||||
# _make_ptr_type expands the C-style suffix into a union, NOT into `rawptr[...]`), so resolving it
|
||||
# resolves the class name and calls ensure_forward_class. register_struct de-duplicates by Ty
|
||||
# pointer, so it would add a SECOND same-named entry and get_struct_type/find_struct (first match
|
||||
|
||||
@@ -318,10 +318,10 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if sign == TSpec.TS_SIGN_SIGNED: return "int64"
|
||||
return None
|
||||
# ---- Floats: exact bit width (no signedness concept, unambiguous) ----
|
||||
if string.strcmp(buf, "half") == 0: return "t.CFloat16T"
|
||||
if string.strcmp(buf, "half") == 0: return "float16"
|
||||
if string.strcmp(buf, "float") == 0: return "float"
|
||||
if string.strcmp(buf, "double") == 0: return "double"
|
||||
if string.strcmp(buf, "fp128") == 0: return "t.CFloat128T"
|
||||
if string.strcmp(buf, "fp128") == 0: return "float128"
|
||||
return None
|
||||
|
||||
def infer_scalar_type_arg_from_llvm_ty(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*, sign: int) -> str:
|
||||
|
||||
@@ -694,7 +694,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m))
|
||||
mm: int = 0
|
||||
if arg_m is not None and arg_m.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_t_attr(arg_m.annotation, "CVoid", imported_modules) == 0:
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg_m.annotation, "CVoid", imported_modules) == 0:
|
||||
mm = self.Gen.TypeGenResolve.ref_param_mode(arg_m.annotation, imported_modules)
|
||||
if mm == 1: mode_buf[mode_cnt] = char(49)
|
||||
elif mm == 2: mode_buf[mode_cnt] = char(50)
|
||||
|
||||
@@ -65,6 +65,42 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
def _is_t_type_object_name(self, name: str) -> int:
|
||||
"""Check whether name is a t.py type-object name (CPtr/CInt/CDouble, ...)."""
|
||||
if name is None: return 0
|
||||
# builtin short type names (int8/uint64/sizet/...)
|
||||
if string.strcmp(name, "sizet") == 0: return 1
|
||||
if string.strcmp(name, "size_t") == 0: return 1
|
||||
if string.strcmp(name, "int8") == 0: return 1
|
||||
if string.strcmp(name, "int16") == 0: return 1
|
||||
if string.strcmp(name, "int32") == 0: return 1
|
||||
if string.strcmp(name, "int64") == 0: return 1
|
||||
if string.strcmp(name, "uint8") == 0: return 1
|
||||
if string.strcmp(name, "uint16") == 0: return 1
|
||||
if string.strcmp(name, "uint32") == 0: return 1
|
||||
if string.strcmp(name, "uint64") == 0: return 1
|
||||
if string.strcmp(name, "intfast8") == 0: return 1
|
||||
if string.strcmp(name, "intfast16") == 0: return 1
|
||||
if string.strcmp(name, "intfast32") == 0: return 1
|
||||
if string.strcmp(name, "intfast64") == 0: return 1
|
||||
if string.strcmp(name, "uintfast8") == 0: return 1
|
||||
if string.strcmp(name, "uintfast16") == 0: return 1
|
||||
if string.strcmp(name, "uintfast32") == 0: return 1
|
||||
if string.strcmp(name, "uintfast64") == 0: return 1
|
||||
if string.strcmp(name, "intleast8") == 0: return 1
|
||||
if string.strcmp(name, "intleast16") == 0: return 1
|
||||
if string.strcmp(name, "intleast32") == 0: return 1
|
||||
if string.strcmp(name, "intleast64") == 0: return 1
|
||||
if string.strcmp(name, "uintleast8") == 0: return 1
|
||||
if string.strcmp(name, "uintleast16") == 0: return 1
|
||||
if string.strcmp(name, "uintleast32") == 0: return 1
|
||||
if string.strcmp(name, "uintleast64") == 0: return 1
|
||||
if string.strcmp(name, "uchar") == 0: return 1
|
||||
if string.strcmp(name, "short") == 0: return 1
|
||||
if string.strcmp(name, "ushort") == 0: return 1
|
||||
if string.strcmp(name, "uint") == 0: return 1
|
||||
if string.strcmp(name, "long") == 0: return 1
|
||||
if string.strcmp(name, "ulong") == 0: return 1
|
||||
if string.strcmp(name, "longlong") == 0: return 1
|
||||
if string.strcmp(name, "ulonglong") == 0: return 1
|
||||
if string.strcmp(name, "ptrdifft") == 0: return 1
|
||||
# type object names in t.py (PascalCase, initial uppercase)
|
||||
if string.strcmp(name, "CPtr") == 0: return 1
|
||||
if string.strcmp(name, "CInt") == 0: return 1
|
||||
@@ -217,36 +253,16 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if use_ty is None: use_ty = mlirlite.Int8(pool)
|
||||
# ensure cross-module struct types in use_ty have an opaque declaration
|
||||
mlirlite.module_ensure_opaque_for_type(mod, pool, use_ty)
|
||||
# special case: type objects in t.py (CPtr/CInt/CDouble etc.). t.py is not compiled but
|
||||
# these objects are referenced at runtime, so emit a linkonce_odr definition with null init.
|
||||
is_t_type_obj: int = 0
|
||||
if string.strcmp(mod_path_w, "t") == 0: is_t_type_obj = self._is_t_type_object_name(at.attr)
|
||||
# a class name is not a module-level global: registered classes/structs/REnums are always
|
||||
# handled by the class/REnum paths, never manufactured into an external global here.
|
||||
if is_t_type_obj == 0 and self.Gen.StructGen.find_struct(at.attr) is not None: return None
|
||||
# fallback declaration name: t type objects (t.py uncompiled, no sha1) keep the bare
|
||||
# linkonce_odr name; other module attributes carry the target module's SHA1 prefix.
|
||||
gv_name_ba: str = at.attr
|
||||
if is_t_type_obj == 0: gv_name_ba = self.Gen.FuncGenBase._mangle_name_with_sha1(self._lookup_module_sha1(mod_path_w), at.attr)
|
||||
if self.Gen.StructGen.find_struct(at.attr) is not None: return None
|
||||
# fallback declaration name: other module attributes carry the target module's SHA1 prefix.
|
||||
gv_name_ba: str = self.Gen.FuncGenBase._mangle_name_with_sha1(self._lookup_module_sha1(mod_path_w), at.attr)
|
||||
ext_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name_ba, use_ty)
|
||||
if ext_gv is not None:
|
||||
if is_t_type_obj != 0:
|
||||
# t.CPtr etc.: create a definition with a null initializer (linkonce_odr)
|
||||
ext_gv.Linkage = "linkonce_odr"
|
||||
# initializer text for i8* null
|
||||
null_init: bytes = pool.alloc(8)
|
||||
if null_init is not None:
|
||||
null_init[0] = 'n'
|
||||
null_init[1] = 'u'
|
||||
null_init[2] = 'l'
|
||||
null_init[3] = 'l'
|
||||
null_init[4] = '\0'
|
||||
ext_gv.Initializer = null_init
|
||||
else: ext_gv.Initializer = None
|
||||
else:
|
||||
# regular module attribute: external declaration (definition provided elsewhere)
|
||||
ext_gv.Linkage = "external"
|
||||
ext_gv.Initializer = None
|
||||
# regular module attribute: external declaration (definition provided elsewhere)
|
||||
ext_gv.Linkage = "external"
|
||||
ext_gv.Initializer = None
|
||||
mlirlite.module_add_global(mod, ext_gv)
|
||||
return self.make_global_ref(pool, gv_name_ba, use_ty)
|
||||
def _lookup_module_sha1(self, mod_name: str) -> str:
|
||||
@@ -256,32 +272,19 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
# module attribute (sys._mbuddy → global), then struct field (GEP + load). Name bases skip the struct load.
|
||||
|
||||
|
||||
# _get_cderef_attr_base - base pointer of a c.Deref(ptr).attr access (like *p.a → p->a): return the ptr
|
||||
# itself, not the loaded struct value; None for non-c.Deref forms (_deref_if_ptr_ptr resolves the level).
|
||||
# _get_cderef_attr_base - base pointer of a `(*ptr).attr` access (like p->a): return the ptr
|
||||
# itself, not the loaded struct value; None for non-deref forms (_deref_if_ptr_ptr resolves the level).
|
||||
def _get_cderef_attr_base(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, at_value: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Return the ptr itself for a dereferenced attribute base (`c.Deref(ptr).attr` / `(*ptr).attr`), else None (no load)."""
|
||||
"""Return the ptr itself for a dereferenced attribute base (`(*ptr).attr`), else None (no load)."""
|
||||
if at_value is None: return None
|
||||
# New syntax form: `(*ptr).attr` is AST UnaryOp(OpKind.Deref) — identical semantics to the legacy
|
||||
# `c.Deref(ptr).attr`. Without this branch the attribute write path sees a *loaded struct value*
|
||||
# as the base and fails with "base=非指针类型,无法做属性写入"; the read path silently does an
|
||||
# extra struct load. Both must GEP the pointer itself (R4 migration of `c.Deref` -> `*`).
|
||||
# `(*ptr).attr` is AST UnaryOp(OpKind.Deref). Without this branch the attribute write path
|
||||
# sees a *loaded struct value* as the base; both paths must GEP the pointer itself.
|
||||
if at_value.kind() == ast.ASTKind.UnaryOp:
|
||||
uo_cd: ast.UnaryOp* = ast.UnaryOp*(at_value)
|
||||
if uo_cd.op != ast.OpKind.Deref or uo_cd.operand is None: return None
|
||||
inner_uo: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_cd.operand, None, 0, trans)
|
||||
if inner_uo is not None and self.is_ptr_type(inner_uo.Ty) != 0: return inner_uo
|
||||
return None
|
||||
if at_value.kind() != ast.ASTKind.Call: return None
|
||||
cl_cd: ast.Call* = ast.Call*(at_value)
|
||||
if cl_cd.func is None or cl_cd.func.kind() != ast.ASTKind.Attribute: return None
|
||||
f_cd: ast.Attribute* = ast.Attribute*(cl_cd.func)
|
||||
if f_cd.attr is None or string.strcmp(f_cd.attr, "Deref") != 0: return None
|
||||
if f_cd.value is None or f_cd.value.kind() != ast.ASTKind.Name: return None
|
||||
nm_cd: ast.Name* = ast.Name*(f_cd.value)
|
||||
if nm_cd.id is None or string.strcmp(nm_cd.id, "c") != 0: return None
|
||||
if cl_cd.args is None or len(cl_cd.args) < 1: return None
|
||||
inner_cd: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_cd.args.get(0), None, 0, trans)
|
||||
if inner_cd is not None and self.is_ptr_type(inner_cd.Ty) != 0: return inner_cd
|
||||
return None
|
||||
def translate_attribute(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Translate an attribute access and return the loaded field value."""
|
||||
@@ -482,7 +485,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
return None # it is a submodule, hand it to the nested attribute path
|
||||
# special case: t.ASM_DESCR is the inline-asm constraint namespace, not a CDefine; its
|
||||
# .XXX is handled by _asm_resolve_constraint, so return None for the nested path to handle.
|
||||
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0: return None
|
||||
if string.strcmp(nm_ma.id, "ASM_DESCR") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0: return None
|
||||
# ALL_CAPS module attribute that is not a CDefine: load it as a cross-module global when the
|
||||
# module defines a same-named global (e.g. PA_DEPS_MAX_STR); else fail-fast (undefined value).
|
||||
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0:
|
||||
@@ -834,20 +837,25 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if fe is None: return None
|
||||
return self.Gen.StructGen.get_field_annot_class_name(fe)
|
||||
|
||||
# _base_class_from_cast_base - bare class name of a pointer-cast base `T*(x)` (parsed as `(T*)(x)`, whose
|
||||
# union-with-t.CPtr type is i8*), extracted from the BinOp annotation; bare name matches TPC's convention.
|
||||
# _base_class_from_cast_base - bare class name of a pointer-cast base `T*(x)` (parsed as CPtr[T]),
|
||||
# extracted from the marker subscript; bare name matches TPC's convention.
|
||||
def _base_class_from_cast_base(self, pool: memhub.MemBuddy*, trans: HT.Translator*, node: ast.AST*) -> str:
|
||||
"""Return the bare class name of a T*(x) cast base, or None for other forms."""
|
||||
if node is None or trans is None: return None
|
||||
if node.kind() != ast.ASTKind.Call: return None
|
||||
cl_cb: ast.Call* = ast.Call*(node)
|
||||
if cl_cb.func is None or cl_cb.func.kind() != ast.ASTKind.BinOp: return None
|
||||
bo_cb: ast.BinOp* = ast.BinOp*(cl_cb.func)
|
||||
if bo_cb.op != ast.OpKind.BitOr: return None
|
||||
# when one side of the union is the t.CPtr marker (the nested union of `T*` / `T**`), take the
|
||||
# other side; otherwise (a hand-written `X | Y`) take the left side, matching parser _make_ptr_type
|
||||
tgt_cb: ast.AST* = bo_cb.left
|
||||
if self.Gen.TypeGenResolve._is_cptr_marker(bo_cb.left, trans._imported_modules) != 0: tgt_cb = bo_cb.right
|
||||
tgt_cb: ast.AST* = node
|
||||
# A cast call node: the pointer type is the callee.
|
||||
if tgt_cb.kind() == ast.ASTKind.Call:
|
||||
cl_cb: ast.Call* = ast.Call*(tgt_cb)
|
||||
if cl_cb.func is None: return None
|
||||
tgt_cb = cl_cb.func
|
||||
# Unwrap every CPtr[...] layer; the innermost node is the cast base.
|
||||
while tgt_cb is not None and tgt_cb.kind() == ast.ASTKind.Subscript:
|
||||
sub_cb: ast.Subscript* = ast.Subscript*(tgt_cb)
|
||||
if sub_cb.value is None or sub_cb.value.kind() != ast.ASTKind.Name: break
|
||||
mk_nm: ast.Name* = ast.Name*(sub_cb.value)
|
||||
if mk_nm.id is None: break
|
||||
if string.strcmp(mk_nm.id, "CPtr") != 0 and string.strcmp(mk_nm.id, "rawptr") != 0: break
|
||||
tgt_cb = sub_cb.slice
|
||||
if tgt_cb is None: return None
|
||||
k_cb: int = tgt_cb.kind()
|
||||
if k_cb == ast.ASTKind.Attribute:
|
||||
|
||||
@@ -328,12 +328,11 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if match_ok == 0: continue
|
||||
if pyi_buf[line_start + name_len] != ':': continue
|
||||
|
||||
# check the line contains "t.CDefine"
|
||||
# scan the line for "t.CDefine"
|
||||
# check the line contains the CDefine marker
|
||||
has_cdefine: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 8 <= line_start + line_len:
|
||||
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and pyi_buf[ci + 8] == 'e'):
|
||||
while ci + 7 <= line_start + line_len:
|
||||
if (pyi_buf[ci] == 'C' and pyi_buf[ci + 1] == 'D' and pyi_buf[ci + 2] == 'e' and pyi_buf[ci + 3] == 'f' and pyi_buf[ci + 4] == 'i' and pyi_buf[ci + 5] == 'n' and pyi_buf[ci + 6] == 'e'):
|
||||
has_cdefine = 1
|
||||
break
|
||||
ci += 1
|
||||
|
||||
@@ -344,6 +344,11 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
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.
|
||||
@@ -363,6 +368,11 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
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.
|
||||
|
||||
@@ -368,9 +368,11 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
if elem_buf_e is None: return None
|
||||
string.strncpy(elem_buf_e, lb_e + 1, elen_e)
|
||||
elem_buf_e[elen_e] = '\0'
|
||||
# Strip the rawptr[...] wrapper (elements declared as object pointers, e.g. rawptr[sha1.AST])
|
||||
# Strip the pointer wrapper (elements declared as object pointers): CPtr[...] / rawptr[...]
|
||||
cur_e: str = elem_buf_e
|
||||
if string.strncmp(cur_e, "rawptr[", 7) == 0: cur_e = cur_e + 7
|
||||
while string.strncmp(cur_e, "CPtr[", 5) == 0 or string.strncmp(cur_e, "rawptr[", 7) == 0:
|
||||
if string.strncmp(cur_e, "CPtr[", 5) == 0: cur_e = cur_e + 5
|
||||
else: cur_e = cur_e + 7
|
||||
# Split unions by '|', trimming leading/trailing whitespace, trailing ']' and quotes; each
|
||||
# segment goes to find_struct (builtin names return None; "sha1.Class" prefix matches by module).
|
||||
total_e: sizet = string.strlen(cur_e)
|
||||
|
||||
@@ -201,13 +201,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm: ast.Name* = ast.Name*(at.value)
|
||||
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 and at.attr is not None:
|
||||
if self._type_name_unsigned(at.attr) != 0: return 1
|
||||
return 0
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: return 0
|
||||
# Plain function call: widen by the callee's **declared return type**. Without this,
|
||||
# `uint64(f())` with `def f() -> uint32` emitted `sext i32 -> i64` (verified in the
|
||||
# generated IR), so a u32 with bit 31 set grew 0xFFFFFFFF in the high half — a real
|
||||
@@ -359,13 +353,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm: ast.Name* = ast.Name*(at.value)
|
||||
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 and at.attr is not None:
|
||||
if self._spec_sign_from_annot(at.attr) == TSpec.TS_SIGN_SIGNED: return 1
|
||||
return 0
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None:
|
||||
|
||||
@@ -42,23 +42,12 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None: continue
|
||||
cfk: int = call_node.func.kind()
|
||||
if cfk != ast.ASTKind.Attribute: continue
|
||||
|
||||
# Detect c.Attribute
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None: continue
|
||||
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if func_attr.value is None: continue
|
||||
fvk: int = func_attr.value.kind()
|
||||
if fvk != ast.ASTKind.Name: continue
|
||||
c_name: ast.Name* = ast.Name*(func_attr.value)
|
||||
if c_name.id is None: continue
|
||||
if string.strcmp(c_name.id, "c") != 0: continue
|
||||
# 不要求 `import c`:`@__attribute__(...)` 会被 ast/parser.vp(_DesugarMagicAttribute)
|
||||
# 脱糖成 `@c.Attribute(...)`,这里的 `c.` 前缀是编译器自己产生的(用户写的是
|
||||
# `@__attribute__`,并非 `@c.Attribute`)。若在此要求 `import c`,用户明明没引入 c
|
||||
# 就会把整条装饰器静默丢弃(`stackrealign`/`always_inline` 等函数属性全失效)。
|
||||
# extract_func_section 本就没有这个守卫,二者保持一致。
|
||||
# Detect the builtin Attribute(...) wrapper emitted by @__attribute__(...).
|
||||
if cfk != ast.ASTKind.Name: continue
|
||||
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||
if func_nm.id is None: continue
|
||||
if string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||
|
||||
# Iterate args and extract attrs
|
||||
if call_node.args is None: continue
|
||||
@@ -80,7 +69,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if found_any == 0: return None
|
||||
return attrs_buf
|
||||
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract @c.Attribute(t.attr.section(...)) as the attr-group text."""
|
||||
"""Extract @Attribute(attr.section(...)) as the attr-group text."""
|
||||
if pool is None or decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
@@ -88,9 +77,9 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Name: continue
|
||||
func_nm: ast.Name* = ast.Name*(call_node.func)
|
||||
if func_nm.id is None or string.strcmp(func_nm.id, "Attribute") != 0: continue
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
@@ -272,11 +261,11 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# t.CVoid means no args: skip
|
||||
# CVoid / void means no args: skip
|
||||
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
|
||||
if arg.annotation.kind() == ast.ASTKind.Name:
|
||||
at_cn: ast.Name* = ast.Name*(arg.annotation)
|
||||
if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
|
||||
@@ -38,7 +38,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(i))
|
||||
if arg is None or arg.arg is None: continue
|
||||
# CVoid params are not emitted into the signature, so they must not occupy a mode slot.
|
||||
if arg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imp) != 0: continue
|
||||
if arg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imp) != 0: continue
|
||||
m: int = 0
|
||||
if arg.annotation is not None: m = self.Gen.TypeGenResolve.ref_param_mode(arg.annotation, imp)
|
||||
if m == 1: buf[cnt] = char(49)
|
||||
@@ -110,14 +110,11 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return).
|
||||
self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type")
|
||||
ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0)
|
||||
# A lone t.CExport return annotation is an export modifier with no real type -> Void
|
||||
# A lone CExport return annotation is an export modifier with no real type -> Void
|
||||
# (CExport fell back to i32, forcing the tail check to return i32: mm.vp free root cause).
|
||||
if fd.returns.kind() == ast.ASTKind.Attribute:
|
||||
ce_at: ast.Attribute* = ast.Attribute*(fd.returns)
|
||||
if ce_at.attr is not None and string.strcmp(ce_at.attr, "CExport") == 0:
|
||||
if ce_at.value is not None and ce_at.value.kind() == ast.ASTKind.Name:
|
||||
ce_nm: ast.Name* = ast.Name*(ce_at.value)
|
||||
if ce_nm.id is not None and string.strcmp(ce_nm.id, "t") == 0: ret_ty = mlirlite.Void(pool)
|
||||
if fd.returns.kind() == ast.ASTKind.Name:
|
||||
ce_at: ast.Name* = ast.Name*(fd.returns)
|
||||
if ce_at.id is not None and string.strcmp(ce_at.id, "CExport") == 0: ret_ty = mlirlite.Void(pool)
|
||||
if ret_ty is None and fd.returns is not None:
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0: ret_ty = mlirlite.Void(pool)
|
||||
# @c.CReturn decorator removed: encountering it is a compile-time error (use -> tuple[...] for multiple returns)
|
||||
@@ -206,7 +203,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if arg is None or arg.arg is None: continue
|
||||
# CVoid means empty param: skip
|
||||
if arg.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
@@ -280,14 +277,11 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return).
|
||||
self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type")
|
||||
ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0)
|
||||
# A lone t.CExport return annotation is an export modifier with no real type -> Void
|
||||
# A lone CExport return annotation is an export modifier with no real type -> Void
|
||||
# (CExport fell back to i32, forcing return i32: mm.vp free root cause). Mixed types keep the resolved result.
|
||||
if fd.returns.kind() == ast.ASTKind.Attribute:
|
||||
ce_at2: ast.Attribute* = ast.Attribute*(fd.returns)
|
||||
if ce_at2.attr is not None and string.strcmp(ce_at2.attr, "CExport") == 0:
|
||||
if ce_at2.value is not None and ce_at2.value.kind() == ast.ASTKind.Name:
|
||||
ce_nm2: ast.Name* = ast.Name*(ce_at2.value)
|
||||
if ce_nm2.id is not None and string.strcmp(ce_nm2.id, "t") == 0: ret_ty = mlirlite.Void(pool)
|
||||
if fd.returns.kind() == ast.ASTKind.Name:
|
||||
ce_at2: ast.Name* = ast.Name*(fd.returns)
|
||||
if ce_at2.id is not None and string.strcmp(ce_at2.id, "CExport") == 0: ret_ty = mlirlite.Void(pool)
|
||||
|
||||
# If the return annotation is purely a decorator marker (e.g. t.State, no real type), use void.
|
||||
# Must be checked before infer_return_type, which would return at least i32.
|
||||
@@ -412,7 +406,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
arg_e: ast.Arg* = ast.Arg*(alist_e.get(ai_e))
|
||||
if arg_e is not None and arg_e.arg is not None:
|
||||
# CVoid means empty param: skip, do not add to the function signature
|
||||
if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg_e.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg_e.annotation, "CVoid", imported_modules) != 0: continue
|
||||
param_ty_e: mlirlite.LLVMType* = i32_ty
|
||||
if arg_e.annotation is not None:
|
||||
resolved_e: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_e.annotation, imported_modules, from_imports, trans, 0)
|
||||
@@ -479,7 +473,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
rearg: ast.Arg* = ast.Arg*(reuse_list.get(reuse_i))
|
||||
if rearg is None or rearg.arg is None: continue
|
||||
# CVoid means empty param: not part of the signature (mirrors the add_param loops above)
|
||||
if rearg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(rearg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if rearg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(rearg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if reused_p is None:
|
||||
# 复用声明形参少于定义:签名与函数体必然错配,禁止静默,fail-fast
|
||||
self.Gen.TypeGenErr.fatal_error(rearg, f"reused declaration for '{fd.name}' has fewer parameters than the definition", "TypeError")
|
||||
@@ -537,7 +531,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# CVoid means empty param: skip, do not add to the function signature
|
||||
if arg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if arg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
@@ -678,7 +672,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if arg2 is not None and arg2.arg is not None:
|
||||
# CVoid means empty param: skip, do not create an alloca
|
||||
if arg2.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_t_attr(arg2.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg2.annotation, "CVoid", imported_modules) != 0: continue
|
||||
param_ty2: mlirlite.LLVMType* = i32_ty
|
||||
if arg2.annotation is not None:
|
||||
resolved2: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg2.annotation, imported_modules, from_imports, trans, 0)
|
||||
|
||||
@@ -94,7 +94,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
arg_n: ast.Arg* = ast.Arg*(alist_n.get(ai_n))
|
||||
if arg_n is None or arg_n.arg is None: continue
|
||||
if arg_n.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_t_attr(arg_n.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg_n.annotation, "CVoid", imported_modules) != 0: continue
|
||||
pty_n: mlirlite.LLVMType* = i32_ty
|
||||
if arg_n.annotation is not None:
|
||||
resolved_n: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_n.annotation, imported_modules, from_imports, trans, 0)
|
||||
@@ -147,7 +147,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
arg_p: ast.Arg* = ast.Arg*(alist_p.get(api))
|
||||
if arg_p is None or arg_p.arg is None: continue
|
||||
if arg_p.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_t_attr(arg_p.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg_p.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if idx_p >= param_count: break
|
||||
pty2_n: mlirlite.LLVMType* = i32_ty
|
||||
if param_tys is not None:
|
||||
|
||||
@@ -86,16 +86,16 @@ def fatal_error(node: ast.AST*, msg: str, err_type: str = "SyntaxError") -> None
|
||||
if gg is not None: gg.TypeGenErr.fatal_error(node, msg, err_type)
|
||||
|
||||
|
||||
# ---------------- map_t_type (delegates to TypeGenResolve.map_t_type) ----------------
|
||||
def map_t_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
|
||||
"""Delegate to gen.map_t_type"""
|
||||
if _g_gen is not None: _g_gen.TypeGenResolve.map_t_type(pool, type_name)
|
||||
# ---------------- map_builtin_type (delegates to TypeGenResolve.map_builtin_type) ----------------
|
||||
def map_builtin_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
|
||||
"""Delegate to gen.map_builtin_type"""
|
||||
if _g_gen is not None: _g_gen.TypeGenResolve.map_builtin_type(pool, type_name)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- resolve_typedef_or_map (delegates to TypeGenResolve.resolve_typedef_or_map) ----------------
|
||||
def resolve_typedef_or_map(pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
|
||||
"""Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_t_type."""
|
||||
"""Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_builtin_type."""
|
||||
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_typedef_or_map(pool, name, imported_modules, from_imports, trans)
|
||||
return None
|
||||
|
||||
|
||||
@@ -546,9 +546,12 @@ novirtual class ImportsGenMixin:
|
||||
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
|
||||
"""Fail-fast when an imported module's SHA1 is not registered."""
|
||||
if mod_name is None: return 1
|
||||
# t/c are internal libraries exempt from includes scanning; so are ctraits (a .vpi
|
||||
# compile-time macro lib) and viperbuild (a compiler builtin macro lib).
|
||||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0: return 1
|
||||
# t / c were retired: their types are builtin and their C helpers now have language syntax.
|
||||
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
|
||||
self._report_import_error(node, "`import t` / `import c` is retired; builtin types and C helpers no longer live in modules")
|
||||
return 0
|
||||
# ctraits (a .vpi compile-time macro lib) and viperbuild (a compiler builtin macro lib) stay exempt.
|
||||
if string.strcmp(mod_name, "ctraits") == 0: return 1
|
||||
if string.strcmp(mod_name, "viperbuild") == 0: return 1
|
||||
mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if mod_sha1 is None:
|
||||
|
||||
@@ -53,18 +53,11 @@ novirtual class StmtGenCifMixin:
|
||||
return 0
|
||||
|
||||
def _is_cif_call(self, node: ast.AST*) -> int:
|
||||
"""Check whether test is a cif marker call (c.@CIf family, or a bare @CIf-family name)."""
|
||||
"""Check whether test is a cif marker call (bare @CIf-family name)."""
|
||||
if node is None or node.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.func is None: return 0
|
||||
fk: int = cl.func.kind()
|
||||
if fk == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at.attr is None: return 0
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is not None and string.strcmp(vn.id, "c") == 0: return self._is_cif_attr(at.attr)
|
||||
return 0
|
||||
if fk == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(cl.func)
|
||||
if nm.id is not None and self._is_cif_attr(nm.id) != 0: return 1
|
||||
|
||||
@@ -2652,7 +2652,7 @@ novirtual class StructGenMixin:
|
||||
if mod_buf_f is not None:
|
||||
string.strncpy(mod_buf_f, name, mod_len_f)
|
||||
mod_buf_f[mod_len_f] = '\0'
|
||||
if string.strcmp(mod_buf_f, "t") != 0 and string.strcmp(mod_buf_f, "c") != 0:
|
||||
if mod_buf_f is not None:
|
||||
# A 16-hex module segment is a SHA1 literal (e.g. "{9aab}.Scene3D", TypeGenResolve L1397-1402's sha1-prefix form): skip the store module-name lookup (store keys are module names, not sha1) and match name+sha1 exactly.
|
||||
# Otherwise _resolve_module_sha1("9aab") returns None and a bare-name fallback returns the first-registered wrong cross-module Scene3D entry (VKernel gfx / App gfx both define Scene3D -> wrong ownership -> ret_sha1=0ae -> Phase A _deps_pending deadlock). [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16 (single source of truth).
|
||||
mod_hex_ok: int = GenApi.is_sha1_hex16(mod_buf_f)
|
||||
@@ -2905,7 +2905,7 @@ novirtual class StructGenMixin:
|
||||
if mod_buf_q is not None:
|
||||
string.strncpy(mod_buf_q, class_name, mod_len_q)
|
||||
mod_buf_q[mod_len_q] = '\0'
|
||||
if string.strcmp(mod_buf_q, "t") != 0 and string.strcmp(mod_buf_q, "c") != 0:
|
||||
if mod_buf_q is not None:
|
||||
# A 16-hex module segment ("{16hex}.Class", the normalized generic type-arg form) is the module SHA1; a registered class proves that module compiled, so match name+sha1 exactly without _resolve_module_sha1 (store keys are module names, so querying the sha1 is always None and would fail-fast wrongly).
|
||||
# [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16; generic subscript dots are already skipped by _last_dot_outside_brackets, so only top-level a.b forms remain here.
|
||||
mod_hex_ok_q: int = GenApi.is_sha1_hex16(mod_buf_q)
|
||||
|
||||
@@ -425,19 +425,17 @@ novirtual class TranslatorGenMixin:
|
||||
viperlib.snprintf(buf, body_len + 8, "{ %s }", body)
|
||||
return buf
|
||||
|
||||
# Module-level global t.attr.* extraction: attributes live in the annotation union
|
||||
# (`T | t.attr.section(".x")`) and are written directly to gv.Section/Align/Linkage fields.
|
||||
# Module-level global attr.* extraction: attributes are marker wrappers
|
||||
# (`attr.section(".x")[T]`) written directly to gv.Section/Align/Linkage fields.
|
||||
def _scan_global_tattr(self, node: ast.AST*, want_name: str) -> ast.AST*:
|
||||
"""Find the t.attr.<want_name> node in a global type annotation; None when absent."""
|
||||
"""Find the attr.<want_name> marker in a global type annotation; None when absent."""
|
||||
if node is None: return None
|
||||
if node.kind() == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if bop.op == ast.OpKind.BitOr:
|
||||
hit_l: ast.AST* = self._scan_global_tattr(bop.left, want_name)
|
||||
if hit_l is not None: return hit_l
|
||||
return self._scan_global_tattr(bop.right, want_name)
|
||||
return None
|
||||
if self.Gen.ClassGenBase._tattr_name(node, want_name) != 0: return node
|
||||
if node.kind() == ast.ASTKind.Subscript:
|
||||
sub_t: ast.Subscript* = ast.Subscript*(node)
|
||||
hit_s: ast.AST* = self._scan_global_tattr(sub_t.value, want_name)
|
||||
if hit_s is not None: return hit_s
|
||||
return self._scan_global_tattr(sub_t.slice, want_name)
|
||||
return None
|
||||
def _extract_global_attr_section(self, node: ast.AST*, var_name: str) -> str:
|
||||
"""Extract the section name from `T | t.attr.section("xxx")`; fail-fast when empty."""
|
||||
@@ -960,12 +958,9 @@ novirtual class TranslatorGenMixin:
|
||||
if cls_nm_ml is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, var_name, cls_nm_ml)
|
||||
# Module-level str*/bytes* globals: mark IsPtrElement so var[i] steps by 8-byte
|
||||
# pointers; otherwise it degrades to a 1-byte GEP + load i8 that truncates pointers.
|
||||
if aa_ml.annotation.kind() == ast.ASTKind.BinOp:
|
||||
bop_ml: ast.BinOp* = ast.BinOp*(aa_ml.annotation)
|
||||
if bop_ml.op == ast.OpKind.BitOr:
|
||||
if self.Gen.AssignGen._annotation_has_cptr_marker(aa_ml.annotation) != 0:
|
||||
if self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "str") != 0 or self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "bytes") != 0:
|
||||
self.Gen.VarGen.set_var_ptr_element(trans.SymTab, var_name)
|
||||
if self.Gen.AssignGen._annotation_has_cptr_marker(aa_ml.annotation) != 0:
|
||||
if self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "str") != 0 or self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "bytes") != 0:
|
||||
self.Gen.VarGen.set_var_ptr_element(trans.SymTab, var_name)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
@@ -351,29 +351,17 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
vn: sizet = len(self.CTypedefVisiting)
|
||||
if vn > 0: self.CTypedefVisiting.pop()
|
||||
def _is_ctypedef_annotation(self, annotation: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is t.CTypedef, otherwise 0."""
|
||||
"""Return 1 if the annotation is the CTypedef marker, otherwise 0."""
|
||||
if annotation is None: return 0
|
||||
k_v: int = annotation.kind()
|
||||
if k_v != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(annotation)
|
||||
# Use ast module accessors for field access: a direct at.attr read may emit a
|
||||
# wrong GEP from the stub type and crash strcmp.
|
||||
at_attr_v: str = ast.attribute_get_attr(at)
|
||||
if at_attr_v is None: return 0
|
||||
if annotation.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(annotation)
|
||||
nm_id_v: str = ast.name_get_id(nm)
|
||||
if nm_id_v is None: return 0
|
||||
# Length-bounded compare instead of strcmp: the token may lack a NUL and strcmp
|
||||
# reads past the page; check byte 9 is a terminator/separator.
|
||||
if string.strncmp(at_attr_v, "CTypedef", 8) != 0: return 0
|
||||
sep_ch_d: char = at_attr_v[8]
|
||||
# reads past the page; check byte 8 is a terminator/separator.
|
||||
if string.strncmp(nm_id_v, "CTypedef", 8) != 0: return 0
|
||||
sep_ch_d: char = nm_id_v[8]
|
||||
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|': return 0
|
||||
at_val_v: ast.AST* = ast.attribute_get_value(at)
|
||||
if at_val_v is None or at_val_v.kind() != ast.ASTKind.Name: return 0
|
||||
mod_nm: ast.Name* = ast.Name*(at_val_v)
|
||||
mod_id_v: str = ast.name_get_id(mod_nm)
|
||||
if mod_id_v is None: return 0
|
||||
# same length-bounded compare ("t" may lack a NUL)
|
||||
if string.strncmp(mod_id_v, "t", 1) != 0: return 0
|
||||
sep_ch_m: char = mod_id_v[1]
|
||||
if sep_ch_m != '\0' and sep_ch_m != ' ' and sep_ch_m != '\n' and sep_ch_m != '\r' and sep_ch_m != '\t' and sep_ch_m != '|': return 0
|
||||
return 1
|
||||
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
|
||||
"""Scan top-level AST nodes and register all NAME: t.CTypedef = Type declarations."""
|
||||
|
||||
@@ -140,18 +140,21 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
|
||||
k: int = node.kind()
|
||||
|
||||
# Attribute node: t.CExtern, t.State, t.CExport, t.CInline
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is not None:
|
||||
if string.strcmp(at.attr, marker_name) == 0: return 1
|
||||
# Name node: a bare builtin marker (CExtern / State / CExport / CInline / CStatic / CConst ...)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, marker_name) == 0: return 1
|
||||
return 0
|
||||
|
||||
# BinOp node: int | t.CExtern (recurse into both children)
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0: return 1
|
||||
if self.has_decorator_marker(bop.right, marker_name, depth + 1) != 0: return 1
|
||||
# Subscript node: a marker wrapper `CConst[T]` / descriptor chain; the marker may be on this
|
||||
# level or nested inside the wrapped type.
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_m: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_m.value is not None and sub_m.value.kind() == ast.ASTKind.Name:
|
||||
mv: ast.Name* = ast.Name*(sub_m.value)
|
||||
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
|
||||
@@ -162,7 +165,8 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
rp_nm: ast.Name* = ast.Name*(sub.value)
|
||||
if rp_nm.id is None or string.strcmp(rp_nm.id, "rawptr") != 0: return 0
|
||||
if rp_nm.id is None: 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: return 0
|
||||
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
|
||||
|
||||
@@ -298,7 +302,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if arg is None or arg.arg is None: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
# Unified: prefer the CTypedef dynamic table, fall back to map_t_type
|
||||
# Unified: prefer the CTypedef dynamic table, fall back to map_builtin_type
|
||||
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -86,6 +86,44 @@ def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
|
||||
if _spec_name_eq(text, start, end, "CSizeT") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "CUIntPtrT") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "CBool") != 0: return TS_SIGN_UNSIGNED
|
||||
# --- builtin short names ---
|
||||
if _spec_name_eq(text, start, end, "sizet") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "size_t") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uint8") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uint16") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uint32") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uint64") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintfast8") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintfast16") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintfast32") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintfast64") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintleast8") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintleast16") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintleast32") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintleast64") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uchar") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "ushort") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uint") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "ulong") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "ulonglong") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "uintptr") != 0: return TS_SIGN_UNSIGNED
|
||||
if _spec_name_eq(text, start, end, "int8") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "int16") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "int32") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "int64") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intfast8") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intfast16") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intfast32") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intfast64") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intleast8") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intleast16") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intleast32") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intleast64") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "short") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "long") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "longlong") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "ptrdifft") != 0: return TS_SIGN_SIGNED
|
||||
if _spec_name_eq(text, start, end, "intptr") != 0: return TS_SIGN_SIGNED
|
||||
# --- builtin lowercase scalars ---
|
||||
if _spec_name_eq(text, start, end, "bool") != 0: return TS_SIGN_UNSIGNED
|
||||
# --- t/c core signed primitives ---
|
||||
|
||||
@@ -1,223 +1,10 @@
|
||||
# ViperC
|
||||
|
||||
[English](#english) | [中文](#中文)
|
||||
|
||||
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**.
|
||||
|
||||
**用 Viper 语言编写的 Viper 编译器**,且**已完成自举**:ViperC 能编译自身源码并产出 `viperc.exe`。它把 Viper 源码翻译为 LLVM IR,再调用 `llc` + `clang++` 链接为 Windows 原生可执行文件。第一代 CPython 编译器([TransPyC](https://git.gvsds.com/GVSDS/TransPyC))现在只作为自举**种子**保留。
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
<a id="中文"></a>
|
||||
# ViperC
|
||||
|
||||
## 中文
|
||||
|
||||
### 这是什么
|
||||
|
||||
`ViperC` 是 Viper 的第二代编译器:**用 Viper 自己写、也被 Viper 自己编译**。整条链路上没有任何 Python 运行时参与编译 —— 词法/语法分析走 `includes/ast`(自研解析器,9 个文件),IR 生成走 `App/lib/core/Gen/`,目标代码由 `llc` 与 `clang++` 产出。
|
||||
|
||||
自举链路:
|
||||
|
||||
```text
|
||||
TransPyC(种子,CPython 实现)
|
||||
│ 只做一次:把 ViperC 源码编译成 viperc.exe
|
||||
▼
|
||||
viperc.exe(第一阶段产物)
|
||||
│ 自举:用这个 exe 再编译 ViperC 自身
|
||||
▼
|
||||
viperc.exe(自举产物,当前仓库随包/随 exe\ 提供的那份)
|
||||
│
|
||||
└─ 用它编译任意 Viper 工程(ViperApps\*、Test\* …)
|
||||
```
|
||||
|
||||
现状与规模(本机实测):
|
||||
|
||||
| 项 | 值 |
|
||||
| --- | --- |
|
||||
| 编译器源码 | `App/` 下 **82 个 `.vp` 文件,63,783 行** |
|
||||
| 其中翻译器核心 | `App/lib/core/Gen/` **54 个文件,47,297 行** |
|
||||
| 阶段与合并 | `App/lib/Phase/` 6 文件 5,339 行;`App/lib/Stub/` 9 文件 6,207 行 |
|
||||
| 工程与工具 | `App/lib/Projectrans/` 3 文件 712 行 |
|
||||
| 自测/回归套件 | 工作区根 `Test\`(**独立仓库**):**97 个工程,129 个 `.vp`**,由 `Test\SequentialRegressionTest.py` 驱动 |
|
||||
| 平台 | Windows x64(源码直接 `import win32.*`) |
|
||||
|
||||
> 自举可行性分析与历史数据见 [`wiki/13-bootstrapping.md`](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/13-bootstrapping.md)(该文标注:Phase A → Phase B/C 已跑通,编译器已脱离 Python 运行时)。
|
||||
|
||||
### 目录结构
|
||||
|
||||
```text
|
||||
ViperC/
|
||||
├── project.vpj # 工程配置(source_dir=./App, build_dir=./__vpcbuild__, 输出 viperc.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\`(独立仓库,97 个工程 129 个 `.vp`),
|
||||
> 由 `Test\SequentialRegressionTest.py` 驱动(`--exe exe\viperc.exe`)。
|
||||
|
||||
### 前提条件
|
||||
|
||||
| 工具 | 说明 |
|
||||
| --- | --- |
|
||||
| Windows 10/11 x64 | 编译器源码直接 `import win32.*`,当前仅 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\ # 第一代编译器(种子)
|
||||
├── ViperC\ # 本仓库
|
||||
├── Test\ # 回归套件(独立仓库)
|
||||
├── exe\ # 随包提供的自举产物(viperc.exe)
|
||||
└── includes\ # Viper 标准库 + includes/ast 解析器
|
||||
```
|
||||
|
||||
### 快速开始
|
||||
|
||||
```bat
|
||||
:: 日常:直接用自举产物编译任意工程
|
||||
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean
|
||||
|
||||
:: 编译并立即运行
|
||||
exe\viperc.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
|
||||
|
||||
:: 只有在需要重建编译器本体(自举种子)时,才动用 TransPyC
|
||||
python TransPyC\Projectrans.py --project ViperC\project.vpj --clean
|
||||
```
|
||||
|
||||
产物位置 = `<工程目录>\<build_dir>\output\<linker.output>`,即默认 `<工程>\__vpcbuild__\output\app.exe`;
|
||||
ViperC 自身因 `linker.output` 为 `viperc.exe`,产物是 `ViperC\__vpcbuild__\output\viperc.exe`。
|
||||
|
||||
跑回归套件(工作区根 `Test\`,独立仓库):
|
||||
|
||||
```bat
|
||||
python Test\SequentialRegressionTest.py --exe exe\viperc.exe
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
| 参数 | 含义 |
|
||||
| --- | --- |
|
||||
| `--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 → `viperc.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 / ViperC 版本、提交号、目标三元组与 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 | ViperC |
|
||||
| --- | --- | --- |
|
||||
| 实现语言 | CPython | Viper |
|
||||
| 定位 | 自举**种子**(只用来生成稳定的 ViperC) | **现行编译器** |
|
||||
| 自举状态 | — | ✅ 已完成,能编译自身 |
|
||||
| 入口 | `TransPyC/Projectrans.py` | `App/main.vp` |
|
||||
| AST 解析 | Python `ast` / 自研 vast | `includes/ast` 自研(9 文件) |
|
||||
| 工程配置 | `project.json` | `project.vpj` |
|
||||
| 平台 | Windows / Linux | Windows(`win32.*`) |
|
||||
|
||||
### 进一步阅读
|
||||
|
||||
- [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` / `viperbuild` 元编程)](https://git.gvsds.com/GVSDS/TransPyC/blob/main/wiki/12-compilation-system.md)
|
||||
- [ViperOS 操作系统(用 Viper 写的 OS)](https://git.gvsds.com/GVSDS/ViperOS)
|
||||
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**.
|
||||
|
||||
---
|
||||
|
||||
@@ -432,4 +219,4 @@ out-of-bounds writes.
|
||||
- [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)
|
||||
- [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)
|
||||
Reference in new issue
Block a user