From 32599a08c18d8ab17316ae9e8a38c93dd5089893 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 13 Oct 2026 12:30:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(stub):=20.vpi=20=E8=AF=BB=E5=8F=96=E4=BE=A7?= =?UTF-8?q?=E5=A4=8D=E5=88=BB=E5=AD=97=E6=AE=B5=E6=A0=87=E8=AE=B0=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=EF=BC=88volatile/pointee-volatile/const/=E5=AD=97?= =?UTF-8?q?=E8=8A=82=E5=BA=8F=EF=BC=89=EF=BC=8C=E8=B7=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E4=B8=A2=E5=AD=97=E6=AE=B5=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/Stub/StubGen/VpiReader.vp | 40 ++++++++++++++++++++++++++++++- App/lib/core/Gen/GenApi.vp | 14 +++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 8078f42..1bfa96a 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -2645,9 +2645,25 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul # 2. collect field types (AnnAssign children, mirroring ClassGenDef.translate_class_def) field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) + # Per-field marker buffers, index-aligned with field_names_buf / add_field order. The .vpi text + # keeps each field's raw annotation (Converter._PyiTypeStr renders BinOp/Attribute verbatim, so + # `X | t.BigEndian` / `t.CVolatile | int` survive), so the same markers ClassGenDef extracts on + # the definition side must be re-extracted here; otherwise a struct referenced across a module + # boundary loses its volatile/const/byte-order semantics and the same field compiles differently + # per module (the bug this fixes). + field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) + field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) + field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) + field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) if field_names_buf is None or field_types_buf is None: return 0 + if field_volatile_buf is None or field_pointee_volatile_buf is None: return 0 + if field_const_buf is None or field_byteorder_buf is None: return 0 string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX) string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX) + string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX) + string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX) + string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX) + string.memset(field_byteorder_buf, 0, 8 * GenTypes.FIELD_MAX) field_count: int = 0 cls_children_c: list[ast.AST*]* = class_cd.children if cls_children_c is not None: @@ -2668,6 +2684,18 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul if field_count < GenTypes.FIELD_MAX: field_names_buf[field_count] = sizet(m_tgt_c.id) field_types_buf[field_count] = sizet(field_ty_c) + # Re-extract the field-level markers from the raw annotation, exactly as + # ClassGenDef.translate_class_def does on the definition side (same predicates, same + # precedence), so a cross-module reference gets identical field semantics. + annot_fc: ast.AST* = m_aa_c.annotation + if annot_fc is not None: + if GenApi.has_decorator_marker(annot_fc, "CVolatile", 0) != 0: field_volatile_buf[field_count] = 1 + if GenApi.has_pointee_volatile_marker(annot_fc) != 0: field_pointee_volatile_buf[field_count] = 1 + if GenApi.is_const_object_marker(annot_fc, field_ty_c) != 0: field_const_buf[field_count] = 1 + # Byte order: 1 = t.BigEndian / 2 = t.LittleEndian (0 stays native); + # annotation `|` means "and", so `X | t.BigEndian` is a BigEndian X. + if GenApi.has_decorator_marker(annot_fc, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1 + elif GenApi.has_decorator_marker(annot_fc, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2 field_count += 1 # 3. The vpi stub has no class fields -> field_count is 0; registering an empty class would make # the source module hit an empty struct and raise InternalCompilerError, so do not register. @@ -2734,7 +2762,17 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul for afi_c in range(int(field_count)): af_name_c: str = str(field_names_buf[afi_c]) af_ty_c: mlirlite.LLVMType* = (mlirlite.LLVMType*)(field_types_buf[afi_c]) - GenApi.add_field(pool, entry_c, af_name_c, af_ty_c) + af_idx_c: int = GenApi.add_field(pool, entry_c, af_name_c, af_ty_c) + # Restore the per-field markers AFTER add_field, because StructGenMixin.add_field always + # resets them to 0 (it cannot see the annotation). Buffers are index-aligned with add_field + # order, so afi_c indexes them directly (mirrors ClassGenDef's post-add_field set). + if af_idx_c >= 0 and GenApi._g_gen is not None: + af_fe_c: GenTypes.FieldEntry* = GenApi._g_gen.StructGen._get_field_entry(entry_c, af_idx_c) + if af_fe_c is not None: + if field_volatile_buf[afi_c] != 0: af_fe_c.IsVolatile = 1 + if field_pointee_volatile_buf[afi_c] != 0: af_fe_c.PointeeVolatile = 1 + if field_const_buf[afi_c] != 0: af_fe_c.IsConst = 1 + if field_byteorder_buf[afi_c] != 0: af_fe_c.ByteOrder = int(field_byteorder_buf[afi_c]) GenApi.add_visible_struct(pool, real_name) return 1 diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index cf6f17d..5cb8dab 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -136,6 +136,20 @@ def has_decorator_marker(node: ast.AST*, marker_name: str, depth: int = 0) -> in return 0 +# ---------------- has_pointee_volatile_marker (delegates to TypeGenErr.has_pointee_volatile_marker) ---------------- +def has_pointee_volatile_marker(node: ast.AST*) -> int: + """Delegate to gen.has_pointee_volatile_marker (rawptr[t.CVolatile | T] field/var pointee)""" + if _g_gen is not None: return _g_gen.TypeGenErr.has_pointee_volatile_marker(node) + return 0 + + +# ---------------- is_const_object_marker (delegates to TypeGenErr.is_const_object_marker) ---------------- +def is_const_object_marker(node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int: + """Delegate to gen.is_const_object_marker (t.CConst non-pointer object field/var)""" + if _g_gen is not None: return _g_gen.TypeGenErr.is_const_object_marker(node, var_ty) + return 0 + + # ---------------- _get_type_name_from_annotation (delegates to TypeGenResolve._get_type_name_from_annotation) ---------------- def _get_type_name_from_annotation(pool: memhub.MemBuddy*, node: ast.AST*) -> str: """Delegate to gen._get_type_name_from_annotation"""