From 75aae26881ab5eeada53d973dd91fa38cae4d2ee Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 7 Oct 2026 20:15:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(codegen):=20arg=20=E7=B3=96=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20vprintf=20=E5=BC=8F=E8=BD=AC=E5=8F=91=E7=9A=84=20va?= =?UTF-8?q?=5Flist=EF=BC=8C=E8=A1=A5=20stdarg=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=99=90=E5=AE=9A=E5=88=86=E6=B4=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _HandleArgLlvm 的回退分支原先把 Gen.variables 里的形参 alloca 直接当 va_list 用,转发形态(vsnprintf(buf,size,fmt,args))因此多一层间接、读错内存;现改为先 load 出槽里的 va_list 指针(自身变参形态仍走 _direct_values 缓冲地址、不 load)。另新增 ModulePath == 'stdarg' 分派,使 stdarg.arg/va_arg/va_start/va_end 命中与裸名相同的内建(点号形式此前会落到外部调用产生未定义符号 @arg)。同时删除 ExprCallHandle 里重复且无调用者的 _HandleArgLlvm 死代码。 --- lib/core/Handles/HandlesExprBuiltin.py | 16 ++++- lib/core/Handles/HandlesExprCall.py | 98 ++++---------------------- 2 files changed, 28 insertions(+), 86 deletions(-) diff --git a/lib/core/Handles/HandlesExprBuiltin.py b/lib/core/Handles/HandlesExprBuiltin.py index f18fc6e..aa068fa 100644 --- a/lib/core/Handles/HandlesExprBuiltin.py +++ b/lib/core/Handles/HandlesExprBuiltin.py @@ -1155,10 +1155,24 @@ class ExprBuiltinHandle(BaseHandle): va_list_ptr = variadic_info.get('va_list_ptr') if not va_list_ptr: vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args' + # va_list 的两种形态(回退分支只会命中其中之一,处理方式**不同**): + # 1) 自身变参形态(`def f(a, *args)` 内部调用 arg(T)):HandlesFunctions + # 在函数入口自动发射 llvm.va_start,并把 **va_list 缓冲的地址** 以 + # args 为名登记进 Gen._direct_values。此处的值本身就是 va_arg 需要的 + # 那个 va_list 指针,**绝不能 load**——load 会把缓冲首字节当成指针再 + # 解引用,读到垃圾。 + # 2) 转发形态(如 `vsnprintf(buf, size, fmt, args)`,被调函数自身**非** + # 变参):第 4 个形参 args 在 LLVM 里是 alloca 出来的 i8** 槽,槽里 + # 存着**调用方传下来的 va_list 指针值**(sprintf/snprintf 把自己的 + # *args 指针转发下来)。va_arg 需要的是槽里存的那个 i8* 指针值,所以 + # 必须 load 形参槽把指针取出来;直接传 alloca(i8**)会多一层间接, + # 把"指向指针的槽"当成 va_list 缓冲,读到的位置完全错误。 + # 两种形态的区别是"登记进 _direct_values 的是缓冲地址 / 形参槽的 alloca": + # 前者直接用,后者必须 load 出槽内容。 if vararg_name in Gen._direct_values: va_list_ptr = Gen._direct_values[vararg_name] elif vararg_name in Gen.variables: - va_list_ptr = Gen.variables[vararg_name] + va_list_ptr = Gen._load(Gen.variables[vararg_name], name="varg_forward_load") if not va_list_ptr: return ir.Constant(ir.IntType(32), 0) # 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index f3c790b..fa5118d 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -967,6 +967,19 @@ class ExprCallHandle(BaseHandle): return self._HandleLLVMIRLlvm(Node) elif FuncAttr in ('LInp', 'LOut'): return ir.Constant(ir.IntType(32), 0) + # stdarg 模块的变参糖:stdarg.arg / stdarg.va_arg / stdarg.va_start / + # stdarg.va_end 命中与裸名同名内建**完全相同**的处理(stdarg 是编译器 + # 拦截的糖模块,不是真符号,与 c 模块的语法设施同一口径)。仅当 + # ModulePath 恰为 'stdarg' 时进入,不会抢掉其它模块/用户模块的调用。 + if ModulePath == 'stdarg': + if FuncAttr == 'arg': + return self.Trans.ExprBuiltinHandle._HandleArgLlvm(Node.args) + elif FuncAttr == 'va_arg': + return self.Trans.ExprBuiltinHandle._HandleVaArgLlvm(Node.args) + elif FuncAttr == 'va_start': + return self.Trans.ExprBuiltinHandle._HandleVaStartLlvm(Node.args) + elif FuncAttr == 'va_end': + return self.Trans.ExprBuiltinHandle._HandleVaEndLlvm(Node.args) if FuncAttr in Gen.structs: result = self._HandleClassNewLlvm(Node, FuncAttr, module_sha1=_ctor_module_sha1) if result is not None: @@ -4906,91 +4919,6 @@ class ExprCallHandle(BaseHandle): Gen = self.Trans.LlvmGen return None - def _HandleArgLlvm(self, args: list[ast.AST], CallNode: ast.Call | None = None) -> ir.Value | None: - Gen = self.Trans.LlvmGen - va_list_ptr = None - variadic_info = getattr(Gen, '_va_arg_info', None) - if not variadic_info: - variadic_info = getattr(Gen, '_variadic_info', None) - if variadic_info: - va_list_ptr = variadic_info.get('va_list_ptr') - if not va_list_ptr: - vararg_name = variadic_info.get('vararg_name', 'args') if variadic_info else 'args' - if vararg_name in Gen._direct_values: - va_list_ptr = Gen._direct_values[vararg_name] - elif vararg_name in Gen.variables: - va_list_ptr = Gen.variables[vararg_name] - if not va_list_ptr: - return ir.Constant(ir.IntType(32), 0) - target_type = ir.IntType(32) - if args: - type_arg = args[0] - if isinstance(type_arg, ast.Name): - type_name = type_arg.id - if type_name in ('str', 'bytes'): - target_type = ir.PointerType(ir.IntType(8)) - elif type_name == 'CPtr': - target_type = ir.PointerType(ir.IntType(8)) - elif type_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[type_name]) - else: - ctype_cls = CTypeRegistry.GetClassByName(type_name) - if ctype_cls is not None: - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - else: - resolved = CTypeRegistry.ResolveName(type_name) - if resolved is not None: - ctype_cls, ptr_level = resolved - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if llvm_str: - base = Gen._type_str_to_llvm(llvm_str) - if base is not None: - for _ in range(ptr_level): - if isinstance(base, ir.VoidType): - base = ir.IntType(8).as_pointer() - else: - base = ir.PointerType(base) - target_type = base - elif isinstance(type_arg, ast.Attribute): - if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): - attr_name = type_arg.attr - ctype_cls = CTypeRegistry.GetClassByName(attr_name) - if ctype_cls is not None: - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - else: - assign_node = getattr(self.Trans, '_current_assign_node', None) - if assign_node: - ann = getattr(assign_node, 'annotation', None) - if ann: - ann_name = None - if isinstance(ann, ast.Name): - ann_name = ann.id - elif isinstance(ann, ast.Attribute): - ann_name = ann.attr - elif isinstance(ann, ast.BinOp) and isinstance(ann.op, ast.BitOr): - ann_name = ExtractTypeNameFromBinOp(ann) - if ann_name: - ctype_cls = CTypeRegistry.GetClassByName(ann_name) - if ctype_cls is not None: - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - elif ann_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[ann_name]) - va_arg_counter = getattr(Gen, '_va_arg_counter', 0) - Gen._va_arg_counter = va_arg_counter + 1 - result = Gen.emit_va_arg(va_list_ptr, target_type) - if result is not None and getattr(result, 'name', ''): - result.name = f"va_arg_result_{Gen._va_arg_counter}" - return result - def _HandleVaArgLlvm(self, args: list[ast.AST]) -> ir.Constant: Gen: LlvmGeneratorMixin = self.Trans.LlvmGen return ir.Constant(ir.IntType(32), 0)