From 9fe88831f322e89b866612cc92ce2afe8f21cc04 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 5 Oct 2026 09:26:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(codegen):=20=E4=BF=AE=E5=A4=8D=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E7=BA=A7=20str=20=E5=AD=97=E9=9D=A2=E9=87=8F=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=9A=84=E5=88=9D=E5=A7=8B=E5=8C=96=E5=99=A8=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 模块级 `X: str = "字面量"` 有两条路径都没写出正确初始化器: 1) 新建全局分支在创建 GlobalVar 之后又查 `VarName not in Gen.module.globals` (此时刚登记,条件恒假),于是永远走 else 分支、从不写初始化器:全局退化成 无初值的声明,第二遍再被升级成 `common global i8* null`,字符串常量沦为无人 引用的 @NAME_str。只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端 strlen(NULL) 直接崩(ASan 0x0)。 2) 把既有 external 声明升级为本模块定义的分支一律设 common,但 LLVM 要求 common 全局的初始化器必须是零值,字符串字面量会触发 verifier 报错。 新增 _BuildStringPtrInitializer 统一产出「指向 @{Name}_str 常量数组首元素的 GEP」,两处改用它。 验证:TransPyV 自举通过;TPV 顺序回归 77/77 全通过。 --- lib/core/Handles/HandlesAssign.py | 71 +++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index 0810640..9e64ad4 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -1765,6 +1765,30 @@ class AssignHandle(BaseHandle): return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") return None + def _BuildStringPtrInitializer(self, Gen: "Translator.LlvmGen", VarName: str, str_value: str) -> ir.Constant: + """模块级 `X: str = "字面量"` 全局的初始化器:指向常量字符数组**首元素** + + 字符串常量以 `@X_str`(internal global [N x i8])承载,重复调用复用同一 + 全局;返回 `getelementptr([N x i8], [N x i8]* @X_str, i32 0, i32 0)`。 + + 注意:**不能**用 `ir.Constant(ptr_type, str_gv)` —— 该写法把整个全局定义 + 内联成初始化器(渲染出 `@X = global i8* @X_str = internal global [...]` + 这种非法 IR),必须用 `ir.Constant.gep` 取首元素地址(与 import 侧 + HandlesImports._EmitExternalGlobalDeclLlvm 的字符串分支同口径)。 + """ + str_bytes: bytes = (str_value + '\x00').encode('utf-8') + arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) + str_gv_name: str = VarName + '_str' + str_gv: ir.GlobalVariable + if str_gv_name not in Gen.module.globals: + str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name) + str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + str_gv.linkage = 'internal' + else: + str_gv = Gen.module.globals[str_gv_name] + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + return ir.Constant.gep(str_gv, [zero, zero]) + def _EmitGlobalAnnAssignLlvm(self, Node: ast.AnnAssign, Gen: "Translator.LlvmGen") -> None: # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): # 模块级全局注解不经过 ResolveAnnotationType,故在此显式校验。 @@ -1930,9 +1954,19 @@ class AssignHandle(BaseHandle): # 声明/定义靠 initializer 区分:external 声明 initializer 恒为 None。 if isinstance(existing_gv, ir.GlobalVariable) and getattr(existing_gv, 'initializer', None) is None: try: - existing_gv.linkage = 'common' - if Node.value is None or (isinstance(Node.value, ast.Constant) and Node.value.value is None): - existing_gv.initializer = ir.Constant(existing_gv.type.pointee, None) + if (isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) + and isinstance(getattr(existing_gv, 'value_type', None), ir.PointerType)): + # 字符串字面量全局:补"指向常量数组首元素"的初始化器。 + # **不能**设 common —— LLVM 要求 common 全局的初始化器必须是 + # 零值(verifier: "'common' global must have a zero + # initializer!"),配非空初值直接非法;保持默认 external + # 强定义即可跨模块共享(Config.BackendTarget / + # _inc_bin_suffix 都在运行时被赋值/读取,符号必须唯一且可见)。 + existing_gv.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) + else: + existing_gv.linkage = 'common' + if Node.value is None or (isinstance(Node.value, ast.Constant) and Node.value.value is None): + existing_gv.initializer = ir.Constant(existing_gv.type.pointee, None) Gen.variables[VarName] = existing_gv except Exception: pass @@ -1962,26 +1996,17 @@ class AssignHandle(BaseHandle): Gen.global_var_ptr_element[VarName] = True if Node.value: if isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str) and isinstance(VarType, ir.PointerType): - str_val: str = Node.value.value + '\x00' - str_bytes: bytes = str_val.encode('utf-8') - arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) - str_gv_name: str = VarName + '_str' - str_gv: ir.GlobalVariable - if str_gv_name not in Gen.module.globals: - str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name) - str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) - str_gv.linkage = 'internal' - else: - str_gv = Gen.module.globals[str_gv_name] - ptr_type: ir.PointerType = ir.PointerType(ir.IntType(8)) - ptr_gv: ir.GlobalVariable - if VarName not in Gen.module.globals: - ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName) - ptr_gv.initializer = ir.Constant(ptr_type, str_gv) - ptr_gv.linkage = 'internal' - else: - ptr_gv = Gen.module.globals[VarName] - Gen.variables[VarName] = ptr_gv + # 字符串字面量全局:初始化器 = 指向 @{VarName}_str 常量数组首元素 + # 的 GEP。此前这里在创建 GlobalVar(上方数行)之后又查 + # `VarName not in Gen.module.globals` —— 该条件恒为假(GlobalVar + # 刚被登记),于是永远走 else 分支、**从不写初始化器**:全工程模块级 + # `str = "字面量"` 全局退化成无初值的声明,第二遍再被升级成 + # `common global i8* null`,字符串常量沦为无人引用的 @NAME_str。 + # 只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端 + # strlen(NULL) 直接崩(ASan 0x0)。 + if getattr(GlobalVar, 'initializer', None) is None: + GlobalVar.initializer = self._BuildStringPtrInitializer(Gen, VarName, Node.value.value) + Gen.variables[VarName] = GlobalVar elif isinstance(VarType, ir.PointerType) and isinstance(VarType.pointee, ir.FunctionType) and isinstance(Node.value, ast.Name): # 函数指针赋值:f3: t.Callable[[int], int] = f RefName: str = Node.value.id