fix(gen): arg 糖支持转发形态的 va_list

_translate_arg_sugar 原本在 trans._va_list_ptr 为 None 时(非变参的 vprintf 式包装函数,如 vsnprintf(buf,size,fmt,args))直接返回 0,转发下来的 va_list 因此读不到;现增加回退:按名 args 取形参 alloca 并 load 出槽内的 va_list 指针(自身变参形态仍用 trans._va_list_ptr,不 load)。点号形式 stdarg.arg(...) 经 ExprGenBase.get_func_name 取属性短名,已天然命中同一分派,未改动。
This commit is contained in:
TermiNexus committed 2026-10-07 20:15:40 +08:00
1 parent c20bd4734b
commit 2ab6342d3c
1 file changed
+20 -5
+20 -5
View File
@@ -68,8 +68,23 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
# Emit the va_arg instruction
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit va_arg using the current function's implicit va_list (trans._va_list_ptr)."""
if trans._va_list_ptr is None: return mlirlite.const_int32(pool, 0)
"""Emit va_arg reading from the enclosing variadic's own va_list or a forwarded va_list."""
# arg() has two forms:
# (1) Own-variadic form: inside `def f(a, *args)`, trans._va_list_ptr is the i8* address of
# the function's implicit va_list buffer; read the next vararg straight from it.
# (2) Forwarding form: in a NON-variadic wrapper such as vsnprintf(buf, size, fmt, args),
# the caller's va_list pointer is passed by value through the 4th param named "args"
# (vprintf style, see includes/viperlib.vp); there is no implicit va_list here, so
# trans._va_list_ptr is None and the pointer must be recovered from that param.
va_ptr: mlirlite.Value* = trans._va_list_ptr
if va_ptr is None:
# The "args" param's alloca is an i8** slot holding the forwarded i8* va_list value, so it
# MUST be loaded once: va_arg needs the pointer value itself (i8*), not the slot address.
args_alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, "args")
if args_alloca is None: return mlirlite.const_int32(pool, 0)
if args_alloca.Ty is None: return mlirlite.const_int32(pool, 0)
va_ptr = mlirlite.build_load(builder, args_alloca.Ty.Pointee, args_alloca)
if va_ptr is None: return mlirlite.const_int32(pool, 0)
# First arg: target type (optional, defaults to i32)
arg_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
@@ -83,12 +98,12 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
abits_arg: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_ty)
if abits_arg != 0 and abits_arg < 64:
read_ty = mlirlite.Int64(pool)
arg_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
arg_raw: mlirlite.Value* = mlirlite.build_va_arg(builder, va_ptr, read_ty)
if arg_raw is None: return mlirlite.const_int32(pool, 0)
return mlirlite.build_trunc(builder, arg_raw, arg_ty)
# Emit va_arg, using trans._va_list_ptr as the va_list address
return mlirlite.build_va_arg(builder, trans._va_list_ptr, read_ty)
# Emit va_arg, using va_ptr as the va_list address (own buffer or forwarded pointer)
return mlirlite.build_va_arg(builder, va_ptr, read_ty)
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)