feat(gen): 字节序 bswap 代码生成——字段/局部变量/数组元素读写处成对 llvm.bswap(仅 BigEndian 且 16/32/64 位整数)
This commit is contained in:
1 parent
5d15d01e21
commit
37c4b0da3b
6 files changed
+158
-12
No files matched your search
@@ -1331,8 +1331,12 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
# _init_global_array_from_list - initialize a global array from a list literal
|
||||
def _init_global_array_from_list(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, array_alloca: mlirlite.Value*, list_node: ast.AST*, trans: HT.Translator*, owner_ctx: str = None) -> int:
|
||||
"""Initialize a global array from a list literal via per-element GEP+store."""
|
||||
def _init_global_array_from_list(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, array_alloca: mlirlite.Value*, list_node: ast.AST*, trans: HT.Translator*, owner_ctx: str = None, byte_order: int = 0) -> int:
|
||||
"""Initialize a global array from a list literal via per-element GEP+store.
|
||||
|
||||
byte_order (0=native / 1=BigEndian / 2=LittleEndian): when the array variable is big-endian
|
||||
every element is stored byte-swapped, so a later element read (which un-swaps) sees the same
|
||||
value; left at 0 for module globals / struct-field defaults (not registered)."""
|
||||
if builder is None or array_alloca is None or list_node is None: return 1
|
||||
|
||||
# Get the array type (pointee of [N x elem_ty]*)
|
||||
@@ -1415,7 +1419,7 @@ novirtual class AssignGenMixin:
|
||||
# Null check for nested element: use is not None (not integer 0)
|
||||
if elem_node is not None and elem_node.kind() == ast.ASTKind.List: inner_arr = 1
|
||||
if inner_arr != 0:
|
||||
self._init_global_array_from_list(builder, pool, mod, elem_ptr, elem_node, trans, owner_ctx)
|
||||
self._init_global_array_from_list(builder, pool, mod, elem_ptr, elem_node, trans, owner_ctx, byte_order)
|
||||
continue
|
||||
elem_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node, None, 0, trans)
|
||||
if elem_val is None:
|
||||
@@ -1424,6 +1428,8 @@ novirtual class AssignGenMixin:
|
||||
# Type coercion (e.g. i32 -> i8); Pointee is statically i64 so compare with != 0
|
||||
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)
|
||||
mlirlite.build_store(builder, store_val, elem_ptr)
|
||||
|
||||
return 0
|
||||
@@ -1571,6 +1577,9 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(target, "field type information missing", "TypeError")
|
||||
# t.CVolatile field: both load and store carry the volatile qualifier
|
||||
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
# BigEndian field: the memory slot holds the byte-swapped pattern, so the current value must
|
||||
# be un-swapped before it takes part in the arithmetic, and the result re-swapped on store.
|
||||
bo_fa: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
# rawptr[t.CVolatile|T] base: field load/store also carry volatile
|
||||
at_aug: ast.Attribute* = ast.Attribute*(target)
|
||||
if at_aug is not None:
|
||||
@@ -1579,6 +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)
|
||||
|
||||
# 3. Translate the RHS value
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans)
|
||||
@@ -1597,6 +1607,9 @@ novirtual class AssignGenMixin:
|
||||
if result is None:
|
||||
# fail-fast: type coercion failed
|
||||
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)
|
||||
mlirlite.build_store(builder, result, field_ptr, vol_fa)
|
||||
return 0
|
||||
|
||||
@@ -1637,10 +1650,18 @@ novirtual class AssignGenMixin:
|
||||
vol_sa: int = 0
|
||||
sub_aug: ast.Subscript* = ast.Subscript*(target)
|
||||
if sub_aug is not None: vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_aug.value)
|
||||
# BigEndian array variable (base is a Name with a registered byte order): element load/store
|
||||
# bswap (TPC HandlesAssign._StoreArrElemWithCoerceBswap). Non-Name bases yield 0 -> no swap.
|
||||
bo_sa: int = 0
|
||||
if sub_aug is not None and sub_aug.value is not None and sub_aug.value.kind() == ast.ASTKind.Name:
|
||||
sub_nm_aug: ast.Name* = ast.Name*(sub_aug.value)
|
||||
if sub_nm_aug.id is not None:
|
||||
bo_sa = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_aug.id))
|
||||
cur_val: mlirlite.Value* = mlirlite.build_load(builder, target_ty, elem_ptr, vol_sa)
|
||||
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)
|
||||
|
||||
# 3. Translate the RHS value
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans)
|
||||
@@ -1659,6 +1680,8 @@ novirtual class AssignGenMixin:
|
||||
if result is None:
|
||||
# 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)
|
||||
mlirlite.build_store(builder, result, elem_ptr, vol_sa)
|
||||
return 0
|
||||
|
||||
@@ -1715,6 +1738,9 @@ novirtual class AssignGenMixin:
|
||||
|
||||
# t.CVolatile variable: both load and store carry the volatile qualifier
|
||||
vol_aa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# BigEndian local variable: un-swap on load / re-swap on store so the arithmetic sees the native
|
||||
# value (local scope only; global/nonlocal are not registered in the byte-order table, like TPC).
|
||||
bo_aa: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
|
||||
# 2. Load the current value
|
||||
cur_val: mlirlite.Value* = None
|
||||
@@ -1743,6 +1769,8 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(target, "variable type information missing", "TypeError")
|
||||
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)
|
||||
|
||||
if cur_val is None:
|
||||
# fail-fast: target variable current value load failed
|
||||
@@ -1808,7 +1836,10 @@ novirtual class AssignGenMixin:
|
||||
if nl_ptr is not None: mlirlite.build_store(builder, result, nl_ptr)
|
||||
else:
|
||||
# Ordinary local variable
|
||||
if target_alloca is not None: mlirlite.build_store(builder, result, target_alloca, vol_aa)
|
||||
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)
|
||||
mlirlite.build_store(builder, result, target_alloca, vol_aa)
|
||||
|
||||
return 0
|
||||
|
||||
@@ -2292,6 +2323,11 @@ novirtual class AssignGenMixin:
|
||||
if cls_nm_hd is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, nm.id, cls_nm_hd)
|
||||
# Check for bytes* or str* (i8** semantics, 8-byte stride)
|
||||
if self._is_ptr_element_annotation(annot) != 0: self.Gen.VarGen.set_var_ptr_element(trans.SymTab, nm.id)
|
||||
# Byte-order marker (`T | t.BigEndian` / `T | t.LittleEndian`): register the variable's byte
|
||||
# order so the local load/store paths bswap (mirrors ClassGenDef's field-level detection).
|
||||
# A bare `t.BigEndian` annotation resolves to i32 upstream, so registration is unconditional.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "BigEndian", 0) != 0: self.Gen.VarGen.set_var_byte_order(trans.SymTab, nm.id, 1)
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(annot, "LittleEndian", 0) != 0: self.Gen.VarGen.set_var_byte_order(trans.SymTab, nm.id, 2)
|
||||
|
||||
new_vars: int = 0
|
||||
existing: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, nm.id)
|
||||
@@ -2325,7 +2361,10 @@ novirtual class AssignGenMixin:
|
||||
if local_arr_ty is not None:
|
||||
zero_arr: mlirlite.Value* = mlirlite.ConstZero(pool, local_arr_ty)
|
||||
if zero_arr is not None: mlirlite.build_store(builder, zero_arr, alloca)
|
||||
self._init_global_array_from_list(builder, pool, mod, alloca, aa.value, trans)
|
||||
# BigEndian array variable: pass its byte order so the literal elements are stored
|
||||
# byte-swapped (pairs the un-swap on element read).
|
||||
arr_list_bo: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
self._init_global_array_from_list(builder, pool, mod, alloca, aa.value, trans, None, arr_list_bo)
|
||||
else:
|
||||
# Set CurAssignAnnot/CurAssignNode for bare generic construction (sync across all four branches)
|
||||
trans.CurAssignAnnot = aa.annotation
|
||||
@@ -2414,6 +2453,10 @@ novirtual class AssignGenMixin:
|
||||
# M3 所有权记账:必须**先**记账再写值——覆盖释放要读到槽里的旧值
|
||||
# (`p: T* = new heap slife …` → 释放旧对象 + 影子置 1;`q: T* = p` → 交还所有权)
|
||||
self._slife_note_assign(pool, builder, mod, trans, alloca, aa.value, nm.id)
|
||||
# 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)
|
||||
mlirlite.build_store(builder, rhs_val, alloca, vol_laa)
|
||||
# P2 willFree:带注解声明/赋值后清除失效态(新变量本就为 0;复用槽兜底)
|
||||
self.Gen.VarGen.clear_var_freed(trans.SymTab, nm.id)
|
||||
@@ -2507,6 +2550,16 @@ novirtual class AssignGenMixin:
|
||||
if store_val is None:
|
||||
self.Gen.TypeGenErr.fatal_error(aa, "type coercion failed for the annotated assignment to an attribute/subscript", "TypeError")
|
||||
return 0
|
||||
# BigEndian field / array element: byte-swap before the store (pairs the read paths).
|
||||
bo_lv: int = 0
|
||||
if is_attr != 0:
|
||||
bo_lv = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
else:
|
||||
sub_lv_b: ast.Subscript* = ast.Subscript*(target)
|
||||
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)
|
||||
# t.CVolatile 字段 / rawptr[t.CVolatile|T] 基址:写出同样带 volatile。
|
||||
vol_lv: int = 0
|
||||
if is_attr != 0:
|
||||
@@ -2724,9 +2777,16 @@ novirtual class AssignGenMixin:
|
||||
self._reject_whole_object_copy_into_ptr_slot(pool, trans, target, elem_ptr, rhs_val)
|
||||
# Guard: coerce only when the element type is complete
|
||||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee != 0: store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, elem_ptr.Ty.Pointee, node, trans)
|
||||
# BigEndian array variable (base is a Name with a registered byte order): store the
|
||||
# byte-swapped element so the slot is big-endian in memory (pairs the element read path).
|
||||
bo_sw: int = 0
|
||||
sub_wr: ast.Subscript* = ast.Subscript*(target)
|
||||
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)
|
||||
# rawptr[t.CVolatile|T] base: p[i] = v stores with volatile when the base is resolvable
|
||||
vol_sw: int = 0
|
||||
sub_wr: ast.Subscript* = ast.Subscript*(target)
|
||||
if sub_wr is not None: vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_wr.value)
|
||||
mlirlite.build_store(builder, store_val, elem_ptr, vol_sw)
|
||||
continue
|
||||
@@ -2859,6 +2919,10 @@ novirtual class AssignGenMixin:
|
||||
# Get the field type and coerce rhs_val (e.g. i32 -> i64)
|
||||
store_val: mlirlite.Value* = rhs_val
|
||||
if field_ptr.Ty is not None and field_ptr.Ty.Pointee != 0: store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, field_ptr.Ty.Pointee, node, trans)
|
||||
# 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)
|
||||
# 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
|
||||
@@ -2968,6 +3032,10 @@ novirtual class AssignGenMixin:
|
||||
# Coerce the value to the alloca type (e.g. double -> float)
|
||||
store_val: mlirlite.Value* = rhs_val
|
||||
if alloca.Ty is not None and alloca.Ty.Pointee != 0: store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, alloca.Ty.Pointee, node, trans)
|
||||
# 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)
|
||||
# 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 所有权记账:必须先记账再写值(覆盖释放要读到旧值);
|
||||
|
||||
@@ -648,8 +648,13 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
decl_ptr_rn: mlirlite.LLVMType* = mlirlite.Ptr(pool, decl_ty_rn)
|
||||
if decl_ptr_rn is not None:
|
||||
cast_ptr_rn: mlirlite.Value* = mlirlite.build_bitcast(builder, alias_ptr, decl_ptr_rn)
|
||||
if cast_ptr_rn is not None: return mlirlite.build_load(builder, decl_ty_rn, cast_ptr_rn)
|
||||
return mlirlite.build_load(builder, alias_fe.Ty, alias_ptr, vol_ba_rd)
|
||||
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)
|
||||
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)
|
||||
# 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:
|
||||
@@ -709,7 +714,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.StructGen.is_union_by_type(struct_ty) == 1:
|
||||
field_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, field_ty)
|
||||
casted: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
|
||||
if casted is not None: return mlirlite.build_load(builder, field_ty, casted, field_vol_rd)
|
||||
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 None
|
||||
# normal struct: GEP + load
|
||||
field_idx: int = field_info.Index
|
||||
@@ -724,11 +732,17 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
decl_ptr_rd: mlirlite.LLVMType* = mlirlite.Ptr(pool, decl_ty_rd)
|
||||
if decl_ptr_rd is not None:
|
||||
cast_ptr_rd: mlirlite.Value* = mlirlite.build_bitcast(builder, field_ptr, decl_ptr_rd)
|
||||
if cast_ptr_rd is not None: return mlirlite.build_load(builder, decl_ty_rd, cast_ptr_rd, field_vol_rd)
|
||||
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)
|
||||
# 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)
|
||||
# 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)
|
||||
@@ -934,6 +948,16 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
return self.Gen.StructGen.field_is_const(fe)
|
||||
|
||||
# attr_field_byte_order - the field's byte order (0=native / 1=BigEndian / 2=LittleEndian) for the
|
||||
# write path, where get_attribute_ptr resolves the pointer but not the FieldEntry. Mirrors
|
||||
# attr_field_is_volatile/attr_field_is_const (same _resolve_attr_field_entry resolution: only a
|
||||
# Name base resolves; a nested/global receiver yields 0 -> no swap, never a wrong swap).
|
||||
def attr_field_byte_order(self, pool: memhub.MemBuddy*, trans: HT.Translator*, attr_node: ast.AST*) -> int:
|
||||
"""Return the obj.field field's byte order (0 = native/unknown)."""
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
if fe is None: return 0
|
||||
return fe.ByteOrder
|
||||
|
||||
def attr_field_decl_src(self, pool: memhub.MemBuddy*, trans: HT.Translator*, attr_node: ast.AST*) -> str:
|
||||
"""Return the field annotation source name for const diagnostics, or None."""
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
|
||||
@@ -149,6 +149,27 @@ 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.
|
||||
# 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
|
||||
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
|
||||
# Same-type in-and-out (iN -> iN); ret_ty=val.Ty keeps the call well-typed before mangling.
|
||||
swapped: mlirlite.Value* = mlirlite.build_call(builder, "llvm.bswap", val, 1, val.Ty, 0)
|
||||
if swapped is None: return val
|
||||
return swapped
|
||||
# _is_zero_int_literal - is the AST node the integer literal 0?
|
||||
# Used for ptr != 0 / ptr == 0, where 0 means NULL and is allowed.
|
||||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||||
|
||||
@@ -173,10 +173,14 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
return alloca
|
||||
# t.CVolatile local: load carries volatile
|
||||
vol_l: int = 0
|
||||
bo_l: int = 0
|
||||
if trans is not None:
|
||||
ve_l: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
||||
vol_l = self.Gen.VarGen.var_is_volatile(ve_l)
|
||||
return mlirlite.build_load(builder, load_ty, alloca, vol_l)
|
||||
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)
|
||||
|
||||
# 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.
|
||||
@@ -561,7 +565,13 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
case mlirlite.LLVMType.Array(elem_ty, count):
|
||||
# Array traversal: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
|
||||
elem_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, pointee, elem_ty, alloca, idx_val)
|
||||
if elem_ptr is not None: return mlirlite.build_load(builder, elem_ty, elem_ptr)
|
||||
if elem_ptr is not None:
|
||||
arr_val_rd: mlirlite.Value* = mlirlite.build_load(builder, elem_ty, elem_ptr)
|
||||
# BigEndian array variable: the element slot is byte-swapped; un-swap
|
||||
# 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 None
|
||||
case mlirlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v):
|
||||
# list[T] value variable (an unannotated assignment rhs returns a list
|
||||
|
||||
@@ -79,6 +79,10 @@ class VarEntry:
|
||||
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||
Life: int
|
||||
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
|
||||
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
|
||||
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
|
||||
ByteOrder: int
|
||||
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
|
||||
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
|
||||
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
|
||||
|
||||
@@ -216,6 +216,23 @@ novirtual class VarGenMixin:
|
||||
if entry is None: return 0
|
||||
return entry.IsConst
|
||||
|
||||
# set_var_byte_order - register a local variable's byte order (VarEntry.ByteOrder) from the
|
||||
# `T | t.BigEndian` / `T | t.LittleEndian` annotation union: 1=BigEndian / 2=LittleEndian.
|
||||
# The value is only 1/2 when a marker is present, so the local load/store paths bswap only then.
|
||||
def set_var_byte_order(self, symtab: GenTypes.SymbolTable*, name: str, byte_order: int) -> int:
|
||||
"""Set a local variable's byte order (VarEntry.ByteOrder), return 0=success / 1=failure"""
|
||||
if symtab is None or name is None: return 1
|
||||
entry: GenTypes.VarEntry* = self.lookup_var_entry(symtab, name)
|
||||
if entry is None: return 1
|
||||
entry.ByteOrder = byte_order
|
||||
return 0
|
||||
|
||||
# var_byte_order - whether a variable is big/little-endian (unified read entry for load/store).
|
||||
def var_byte_order(self, entry: GenTypes.VarEntry*) -> int:
|
||||
"""The variable entry's byte order (0=native / 1=BigEndian / 2=LittleEndian; 0 when None)"""
|
||||
if entry is None: return 0
|
||||
return entry.ByteOrder
|
||||
|
||||
# ============================================================
|
||||
# 生命期标签读写(LifeTag,见 GenTypes define LIFE_*;架构见 ownership-and-new.md §11)
|
||||
# ============================================================
|
||||
@@ -320,6 +337,8 @@ novirtual class VarGenMixin:
|
||||
entry.IsConst = 0
|
||||
# 生命期标签显式初始化:未登记 = LIFE_UNKNOWN(保守放行)。
|
||||
entry.Life = GenTypes.LIFE_UNKNOWN
|
||||
# 字节序显式初始化:未标注 = 0 (native),只有 set_var_byte_order 才置 1/2(避免槽复用残留)。
|
||||
entry.ByteOrder = 0
|
||||
# [S3 dual write] Value-type Spec (alloca.Ty is Ptr(value type), so take its pointee)
|
||||
entry.Spec = self._spec_of_var_alloca(alloca)
|
||||
return 0
|
||||
|
||||
Reference in new issue
Block a user