fix(codegen): arg 糖支持 vprintf 式转发的 va_list,补 stdarg 模块限定分派
_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 死代码。
This commit is contained in:
1 parent
9656bb161a
commit
75aae26881
2 files changed
+28
-86
No files matched your search
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user