feat(gen): 字节序 bswap 改为按目标端序判断(BigEndian/LittleEndian 都相对目标),原语改名 apply_byte_order_bswap

This commit is contained in:
TermiNexus committed 2026-10-13 13:29:03 +08:00
1 parent 444e87dd7d
commit cb2dc4cb3a
5 files changed
+51 -27

No files matched your search

+12 -12
View File
@@ -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 所有权记账:必须先记账再写值(覆盖释放要读到旧值);
+5 -5
View File
@@ -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)
+14 -8
View File
@@ -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
+2 -2
View File
@@ -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
+18
View File
@@ -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