Skip to content

CIRGen drops variadic on programmatically-constructed printf FunctionDecl #239

Description

@kumarak

When a clang::FunctionDecl for printf is constructed programmatically (FunctionDecl::Create + TranslationUnitDecl::addDecl) — bypassing Sema::ActOnFunctionDeclarator — CIRGen emits the resulting cir.func as non-variadic, even though the underlying FunctionProtoType::isVariadic() is true at every observable point (immediately after Create, in redecls(), and at the start of CIR generation).

snprintf and fprintf constructed via the same path emit variadic cir.func correctly. Only printf is affected. Standard Clang (which routes through Sema::ActOnFunctionDeclarator and sets BuiltinID = BIprintf) emits variadic correctly.

The malformed CIR then fails LLVM lowering with:

loc(...): error: 'cir.call' op incorrect number of operands for callee
LLVM ERROR: The pass manager failed to lower CIR to LLVMIR dialect!

Reproducer

Using a test fixture that ends up calling printf with a format + 1 arg:

builds/default/tools/patchir-decomp/Debug/patchir-decomp \
  --input local_test/option2-verify/triage/FUN_00015140.json \
  --use-structuring-pass --emit-cir --print-tu \
  --output /tmp/repro

Then inspect the CIR:

$ grep -E 'sym_name = "printf"|sym_name = "snprintf"|sym_name = "fprintf"' /tmp/repro.cir
"cir.func"() <{function_type = !cir.func<(!cir.ptr<!s8i>, !u32i, !cir.ptr<!s8i>, ...) -> !s32i>, ..., sym_name = "snprintf", ...
"cir.func"() <{function_type = !cir.func<(!cir.ptr<!rec__IO_FILE>, !cir.ptr<!s8i>, ...) -> !s32i>, ..., sym_name = "fprintf", ...
"cir.func"() <{function_type = !cir.func<(!cir.ptr<!s8i>) -> !s32i>, ..., sym_name = "printf", ...

Compare with the printed AST (/tmp/repro.c):

int snprintf(signed char *param_0, size_t param_1, signed char *param_2, ...);
int fprintf(FILE *param_0, signed char *param_1, ...);
int printf(signed char *param_0, ...);   // <-- AST is variadic

All three FunctionDecls are variadic at the AST level — but only printf's cir.func is missing the ....

Observable state of printf's FunctionDecl just before CIRGen

  • getNumParams() == 1
  • getBuiltinID() == 0 (parser bypass — Sema's builtin recognition never runs)
  • getCanonicalDecl() == getFirstDecl() == getMostRecentDecl() == this
  • redecls() yields one entry, that entry is the same FunctionDecl
  • getType()->castAs<FunctionProtoType>()->isVariadic() == true
  • hasBody() == false

snprintf and fprintf have identical structural shape (BuiltinID==0, single decl, no body, isVariadic==1) yet emit variadic cir.func correctly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions