diff --git a/ast/base.vp b/ast/base.vp index fcf145e..dd2d38d 100644 --- a/ast/base.vp +++ b/ast/base.vp @@ -155,6 +155,7 @@ FLAG_IS_ASYNC: t.CDefine = 1 FLAG_SIMPLE: t.CDefine = 2 FLAG_HAS_STAR: t.CDefine = 4 FLAG_IS_EXTERN: t.CDefine = 8 +FLAG_IS_EXPORT: t.CDefine = 16 # ============================================================ diff --git a/ast/exprs.vp b/ast/exprs.vp index 366fffc..574ef7f 100644 --- a/ast/exprs.vp +++ b/ast/exprs.vp @@ -2,6 +2,7 @@ import t, c from stdint import * import memhub import string +import stdio from .base import ( AST, ExprNode, ASTKind, ASTCtx, OpKind, _init_ast, _copy_str, _set_parent_list, @@ -53,6 +54,34 @@ class Name(ExprNode): return pos +def name_get_id(node: Name | t.CPtr) -> str: + """获取 Name.id 字段(跨模块访问器,stub 字段访问陷阱防护)""" + if node is None: + return None + return node.id + + +def constant_get_str_val(node: Constant | t.CPtr) -> str: + """获取 Constant.str_val 字段(跨模块访问器)""" + if node is None: + return None + return node.str_val + + +def binop_get_left(node: BinOp | t.CPtr) -> AST | t.CPtr: + """获取 BinOp.left 字段(跨模块访问器)""" + if node is None: + return None + return node.left + + +def binop_get_right(node: BinOp | t.CPtr) -> AST | t.CPtr: + """获取 BinOp.right 字段(跨模块访问器)""" + if node is None: + return None + return node.right + + # ============================================================ # Constant nodes (subtype marked by const_kind) # ============================================================ @@ -943,6 +972,27 @@ class Attribute(ExprNode): return pos +# ============================================================ +# 字段访问器(跨模块 stub 字段访问陷阱防护) +# +# 其他模块(如 TypeGenCType)直接访问 ast.Attribute 的字段时, +# TPC 可能按 stub 类型生成错误 GEP(读垃圾地址 → 0xC0000005 崩溃)。 +# 本模块是完整类型,字段偏移正确;跨模块调用方用这些访问器。 +# ============================================================ +def attribute_get_value(node: Attribute | t.CPtr) -> AST | t.CPtr: + """获取 Attribute.value 字段(跨模块访问器)""" + if node is None: + return None + return node.value + + +def attribute_get_attr(node: Attribute | t.CPtr) -> str: + """获取 Attribute.attr 字段(跨模块访问器)""" + if node is None: + return None + return node.attr + + # ============================================================ # Subscript Node # ============================================================ diff --git a/ast/parser.vp b/ast/parser.vp index 3f49e0e..44aa098 100644 --- a/ast/parser.vp +++ b/ast/parser.vp @@ -10,7 +10,7 @@ from .base import ( _set_pos, _binop_from_op, _augop_from_op, _cmpop_from_op, _copy_str, _inherit_pos, _init_ast, CONST_INT, CONST_FLOAT, CONST_STR, CONST_BOOL, CONST_NONE, CONST_CHAR, - FLAG_IS_ASYNC, FLAG_IS_EXTERN, + FLAG_IS_ASYNC, FLAG_IS_EXTERN, FLAG_IS_EXPORT, ) from .exprs import ( BoolOp, BinOp, UnaryOp, Lambda, IfExp, Dict, Set, @@ -95,18 +95,20 @@ def _cur_kw(ps: Parser | t.CPtr) -> t.CInt: def _is_funcdef_kw(ps: Parser | t.CPtr, kw: t.CInt) -> int: """判断当前 token 是否为函数定义关键字(def 及其别名 fn/fun/func/function, - 以及 extern 声明关键字) + 以及 extern/export 声明关键字) - def 是硬关键字直接判定;fn/fun/func/function/extern 是软关键字, + def 是硬关键字直接判定;fn/fun/func/function/extern/export 是软关键字, 仅当下一个 token 是 Name 且再下一个是 '('(或泛型 '[T]' 前的 '[')时 - 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def)。 + 判定为函数定义。extern 还允许 'extern def A(...)' 形式(下一个是 def), + export 同样允许 'export def A(...)' 形式。 否则按普通标识符处理,兼容既有代码把 func 用作 字段/参数名(如 AST 节点 func 字段、Call.func 注解 `func: AST | t.CPtr`)。 """ if kw == Keyword.Def: return 1 - if kw == Keyword.Extern: - # extern def A(...) 或 extern A(...)(extern 后是 def 或 Name+'(/'[') + if kw == Keyword.Extern or kw == Keyword.Export: + # extern/export def A(...) 或 extern/export A(...) + # (extern/export 后是 def 或 Name+'(/'[') nxt: Token | t.CPtr = ps.cur.next if nxt is None: return 0 @@ -2229,12 +2231,19 @@ def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr: lineno: t.CInt = _cur_lineno(ps) col: t.CInt = _cur_col(ps) is_extern_fd: t.CInt = 0 + is_export_fd: t.CInt = 0 if _cur_kw(ps) == Keyword.Extern: # extern 声明:extern [def] A(...) [-> T] [": pass" 可选] is_extern_fd = 1 _advance(ps) # extern if _cur_kw(ps) == Keyword.Def: _advance(ps) + elif _cur_kw(ps) == Keyword.Export: + # export 声明:export [def] A(...) [-> T] [": pass" 可选] + is_export_fd = 1 + _advance(ps) # export + if _cur_kw(ps) == Keyword.Def: + _advance(ps) else: _advance(ps) # def / fn / ... if _cur_type(ps) != TokenType.Name: @@ -2280,15 +2289,33 @@ def _parse_funcdef(ps: Parser | t.CPtr, is_async: t.CInt) -> AST | t.CPtr: returns = BinOp(ps.pool, returns, OpKind.BitOr, state_attr_fd) else: returns = state_attr_fd + elif is_export_fd: + # export 声明:返回注解改写为 <原类型> | t.CExport,复用既有 + # CExport 识别路径(vpi 注解含 "CExport" → 定义端裸名 + 不加模块 SHA1 头) + export_attr_fd: AST | t.CPtr = Attribute(ps.pool, + Name(ps.pool, "t", ASTCtx.Load), + "CExport", ASTCtx.Load) + if returns != None: + returns = BinOp(ps.pool, returns, OpKind.BitOr, export_attr_fd) + else: + returns = export_attr_fd dec_list: list[AST | t.CPtr] | t.CPtr = ps.pending_decorators ps.pending_decorators = None node: FunctionDef | t.CPtr = FunctionDef(ps.pool, name, args, dec_list, returns, is_async, type_params_fd) if is_extern_fd: node.flags = node.flags | FLAG_IS_EXTERN - if is_extern_fd == 0 or _match_op(ps, TokOp.Colon): + if is_export_fd: + node.flags = node.flags | FLAG_IS_EXPORT + if (is_extern_fd == 0 and is_export_fd == 0) or _match_op(ps, TokOp.Colon): _expect_op(ps, TokOp.Colon) _parse_suite_into_children(ps, node) - # extern 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明 + else: + # extern/export 无 ': pass' 形式(extern A(...) -> int):body 为空,仅声明。 + # 必须消费本行 NewLine:模块级/类体语句循环只 _skip_nl(Nl), + # 残留 NewLine 会被下一轮当表达式解析(_parse_atom → error_count++) + # → parse_tokens 返回 None(TPV 编译 extern 文件报 7 个扫描失败的根因) + if _cur_type(ps) == TokenType.NewLine: + _advance(ps) _set_pos(node, lineno, col, 0, 0) return node diff --git a/ast/tokens.vp b/ast/tokens.vp index d9c07c5..54a316b 100644 --- a/ast/tokens.vp +++ b/ast/tokens.vp @@ -79,6 +79,8 @@ class Keyword(t.CEnum): Function: t.State # extern 函数声明关键字(FFI,等价 t.State 标记;软判定见 _is_funcdef_kw) Extern: t.State + # export 函数声明关键字(等价 t.CExport,裸名导出不加 SHA1 头;软判定见 _is_funcdef_kw) + Export: t.State # ============================================================ @@ -258,7 +260,8 @@ def _init_keywords(pool: memhub.MemManager | t.CPtr): _kw_intern(pool, "fun", Keyword.Fun) _kw_intern(pool, "func", Keyword.Func) _kw_intern(pool, "function", Keyword.Function) - _kw_intern(pool, "extern", Keyword.Extern) + _kw_intern(pool, "extern", Keyword.Extern) + _kw_intern(pool, "export", Keyword.Export) # ============================================================ diff --git a/llvmlite/__builder.vp b/llvmlite/__builder.vp index 8c949a9..0de1afc 100644 --- a/llvmlite/__builder.vp +++ b/llvmlite/__builder.vp @@ -59,6 +59,19 @@ class IRBuilder: if buf is None: return None buf[0] = '\0' TypePrint(buf, 512, ty, pool) + # [IR-FAIL] 校验护栏:LLVMType 类型对象可能被 MemBuddy 覆盖/悬空 + # (跨遍 reset 回收后 pool 内存复用),此时 TypePrint 输出首字符 + # 变为垃圾(如 't')或为空。立即 fail-fast 终止,防止生成 + # `bitcast i8* null t` 类垃圾 IR,避免 llc 报 "expected 'to' after + # cast value" 后难以定位根因。 + c0: t.CInt = buf[0] + if c0 == 0 or (c0 != '%' and c0 != '[' and c0 != '{' and c0 != 'f' and + c0 != 'd' and c0 != 'h' and c0 != 'i' and c0 != 'l' and + c0 != 'm' and c0 != 'v' and c0 != '.'): + stdio.printf("[IR-FAIL] _type_str: LLVMType 对象已损坏 ty=0x%llx 首字符=%d 输出='%s'\n", + t.CUInt64T(ty), c0, buf) + stdlib.exit(1) + return None return buf diff --git a/llvmlite/__function.vp b/llvmlite/__function.vp index 8e0577e..37b8d61 100644 --- a/llvmlite/__function.vp +++ b/llvmlite/__function.vp @@ -4,10 +4,17 @@ import string import viperlib import memhub import stdio +import stdlib from linkedlist import GSListNode, GSList from .__types import LLVMType, ParamNode, TypePrint +# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast,与 __module.vp 对齐)。 +# 函数/基本块/指令行链的 Next 被越界写破坏成环时,遍历无限循环挂起; +# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"。 +IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 + + # ============================================================ # Line: 指令文本行 # @@ -425,10 +432,21 @@ def _find_max_ssa_number(func: Function | t.CPtr) -> t.CInt: if func is None or func.Blocks is None: return max_n blk: BasicBlock | t.CPtr = func.Blocks.Head + blk_steps: t.CSizeT = 0 while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 扫描终止)\n", func.Name) + stdlib.exit(1) if blk.Lines is not None: line: Line | t.CPtr = blk.Lines.Head + line_steps: t.CSizeT = 0 while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 扫描终止)\n", + func.Name, blk.Name) + stdlib.exit(1) if line.Buf is not None: max_n = _scan_max_ssa_in_text(line.Buf, max_n) line = line.Next @@ -550,10 +568,21 @@ def RenumberSSA(func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr): # 3. 第二遍扫描:按文本顺序分配新编号 next_new: t.CInt = 0 blk: BasicBlock | t.CPtr = func.Blocks.Head + blk_steps: t.CSizeT = 0 while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 重编号终止)\n", func.Name) + stdlib.exit(1) if blk.Lines is not None: line: Line | t.CPtr = blk.Lines.Head + line_steps: t.CSizeT = 0 while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 重编号终止)\n", + func.Name, blk.Name) + stdlib.exit(1) if line.Buf is not None: next_new = _assign_new_numbers(line.Buf, map_arr, max_n, next_new) line = line.Next @@ -573,10 +602,21 @@ def RenumberSSA(func: Function | t.CPtr, pool: memhub.MemBuddy | t.CPtr): # 5. 第三遍扫描:替换所有指令行文本 blk = func.Blocks.Head + blk_steps = 0 while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,ssa 替换终止)\n", func.Name) + stdlib.exit(1) if blk.Lines is not None: line: Line | t.CPtr = blk.Lines.Head + line_steps = 0 while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,ssa 替换终止)\n", + func.Name, blk.Name) + stdlib.exit(1) if line.Buf is not None: new_buf: t.CChar | t.CPtr = _replace_ssa_numbers( line.Buf, pool, map_arr, max_n) @@ -643,7 +683,17 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT, if func.Params is not None: cur = func.Params.Head first: t.CInt = 1 + cur_steps: t.CSizeT = 0 while cur is not None: + cur_steps += 1 + if cur_steps > IR_DUMP_CHAIN_LIMIT: + if func.Params is not None: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,遍历超 %d 步,print 终止)\n", + func.Name, func.Params.Count, IR_DUMP_CHAIN_LIMIT) + else: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params=None,遍历超 %d 步,print 终止)\n", + func.Name, IR_DUMP_CHAIN_LIMIT) + stdlib.exit(1) if first == 0: _append_cstr(buf, size, ", ") # 参数属性(如 "noundef"/"nocapture")在类型之前 @@ -685,7 +735,12 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT, blk: BasicBlock | t.CPtr = None if func.Blocks is not None: blk = func.Blocks.Head + blk_steps: t.CSizeT = 0 while blk is not None: + blk_steps += 1 + if blk_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: func blocks(函数名=%s,print 终止)\n", func.Name) + stdlib.exit(1) # 块标签 if blk.Name is not None: _append_cstr(buf, size, blk.Name) @@ -695,7 +750,13 @@ def FunctionPrint(buf: t.CChar | t.CPtr, size: t.CSizeT, if blk.Lines is not None: line = blk.Lines.Head has_lines: t.CInt = 0 + line_steps: t.CSizeT = 0 while line is not None: + line_steps += 1 + if line_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: block lines(函数名=%s,块=%s,print 终止)\n", + func.Name, blk.Name) + stdlib.exit(1) if line.Buf is not None: _append_cstr(buf, size, " ") _append_cstr(buf, size, line.Buf) diff --git a/llvmlite/__module.vp b/llvmlite/__module.vp index cc96d15..3d657fc 100644 --- a/llvmlite/__module.vp +++ b/llvmlite/__module.vp @@ -7,7 +7,7 @@ import viperlib import memhub from linkedlist import GSList, GSListNode from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct, ParamNode -from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param +from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param, param_get_next, param_get_ty # ============================================================ @@ -227,21 +227,43 @@ class LLVMModule: # 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义 # 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册 func_scan: Function | t.CPtr = self.FuncHead + func_scan_steps: t.CSizeT = 0 while func_scan is not None: + func_scan_steps += 1 + if func_scan_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module funcs(FuncCount=%zu,dump 终止)\n", + self.FuncCount) + stdlib.exit(1) if func_scan.RetTy is not None: self.ensure_opaque_for_type(pool, func_scan.RetTy) if func_scan.Params is not None: param_scan: Param | t.CPtr = func_scan.Params.Head + param_scan_steps: t.CSizeT = 0 while param_scan is not None: - if param_scan.Ty is not None: - self.ensure_opaque_for_type(pool, param_scan.Ty) - param_scan = param_scan.Next + param_scan_steps += 1 + if param_scan_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,param=%llu next=%llu,遍历超 %d 步,终止)\n", + func_scan.Name, func_scan.Params.Count, + t.CUInt64T(param_scan), t.CUInt64T(param_scan.Next), IR_DUMP_CHAIN_LIMIT) + stdlib.exit(1) + # 用访问器(__function.vp 完整类型内)绕开 stub 字段访问 + # 陷阱:跨模块 struct 字段访问可能生成错误 GEP/静默跳过, + # 导致 param_scan 永不更新死循环。 + if param_get_ty(param_scan) is not None: + self.ensure_opaque_for_type(pool, param_get_ty(param_scan)) + param_scan = param_get_next(param_scan) func_scan = func_scan.Next nt: NamedTypeNode | t.CPtr = self.NamedTypeHead # 结构体定义缓冲区:48 字段 × ~50 字节/字段类型名 ≈ 2400 字节,4096 足够 # 在循环外分配一次并复用,减少 pool 碎片 ty_buf: t.CChar | t.CPtr = pool.alloc(4096) + nt_steps: t.CSizeT = 0 while nt is not None: + nt_steps += 1 + if nt_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module named types(NamedTypeCount=%zu,dump 终止)\n", + self.NamedTypeCount) + stdlib.exit(1) if nt.Ty is not None: if ty_buf is not None: ty_buf[0] = '\0' @@ -255,9 +277,15 @@ class LLVMModule: # 全局变量 gv: GlobalVariable | t.CPtr = self.GlobalHead + gv_steps: t.CSizeT = 0 if mode == OUTPUT_STUB: # stub 模式:输出 external global(无初值) while gv is not None: + gv_steps += 1 + if gv_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals stub(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) _print_global_stub(buf, size, gv, pool) _append_cstr(buf, size, "\n") gv = gv.Next @@ -266,6 +294,11 @@ class LLVMModule: # text 模式跳过无初值的全局变量(纯声明) gv_idx: t.CInt = 0 while gv is not None: + gv_steps += 1 + if gv_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) if mode == OUTPUT_TEXT and gv.Initializer is None: gv = gv.Next gv_idx += 1 @@ -281,7 +314,13 @@ class LLVMModule: # text 模式下如果输出了全局变量,也加空行 had_global: t.CInt = 0 gv2: GlobalVariable | t.CPtr = self.GlobalHead + gv2_steps: t.CSizeT = 0 while gv2 is not None: + gv2_steps += 1 + if gv2_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module globals text(GlobalCount=%zu,dump 终止)\n", + self.GlobalCount) + stdlib.exit(1) if gv2.Initializer is not None: had_global = 1 break @@ -292,7 +331,13 @@ class LLVMModule: # 遍历函数 func: Function | t.CPtr = self.FuncHead func_idx: t.CInt = 0 + func_steps: t.CSizeT = 0 while func is not None: + func_steps += 1 + if func_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: module funcs print(FuncCount=%zu,dump 终止)\n", + self.FuncCount) + stdlib.exit(1) if mode == OUTPUT_STUB: # stub 模式:仅输出 declare(IsDeclared==1) if func.IsDeclared == 1: @@ -364,6 +409,13 @@ def module_add_function(mod: LLVMModule | t.CPtr, func: Function | t.CPtr): return mod.add_function(func) +def module_get_func_head(mod: LLVMModule | t.CPtr) -> Function | t.CPtr: + """获取模块函数链表头(跨模块访问器,绕 stub 字段访问陷阱)""" + if mod is None: + return None + return mod.FuncHead + + def module_add_global(mod: LLVMModule | t.CPtr, gv: GlobalVariable | t.CPtr): return mod.add_global(gv) @@ -461,7 +513,13 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr, # 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用 if sfields is not None: fld: ParamNode | t.CPtr = sfields + fld_steps: t.CSizeT = 0 while fld is not None: + fld_steps += 1 + if fld_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: struct fields(sname=%s,dump 终止)\n", + sname) + stdlib.exit(1) if fld.Ty is not None: _scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1) fld = fld.Next @@ -471,7 +529,13 @@ def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy | t.CPtr, # 顶层结构体(非指针):递归扫描字段 if sfields2 is not None: fld2: ParamNode | t.CPtr = sfields2 + fld2_steps: t.CSizeT = 0 while fld2 is not None: + fld2_steps += 1 + if fld2_steps > IR_DUMP_CHAIN_LIMIT: + stdio.printf("[IR-DUMP] 链表成环: struct fields2(sname=%s,dump 终止)\n", + sname2) + stdlib.exit(1) if fld2.Ty is not None: _scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1) fld2 = fld2.Next @@ -522,6 +586,13 @@ OUTPUT_FULL: t.CDefine = 0 # 完整 IR(stub + text) OUTPUT_STUB: t.CDefine = 1 # 仅声明(header + 类型定义 + declare + external global) OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值) +# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。 +# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时, +# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。 +# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位" +# (对齐 memhub.self_check 的 max_steps 哲学)。 +IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000 + # ============================================================ # LLVMModulePrint: 将模块序列化为 .ll 文本