fix(codegen): 修复模块级 str 字面量全局的初始化器缺失
模块级 `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 全通过。
This commit is contained in:
1 parent
07ba7a4fe5
commit
9fe88831f3
1 file changed
+48
-23
@@ -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
|
||||
|
||||
Reference in new issue
Block a user