From cb2dc4cb3a294686467bddbe24e992eddfbd12a8 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 13 Oct 2026 13:29:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(gen):=20=E5=AD=97=E8=8A=82=E5=BA=8F=20bswa?= =?UTF-8?q?p=20=E6=94=B9=E4=B8=BA=E6=8C=89=E7=9B=AE=E6=A0=87=E7=AB=AF?= =?UTF-8?q?=E5=BA=8F=E5=88=A4=E6=96=AD=EF=BC=88BigEndian/LittleEndian=20?= =?UTF-8?q?=E9=83=BD=E7=9B=B8=E5=AF=B9=E7=9B=AE=E6=A0=87=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E5=8E=9F=E8=AF=AD=E6=94=B9=E5=90=8D=20apply=5Fbyte=5Forder=5Fb?= =?UTF-8?q?swap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/core/Gen/AssignGenMixin.vp | 24 ++++++++++++------------ App/lib/core/Gen/Expr/ExprGenAttr.vp | 10 +++++----- App/lib/core/Gen/Expr/ExprGenBase.vp | 22 ++++++++++++++-------- App/lib/core/Gen/Expr/ExprGenName.vp | 4 ++-- App/lib/core/Gen/Type/TypeGenResolve.vp | 18 ++++++++++++++++++ 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index fa1dc10..99cc14e 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -1429,7 +1429,7 @@ novirtual class AssignGenMixin: store_val: mlirlite.Value* = elem_val if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee != 0: store_val = self.Gen.ExprGenBase.coerce_to_type(builder, elem_val, elem_ptr.Ty.Pointee, elem_node) # BigEndian array: store each element byte-swapped (pairs the un-swap on element read). - store_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, store_val, byte_order) + store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, byte_order) mlirlite.build_store(builder, store_val, elem_ptr) return 0 @@ -1588,7 +1588,7 @@ novirtual class AssignGenMixin: if cur_val is None: # fail-fast: attribute current value load failed self.Gen.TypeGenErr.fatal_error(target, "cannot load the current value of the attribute", "TypeError") - cur_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, cur_val, bo_fa) + cur_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, cur_val, bo_fa) # 3. Translate the RHS value rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans) @@ -1609,7 +1609,7 @@ novirtual class AssignGenMixin: self.Gen.TypeGenErr.fatal_error(target, "type coercion failed", "TypeError") # BigEndian field: re-swap the result so the slot keeps the byte-swapped pattern # (pairs the un-swap of cur_val above, keeping memory big-endian). - result = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, result, bo_fa) + result = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, result, bo_fa) mlirlite.build_store(builder, result, field_ptr, vol_fa) return 0 @@ -1661,7 +1661,7 @@ novirtual class AssignGenMixin: if cur_val is None: # fail-fast: element current value load failed self.Gen.TypeGenErr.fatal_error(target, "cannot load the current value of the subscript element", "TypeError") - cur_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, cur_val, bo_sa) + cur_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, cur_val, bo_sa) # 3. Translate the RHS value rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans) @@ -1681,7 +1681,7 @@ novirtual class AssignGenMixin: # fail-fast: type coercion failed self.Gen.TypeGenErr.fatal_error(target, "type coercion failed", "TypeError") # BigEndian array element: re-swap the result so the slot stays byte-swapped (pairs cur_val above). - result = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, result, bo_sa) + result = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, result, bo_sa) mlirlite.build_store(builder, result, elem_ptr, vol_sa) return 0 @@ -1770,7 +1770,7 @@ novirtual class AssignGenMixin: target_ty = target_alloca.Ty.Pointee cur_val = mlirlite.build_load(builder, target_ty, target_alloca, vol_aa) # BigEndian local: un-swap the loaded slot value before the arithmetic (paired with the store below). - cur_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, cur_val, bo_aa) + cur_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, cur_val, bo_aa) if cur_val is None: # fail-fast: target variable current value load failed @@ -1838,7 +1838,7 @@ novirtual class AssignGenMixin: # Ordinary local variable if target_alloca is not None: # BigEndian local: re-swap the result so the slot keeps the byte-swapped pattern (pairs the load above). - result = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, result, bo_aa) + result = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, result, bo_aa) mlirlite.build_store(builder, result, target_alloca, vol_aa) return 0 @@ -2466,7 +2466,7 @@ novirtual class AssignGenMixin: # BigEndian local: store the byte-swapped value so the slot is big-endian in memory # (pairs the un-swap done by ExprGenName.translate_name_value on the read path). decl_bo: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) - rhs_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, rhs_val, decl_bo) + rhs_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, rhs_val, decl_bo) mlirlite.build_store(builder, rhs_val, alloca, vol_laa) # P2 willFree:带注解声明/赋值后清除失效态(新变量本就为 0;复用槽兜底) self.Gen.VarGen.clear_var_freed(trans.SymTab, nm.id) @@ -2569,7 +2569,7 @@ novirtual class AssignGenMixin: if sub_lv_b.value is not None and sub_lv_b.value.kind() == ast.ASTKind.Name: sub_nm_lv: ast.Name* = ast.Name*(sub_lv_b.value) if sub_nm_lv.id is not None: bo_lv = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_lv.id)) - store_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, store_val, bo_lv) + store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_lv) # t.CVolatile 字段 / rawptr[t.CVolatile|T] 基址:写出同样带 volatile。 vol_lv: int = 0 if is_attr != 0: @@ -2794,7 +2794,7 @@ novirtual class AssignGenMixin: if sub_wr is not None and sub_wr.value is not None and sub_wr.value.kind() == ast.ASTKind.Name: sub_nm_sw: ast.Name* = ast.Name*(sub_wr.value) if sub_nm_sw.id is not None: bo_sw = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_sw.id)) - store_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, store_val, bo_sw) + store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_sw) # rawptr[t.CVolatile|T] base: p[i] = v stores with volatile when the base is resolvable vol_sw: int = 0 if sub_wr is not None: vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_wr.value) @@ -2932,7 +2932,7 @@ novirtual class AssignGenMixin: # BigEndian field: store the byte-swapped value so the slot is big-endian in memory # (pairs the un-swap done by ExprGenAttr.translate_attribute on the load path). bo_fw: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target) - store_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, store_val, bo_fw) + store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_fw) # t.CVolatile field: store with the volatile qualifier vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target) # rawptr[t.CVolatile|T] base: p.field = v also stores with volatile @@ -3045,7 +3045,7 @@ novirtual class AssignGenMixin: # BigEndian local variable: store the byte-swapped value so the slot is big-endian in # memory (pairs the un-swap done by ExprGenName.translate_name_value on the read path). bo_las: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) - store_val = self.Gen.ExprGenBase.apply_bswap_if_big(pool, builder, store_val, bo_las) + store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_las) # t.CVolatile local variable: store with the volatile qualifier vol_las: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) # M3 所有权记账:必须先记账再写值(覆盖释放要读到旧值); diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 093b7fd..46aa82c 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -651,10 +651,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if cast_ptr_rn is not None: rn_alias_val: mlirlite.Value* = mlirlite.build_load(builder, decl_ty_rn, cast_ptr_rn) # BigEndian REnum payload alias: un-swap the load (pairing the write side). - return self.apply_bswap_if_big(pool, builder, rn_alias_val, alias_fe.ByteOrder) + return self.apply_byte_order_bswap(pool, builder, rn_alias_val, alias_fe.ByteOrder) rn_slot_val: mlirlite.Value* = mlirlite.build_load(builder, alias_fe.Ty, alias_ptr, vol_ba_rd) # BigEndian REnum slot: un-swap the load (pairing the write side). - return self.apply_bswap_if_big(pool, builder, rn_slot_val, alias_fe.ByteOrder) + return self.apply_byte_order_bswap(pool, builder, rn_slot_val, alias_fe.ByteOrder) # property getter fallback: when obj.attr is a @property/@x.getter method, emit a call # (try before reporting a field-not-found error, to avoid a misleading message) if field_info is None and at.value is not None and trans is not None: @@ -717,7 +717,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if casted is not None: union_val_rd: mlirlite.Value* = mlirlite.build_load(builder, field_ty, casted, field_vol_rd) # BigEndian field: the slot holds the byte-swapped pattern, so un-swap the load. - return self.apply_bswap_if_big(pool, builder, union_val_rd, field_info.ByteOrder) + return self.apply_byte_order_bswap(pool, builder, union_val_rd, field_info.ByteOrder) return None # normal struct: GEP + load field_idx: int = field_info.Index @@ -735,14 +735,14 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if cast_ptr_rd is not None: rn_val_rd: mlirlite.Value* = mlirlite.build_load(builder, decl_ty_rd, cast_ptr_rd, field_vol_rd) # BigEndian REnum payload slot: un-swap the load, pairing with the write side. - return self.apply_bswap_if_big(pool, builder, rn_val_rd, field_info.ByteOrder) + return self.apply_byte_order_bswap(pool, builder, rn_val_rd, field_info.ByteOrder) # an array-typed field cannot be loaded as an SSA value; return the field pointer for # later indexing: self.state[0] → GEP the state field → GEP the array element if self.is_array_type(field_ty) != 0: return field_ptr loaded_val: mlirlite.Value* = mlirlite.build_load(builder, field_ty, field_ptr, field_vol_rd) # BigEndian field: the memory slot holds the byte-swapped pattern, so un-swap the # loaded value; a no-op for non-16/32/64-bit ints (pointers/aggregates unaffected). - loaded_val = self.apply_bswap_if_big(pool, builder, loaded_val, field_info.ByteOrder) + loaded_val = self.apply_byte_order_bswap(pool, builder, loaded_val, field_info.ByteOrder) # a union-typed field (e.g. Token*) is compiled as i8*; when AnnotClassName points to a # concrete struct type, bitcast the loaded pointer (value→pointer bitcast would be invalid). fc_annot: str = self.Gen.StructGen.get_field_annot_class_name(field_info) diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index f45a2c4..930635c 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -149,20 +149,26 @@ novirtual class ExprGenBase: string.memset(ty, 0, mlirlite.LLVMType.__sizeof__()) *ty = *mlirlite.LLVMType.Int(bits) return ty - # apply_bswap_if_big - byte-order bswap primitive (TPC parity: BaseGen._apply_bswap_if_big). - # Only BigEndian (byte_order==1) on a 16/32/64-bit integer emits `llvm.bswap`; every other case - # returns val unchanged. byte_order==0 (native) must be zero-cost: no IR is emitted at all, and - # 2 (LittleEndian) also returns unchanged because the host is little-endian (same as TPC, which - # only swaps for 'big'), so the in-memory slot stays little-endian and no swap is needed. + # apply_byte_order_bswap - byte-order bswap primitive (TPC parity: BaseGen._apply_bswap_if_big, + # extended to judge BOTH markers against the target's native order). + # Swapping is needed only when the declared encoding differs from the target's native order, + # so: BigEndian field on a little-endian target -> swap; LittleEndian field on a big-endian + # target -> swap; declared order == target order -> no swap. byte_order==0 (unspecified) + # means native and is always zero-cost: no IR is emitted at all. 16/32/64-bit integers only, + # matching TPC (i8/floats/pointers/aggregates are never swapped). # Call name: bare "llvm.bswap" (no width suffix). build_call emits `call i32 @llvm.bswap(i32 %x)`; # __function.vp:2607 detects a bare `@llvm.*` (non-`llvm.mlir.`) callee and renders it as # `llvm.call_intrinsic "llvm.bswap"(...)`, and mlir-translate mangles the name from the operand # type to `llvm.bswap.i32` (README.md:367-378, the same path used by CallGenVa.vp:123 `llvm.va_start` # -> `llvm.va_start.p0`). Passing `llvm.bswap.i32` explicitly is unnecessary and would break the # uniform mangle path, so the un-suffixed name is used. - def apply_bswap_if_big(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, byte_order: int) -> mlirlite.Value*: - """Return llvm.bswap(val) when byte_order==1 and val is a 16/32/64-bit int; else val unchanged.""" - if byte_order != 1: return val + def apply_byte_order_bswap(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, byte_order: int) -> mlirlite.Value*: + """Return llvm.bswap(val) when val's declared byte_order differs from the target order; else val.""" + if byte_order == 0: return val + # 1=BigEndian, 2=LittleEndian; compare against the target's native order. + want_big: int = 0 + if byte_order == 1: want_big = 1 + if want_big == self.Gen.TypeGenResolve.target_is_big_endian(): return val if val is None or val.Ty is None: return val bits: int = self.get_llvm_type_bits(val.Ty) if bits != 16 and bits != 32 and bits != 64: return val diff --git a/App/lib/core/Gen/Expr/ExprGenName.vp b/App/lib/core/Gen/Expr/ExprGenName.vp index c880b5e..b029a46 100644 --- a/App/lib/core/Gen/Expr/ExprGenName.vp +++ b/App/lib/core/Gen/Expr/ExprGenName.vp @@ -180,7 +180,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): bo_l = self.Gen.VarGen.var_byte_order(ve_l) local_val_l: mlirlite.Value* = mlirlite.build_load(builder, load_ty, alloca, vol_l) # BigEndian local: the slot is byte-swapped, so un-swap the load (pairs the store in AssignGenMixin). - return self.apply_bswap_if_big(pool, builder, local_val_l, bo_l) + return self.apply_byte_order_bswap(pool, builder, local_val_l, bo_l) # REPL session variables: a name missing from the local table is read back from its ctx slot, # checked after locals and before module aliases; non-REPL behavior is unchanged. @@ -571,7 +571,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): # on load (pairs the element store in AssignGenMixin). Non-Name bases are # not covered (matches TPC, which also keys off the base Name). bo_arr_rd: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) - return self.apply_bswap_if_big(pool, builder, arr_val_rd, bo_arr_rd) + return self.apply_byte_order_bswap(pool, builder, arr_val_rd, bo_arr_rd) return None case mlirlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v): # list[T] value variable (an unannotated assignment rhs returns a list diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 5e14161..711afc6 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -1495,6 +1495,24 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): 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 target_is_big_endian(self) -> int: + """Whether the compilation target is big-endian (1) or little-endian (0). + The configured data layout wins: LLVM's first data-layout field is the endianness + marker ('e' = little, 'E' = big; omitted means little). Without a data layout, fall + back to the triple's `_be` arch suffix (e.g. aarch64_be-...); otherwise little, which + is the default target. Both byte-order markers are judged against this: a `t.BigEndian` + field on a big-endian target needs no swap, and a `t.LittleEndian` field on a + big-endian target does.""" + dl: str = Config.TargetDataLayout + if dl is not None and dl[0] != '\0': + if dl[0] == 'E': return 1 + if dl[0] == 'e': return 0 + tr: str = Config.TargetTriple + if tr is not None: + ei: int = 0 + while tr[ei] != '\0' and tr[ei] != '-': ei += 1 + if ei >= 3 and tr[ei - 3] == '_' and tr[ei - 2] == 'b' and tr[ei - 1] == 'e': return 1 + 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