diff --git a/App/lib/core/Gen/Class/ClassGenDef.vp b/App/lib/core/Gen/Class/ClassGenDef.vp index 142ca11..4dd727c 100644 --- a/App/lib/core/Gen/Class/ClassGenDef.vp +++ b/App/lib/core/Gen/Class/ClassGenDef.vp @@ -162,9 +162,13 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): # Field const-object flag buffer (1 = annotation has t.CConst and the type is not # a pointer), parallel to field_volatile_buf with values 0/1. field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) + # Field byte-order buffer (0 = native, 1 = t.BigEndian, 2 = t.LittleEndian), parallel + # to field_volatile_buf; from the `X | t.BigEndian` annotation union. + field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX) if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0 if field_annot_buf is None or field_volatile_buf is None: return 0 if field_pointee_volatile_buf is None or field_const_buf is None: return 0 + if 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_defaults_buf, 0, 8 * GenTypes.FIELD_MAX) @@ -172,6 +176,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): 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 @@ -221,6 +226,8 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile) # Inherit the const-object flag (unchanged after flattening) field_const_buf[field_count] = sizet(pfe.IsConst) + # Inherit the byte-order flag (unchanged after flattening) + field_byteorder_buf[field_count] = sizet(pfe.ByteOrder) field_count += 1 children: list[ast.AST*]* = cd.children @@ -340,11 +347,17 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): # through the object fail-fasts, matching TPC's Gen.field_const_flags. if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0: field_const_buf[field_count] = 1 else: field_const_buf[field_count] = 0 + # Byte-order marker (t.BigEndian / t.LittleEndian) -> 1 / 2; annotation `|` + # means "and", so `X | t.BigEndian` is a BigEndian-encoded X field. + if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1 + elif self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2 + else: field_byteorder_buf[field_count] = 0 else: field_annot_buf[field_count] = 0 field_volatile_buf[field_count] = 0 field_pointee_volatile_buf[field_count] = 0 field_const_buf[field_count] = 0 + field_byteorder_buf[field_count] = 0 field_count += 1 if field_count == 0: @@ -518,6 +531,10 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0: fe_pv: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add) if fe_pv is not None: fe_pv.PointeeVolatile = 1 + # byte-order field flag (field_byteorder_buf[fi] corresponds 1:1 with add_field order) + if fidx_add >= 0 and field_byteorder_buf[fi] != 0: + fe_bo: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add) + if fe_bo is not None: fe_bo.ByteOrder = int(field_byteorder_buf[fi]) # const object field flag (field_const_buf[fi] corresponds 1:1 with add_field order) if fidx_add >= 0 and field_const_buf[fi] != 0: fe_c: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add) diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 1eaa88a..8be3271 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -106,6 +106,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if string.strcmp(name, "CRef") == 0: return 1 if string.strcmp(name, "CTempRef") == 0: return 1 if string.strcmp(name, "CAutoPtr") == 0: return 1 + # Byte-order markers (t.BigEndian / t.LittleEndian): decorator markers used as + # `X | t.BigEndian`; recognized here so they are not taken for an unregistered class. + if string.strcmp(name, "BigEndian") == 0: return 1 + if string.strcmp(name, "LittleEndian") == 0: return 1 return 0 def _attr_module_path_str(self, pool: memhub.MemBuddy*, node: ast.AST*) -> str: """Convert a module-reference AST into a dotted module path; None for non-module forms.""" diff --git a/App/lib/core/Gen/GenTypes.vp b/App/lib/core/Gen/GenTypes.vp index 2259ef7..4d2935a 100644 --- a/App/lib/core/Gen/GenTypes.vp +++ b/App/lib/core/Gen/GenTypes.vp @@ -121,6 +121,10 @@ class FieldEntry: # 1=this field is a const object (annotation has t.CConst and the type is not a pointer): # assigning it via an object fails fast, same as VarEntry.IsConst. IsConst: int + # Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation + # union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets + # are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only). + ByteOrder: int # ============================================================ diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index f1a3514..8842409 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -2493,6 +2493,8 @@ novirtual class StructGenMixin: # Explicitly zero the const flag (not relying on the entry's zero-init): ClassGenDef sets it to 1 only for a confirmed const object, so explicit zeroing prevents a false "uninitialized read as 1". fe.IsConst = 0 + # Byte order is filled in by ClassGenDef from the field annotation (0 = native here). + fe.ByteOrder = 0 struct_entry.FieldCount = idx + 1 return idx diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 312c7c0..5e14161 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -1443,6 +1443,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): return mlirlite.Int1(pool) # ptrdiff_t elif string.strcmp(type_name, "CPtrDiffT") == 0: return mlirlite.Int64(pool) + # Byte-order markers written alone (no real type on the other side) degrade to CInt, + # matching TPC: `t.BigEndian` alone -> BaseType=CInt() + ByteOrder='big'. + elif string.strcmp(type_name, "BigEndian") == 0: return mlirlite.Int32(pool) + elif string.strcmp(type_name, "LittleEndian") == 0: return mlirlite.Int32(pool) # stdint.vp aliases (INT/BYTE/DWORD/int8_t/size_t/_Bool ...) now come from its # CTypedef declarations; lowercase bare char/float/double stay here, int/bool use Name. @@ -1486,6 +1490,11 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if self._is_t_attr(node, "CRef", imported_modules) != 0: return 1 if self._is_t_attr(node, "CTempRef", imported_modules) != 0: return 1 return 0 + def _is_byteorder_marker(self, node: ast.AST*, imported_modules: str) -> int: + """Whether a node is a byte-order marker: 0=none / 1=BigEndian / 2=LittleEndian.""" + if self._is_t_attr(node, "BigEndian", imported_modules) != 0: return 1 + if self._is_t_attr(node, "LittleEndian", imported_modules) != 0: return 2 + return 0 def _ref_marker_name_mode(self, node: ast.AST*) -> int: """Syntactic value-reference marker test (no import context): 0=none / 1=CRef / 2=CTempRef.""" if node is None: return 0 @@ -1640,6 +1649,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if string.strcmp(name, "CExport") == 0: return 1 if string.strcmp(name, "CExtern") == 0: return 1 if string.strcmp(name, "CInline") == 0: return 1 + # Byte-order markers are decorators, not types: never let them be extracted as a + # class name (which would make the field degrade to an unknown struct lookup). + if string.strcmp(name, "BigEndian") == 0: return 1 + if string.strcmp(name, "LittleEndian") == 0: return 1 return 0 def extract_class_name_from_annotation(self, node: ast.AST*, imported_modules: str, pool: memhub.MemBuddy* = None, from_imports: str = None) -> str: @@ -2332,6 +2345,19 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if k == ast.ASTKind.BinOp: bop: ast.BinOp* = ast.BinOp*(node) if bop.op == ast.OpKind.BitOr: + # Byte-order markers: annotation `|` means "and", so `ActualType | t.BigEndian` + # yields the actual type; the byte order itself is recorded on the field entry + # (see ClassGenDef field_byteorder_buf), not carried in the LLVM type. + left_bo: int = self._is_byteorder_marker(bop.left, imported_modules) + right_bo: int = self._is_byteorder_marker(bop.right, imported_modules) + if left_bo != 0 or right_bo != 0: + other_node_bo: ast.AST* = bop.right + if right_bo != 0: other_node_bo = bop.left + if other_node_bo is not None: + other_ty_bo: mlirlite.LLVMType* = self.resolve_annotation_type(pool, other_node_bo, imported_modules, from_imports, trans, depth + 1) + if other_ty_bo is not None: return other_ty_bo + # Markers on both sides (e.g. `t.BigEndian | t.LittleEndian`) -> CInt. + return mlirlite.Int32(pool) # Detect the t.CPtr marker (supports t.CPtr and the bare from t import CPtr name). # CRef/CTempRef (value-reference parameter markers) resolve identically here: the # parameter ABI is Ptr(other); the ref/temp distinction only drives call-site auto-address.