import t, c from stdint import * import ast import llvmlite import memhub import string import viperlib import stdio import stdlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT import lib.core.Gen.GenApi as GenApi import lib.core.Gen.Call.CallGenBase as CallGenBase # ============================================================ # FuncGenBase - FuncGenMixin 拆分(阶段 D):函数属性 / 命名 / mangle / create_function 基础工具 # ============================================================ novirtual class FuncGenBase(CallGenBase.CallGenBase): """函数属性 / 命名 / mangle / create_function 基础工具""" # ============================================================ # extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性 # # 支持 @c.Attribute(t.attr.xxx()) 和 @c.Attribute(t.attr.xxx) 形式 # 也支持 @c.Attribute(t.attr.llvm.xxx) 形式 # # 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None # ============================================================ def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str: """从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串""" if decorator_list is None: return None dn: sizet = len(decorator_list) if dn == 0: return None attrs_buf: str = pool.alloc(256) if attrs_buf is None: return None attrs_buf[0] = '\0' found_any: int = 0 for di in range(dn): deco: ast.AST* = decorator_list.get(di) if deco is None: continue dk: int = deco.kind() # 直接形式 `@t.attr.`(与类侧 ClassGenBase._has_decorator 同口径, # 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态, # 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件 # 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略 # (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。 if dk == ast.ASTKind.Attribute: dan: str = self._get_attr_name_from_node(deco) if dan is not None and string.strcmp(dan, "section") != 0: if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1 continue if dk != ast.ASTKind.Call: continue call_node: ast.Call* = ast.Call*(deco) if call_node.func is None: continue cfk: int = call_node.func.kind() if cfk != ast.ASTKind.Attribute: continue # 检测 c.Attribute func_attr: ast.Attribute* = ast.Attribute*(call_node.func) if func_attr.attr is None: continue if string.strcmp(func_attr.attr, "Attribute") != 0: continue if func_attr.value is None: continue fvk: int = func_attr.value.kind() if fvk != ast.ASTKind.Name: continue c_name: ast.Name* = ast.Name*(func_attr.value) if c_name.id is None: continue if string.strcmp(c_name.id, "c") != 0: continue imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c") if imp_c == 0: continue # 遍历参数提取属性 if call_node.args is None: continue args_list: list[ast.AST*]* = call_node.args an: sizet = len(args_list) for ai in range(an): arg_node: ast.AST* = args_list.get(ai) if arg_node is None: continue # 提取属性名(t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx) attr_name: str = self._get_attr_name_from_node(arg_node) if attr_name is None: continue # B5:section 字符串属性(t.attr.section(".text.startup"))不能内联 # 在函数签名行(llc: expected string constant),必须走属性组 # #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。 if string.strcmp(attr_name, "section") == 0: continue # 映射到 LLVM 属性名并追加 ar: int = self._append_llvm_attr(attrs_buf, attr_name) if ar != 0: found_any = 1 if found_any == 0: return None return attrs_buf def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str: """提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。 LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾 attributes #N = { ... }),不能内联(llc: expected string constant)。 main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。 """ if pool is None or decorator_list is None: return None dn: sizet = len(decorator_list) if dn == 0: return None for di in range(dn): deco: ast.AST* = decorator_list.get(di) if deco is None or deco.kind() != ast.ASTKind.Call: continue call_node: ast.Call* = ast.Call*(deco) if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue func_attr: ast.Attribute* = ast.Attribute*(call_node.func) if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue if call_node.args is None: continue args_list: list[ast.AST*]* = call_node.args an: sizet = len(args_list) for ai in range(an): arg_node: ast.AST* = args_list.get(ai) if arg_node is None: continue attr_name: str = self._get_attr_name_from_node(arg_node) if attr_name is None or string.strcmp(attr_name, "section") != 0: continue sec_val: str = self._get_attr_call_arg_str(arg_node) if sec_val is None: continue sec_buf: str = pool.alloc(128) if sec_buf is None: return None viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val) return sec_buf return None def _get_attr_name_from_node(self, node: ast.AST*) -> str: """从 AST 节点提取属性名 支持: - Call(func=Attribute(...)): t.attr.always_inline() -> "always_inline" - Attribute: t.attr.packed -> "packed" - Attribute(t.attr.llvm.xxx): t.attr.llvm.nounwind -> "nounwind" """ if node is None: return None k: int = node.kind() # Call 节点: t.attr.always_inline() if k == ast.ASTKind.Call: call: ast.Call* = ast.Call*(node) if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None at: ast.Attribute* = ast.Attribute*(call.func) return at.attr # Attribute 节点: t.attr.packed 或 t.attr.llvm.nounwind if k == ast.ASTKind.Attribute: at2: ast.Attribute* = ast.Attribute*(node) return at2.attr return None def _get_attr_call_arg_str(self, node: ast.AST*) -> str: """取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))""" if node is None or node.kind() != ast.ASTKind.Call: return None cl: ast.Call* = ast.Call*(node) if cl.args is None or len(cl.args) == 0: return None a0: ast.AST* = cl.args.get(0) if a0 is None or a0.kind() != ast.ASTKind.Constant: return None cn: ast.Constant* = ast.Constant*(a0) if cn.const_kind != ast.CONST_STR: return None return cn.str_val def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int: """将属性名映射到 LLVM 属性名并追加到缓冲区 返回 1 表示已追加,0 表示不支持该属性 """ if attrs_buf is None or attr_name is None: return 0 # 先确定映射的 LLVM 属性名 llvm_name: str = None if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline" elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline" elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn" elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly" elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone" elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind" elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone" elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"' elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn" elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress" else: # packed/aligned/section/visibility/weak 等不支持作为函数内联属性 return 0 # 支持的属性:如果缓冲区非空,先追加空格分隔 if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256) self._append_str(attrs_buf, llvm_name, 256) return 1 def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None: """将字符串追加到 buf 末尾(有界,防 strcpy 越界写穿定长缓冲)""" if buf is None or s is None: return # 防御:TPV 方法默认参数注册存在缺陷(缺参调用 max_len 落到 0), # 显式兜底为 256,避免有界写入检查误判丢弃全部属性文本。 if max_len == 0: max_len = 256 cur_len: sizet = string.strlen(buf) s_len: sizet = string.strlen(s) if cur_len + s_len + 1 > max_len: return # 无剩余容量:丢弃本次追加,避免越界写 存疑 string.strcpy(buf + cur_len, s) # ============================================================ # _mangle_name - 为名称添加 SHA1 前缀 # # 返回 "sha1.name" 格式的混淆名。若 ModuleSha1 为 None 或名称已带 # SHA1 前缀(16 hex + '.'),则原样返回。 # ============================================================ def _mangle_name(self, trans: HT.Translator*, name: str) -> str: """为任意名称添加 SHA1 前缀(不做 main/export 检查)""" if trans is None or name is None: return name if trans.ModuleSha1 is None: return name name_len: sizet = string.strlen(name) # 已带 SHA1 前缀("{16hex}.")则跳过 # [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`。旧判据是 `name_len > 17`, # 与真源的 `>= 17` 只差"前缀之后为空串"这一退化形态(无实际意义),一并对齐。 if GenApi.sha1_prefix_len(name) != 0: return name sha1: str = trans.ModuleSha1 sha1_len: sizet = string.strlen(sha1) mangled: str = stdlib.malloc(sha1_len + name_len + 2) if mangled is None: return name string.strcpy(mangled, sha1) mangled[sha1_len] = '.' string.strcpy(mangled + sha1_len + 1, name) return mangled # ============================================================ # _mangle_name_with_sha1 — 用指定 SHA1 为名称添加前缀 # # 用于跨模块 vtable 继承: 继承方法需要用父模块的 SHA1 做 mangling, # 而非当前模块的 SHA1。 # ============================================================ def _mangle_name_with_sha1(self, sha1: str, name: str) -> str: """用指定 SHA1 为名称添加前缀(不做 main/export 检查)""" if sha1 is None or name is None: return name name_len: sizet = string.strlen(name) # 已带 SHA1 前缀("{16hex}.")则跳过 # [C10 收敛] 判定走唯一真源 `GenApi.sha1_prefix_len`(口径对齐同 _mangle_name)。 if GenApi.sha1_prefix_len(name) != 0: return name sha1_len: sizet = string.strlen(sha1) mangled: str = stdlib.malloc(sha1_len + name_len + 2) if mangled is None: return name string.strcpy(mangled, sha1) mangled[sha1_len] = '.' string.strcpy(mangled + sha1_len + 1, name) return mangled # ============================================================ # _mangle_func_name - 为函数名添加 SHA1 前缀(含 main/export 检查) # # 规则: # - main 函数不加前缀(程序入口点) # - has_export 非 0 时不加前缀(t.CExport 导出函数) # - 已带 SHA1 前缀的不再加 # - ModuleSha1 为 None 时不加前缀 # ============================================================ def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str: """为函数名添加 SHA1 前缀(检查 main/export)""" if trans is None or name is None: return name if has_export != 0: return name if string.strcmp(name, "main") == 0: return name return self._mangle_name(trans, name) # ============================================================ # _body_has_raise - 函数体是否含 raise(非 StopIteration) # # 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名 # 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体 # 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。 # ============================================================ def _body_has_raise(self, fd: ast.FunctionDef*) -> int: """函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否""" if fd is None or fd.children is None: return 0 body_f: list[ast.AST*]* = fd.children bn_f: sizet = len(body_f) for bi_f in range(bn_f): stmt_f: ast.AST* = body_f.get(bi_f) if stmt_f is None: continue if stmt_f.kind() != ast.ASTKind.Raise: continue rn_f: rawptr[ast.Raise] = rawptr[ast.Raise](stmt_f) if rn_f is None or rn_f.exc is None: continue ek_f: int = rn_f.exc.kind() is_stop_f: int = 0 if ek_f == ast.ASTKind.Name: enm_f: ast.Name* = ast.Name*(rn_f.exc) if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1 elif ek_f == ast.ASTKind.Call: ecl_f: ast.Call* = ast.Call*(rn_f.exc) if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name: ecn_f: ast.Name* = ast.Name*(ecl_f.func) if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1 if is_stop_f == 0: return 1 return 0 # ============================================================ # RejectCReturnDecorator - @c.CReturn 装饰器已移除,遇到即编译期报错 # # 多返回值唯一入口是返回注解 `-> tuple[ty1, ty2, ...]`(tuple → struct 通路 # 逐字保留)。任何装饰器形态出现 CReturn 一律 fail-fast,不静默忽略。 # 供 FuncGenDef(pre_scan 前向声明 / translate_function_def 定义两阶段)复用; # Converter 为模块级函数,同报文另写一份(见 StubGen/Converter.vp)。 # 无 CReturn 返回 0。 # ============================================================ def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int: """装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)""" if decorators is None: return 0 rcr_dn: sizet = len(decorators) for rcr_i in range(rcr_dn): rcr_deco: ast.AST* = decorators.get(rcr_i) if rcr_deco is None: continue # 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态 rcr_attr: ast.Attribute* = None if rcr_deco.kind() == ast.ASTKind.Call: rcr_call: ast.Call* = ast.Call*(rcr_deco) if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func) elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco) if rcr_attr is None or rcr_attr.attr is None: continue if string.strcmp(rcr_attr.attr, "CReturn") == 0: self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct 返回)", "TypeError") return 1 return 0 # ============================================================ # 创建 LLVM 函数(简单版本,仅声明) # ============================================================ def create_function(self, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: llvmlite.LLVMType*) -> llvmlite.Function*: """创建 LLVM 函数并添加参数""" if name is None or mod is None: return None func: llvmlite.Function* = llvmlite.create_function(pool, mod, name, ret_ty) if func is None: return None if args_node is not None: ags: ast.Arguments* = ast.Arguments*(args_node) if ags.args is not None: alist: list[ast.AST*]* = ags.args an: sizet = len(alist) i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) for ai in range(an): arg: ast.Arg* = ast.Arg*(alist.get(ai)) if arg is not None and arg.arg is not None: # t\.CVoid 表示空参:跳过 if arg.annotation is not None: if arg.annotation.kind() == ast.ASTKind.Attribute: at_cf: ast.Attribute* = ast.Attribute*(arg.annotation) if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue param_ty: llvmlite.LLVMType* = i32_ty if arg.annotation is not None: resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0) if resolved is None: # [S4 fail-fast] 有注解但解析失败:不再静默用 i32。 # 本函数不传 trans/imports 上下文,解析失败即调用方未补齐 # 上下文(暴露上游,不由本函数兜底)。 self.Gen.TypeGenErr.fatal_error(arg, f"函数 '{name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError") return None param_ty = resolved pname: str = pool.alloc(32) if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg) llvmlite.add_param(pool, func, param_ty, pname) return func