[clang] [CIR] Fix undeclared variadic function prototype and no-proto call handling (PR #182718)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Feb 21 19:08:56 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Shashwat Pandey (Shashwatpandey4)
<details>
<summary>Changes</summary>
- Allows calls through unprototyped functions instead of errorNYI when the target permits variadic no-proto calls.
- In arrangeFunctionDeclaration, when isNoProtoCallVariadic() is true, emit no-proto functions as variadic with zero fixed parameters so multiple call sites with different arities do not cause CIR verification failures.
- In arrangeFreeFunctionLikeCall, do not force RequiredArgs(0) for no-proto; use the call-site argument types so the cast matches the promoted args (e.g. noProto3).
- In emitCallArgs, set isVariadic for no-proto so arguments use promoted vararg types.
- Fix CIRGenFunctionInfo::Profile to use AddInteger for RequiredArgs so FoldingSet distinguishes RequiredArgs(0) from RequiredArgs::All.
- Update no-prototype.c tests and add no-prototype-variadic.c for the implicit printf / undeclared variadic case.
Fixes #<!-- -->182175
@<!-- -->andykaylor
---
Full diff: https://github.com/llvm/llvm-project/pull/182718.diff
4 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCall.cpp (+28-11)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h (+1-1)
- (added) clang/test/CIR/CodeGen/no-prototype-variadic.c (+12)
- (modified) clang/test/CIR/CodeGen/no-prototype.c (+2-1)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 46c0d9c8fd79a..beb3bd3b62a92 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -792,14 +792,12 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
required = RequiredArgs::getFromProtoWithExtraSlots(proto, 0);
if (proto->hasExtParameterInfos())
cgm.errorNYI("call to functions with extra parameter info");
- } else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
- cast<FunctionNoProtoType>(fnType)))
- cgm.errorNYI("call to function without a prototype");
+ }
SmallVector<CanQualType, 16> argTypes;
+ argTypes.reserve(args.size());
for (const CallArg &arg : args)
argTypes.push_back(cgt.getASTContext().getCanonicalParamType(arg.ty));
-
CanQualType retType = fnType->getReturnType()->getCanonicalTypeUnqualified();
assert(!cir::MissingFeatures::opCallFnInfoOpts());
@@ -930,12 +928,26 @@ CIRGenTypes::arrangeFunctionDeclaration(const FunctionDecl *fd) {
// TODO: setCUDAKernelCallingConvention
assert(!cir::MissingFeatures::cudaSupport());
- // When declaring a function without a prototype, always use a non-variadic
- // type.
+ // Handle C89/gnu89 no-prototype functions (FunctionNoProtoType).
+ //
+ // In C89, a function declared without a prototype does not type-check
+ // arguments and may legally be called with additional arguments.
+ // If we model such functions as non-variadic, the first observed call-site
+ // would freeze the function signature to that arity, causing later calls
+ // with different argument counts to fail CIR verification.
+ //
+ // When the target ABI permits variadic no-proto calls, model the function
+ // as variadic with zero fixed parameters to preserve C semantics and keep
+ // the CIR function type stable across call sites.
if (CanQual<FunctionNoProtoType> noProto =
funcTy.getAs<FunctionNoProtoType>()) {
assert(!cir::MissingFeatures::opCallCIRGenFuncInfoExtParamInfo());
assert(!cir::MissingFeatures::opCallFnInfoOpts());
+
+ if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(noProto.getTypePtr())) {
+ return arrangeCIRFunctionInfo(noProto->getReturnType(), {},
+ noProto->getExtInfo(), RequiredArgs(0));
+ }
return arrangeCIRFunctionInfo(noProto->getReturnType(), {},
noProto->getExtInfo(), RequiredArgs::All);
}
@@ -1213,9 +1225,9 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType);
- // In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
- // However, we still have to push an EH-only cleanup in case we unwind before
- // we make it to the call.
+ // In the Microsoft C++ ABI, aggregate arguments are destructed by the
+ // callee. However, we still have to push an EH-only cleanup in case we
+ // unwind before we make it to the call.
if (argType->isRecordType() &&
argType->castAsRecordDecl()->isParamDestroyedInCallee()) {
assert(!cir::MissingFeatures::msabi());
@@ -1275,6 +1287,10 @@ void CIRGenFunction::emitCallArgs(
assert(!cir::MissingFeatures::opCallCallConv());
argTypes.assign(fpt->param_type_begin() + paramsToSkip,
fpt->param_type_end());
+ } else {
+ // No prototype (e.g. implicit/old-style): allow extra args.
+ // Treat as variadic so we use promoted vararg types for all arguments.
+ isVariadic = true;
}
// If we still have any arguments, emit them using the type of the argument.
@@ -1285,8 +1301,9 @@ void CIRGenFunction::emitCallArgs(
// We must evaluate arguments from right to left in the MS C++ ABI, because
// arguments are destroyed left to right in the callee. As a special case,
// there are certain language constructs taht require left-to-right
- // evaluation, and in those cases we consider the evaluation order requirement
- // to trump the "destruction order is reverse construction order" guarantee.
+ // evaluation, and in those cases we consider the evaluation order
+ // requirement to trump the "destruction order is reverse construction
+ // order" guarantee.
auto leftToRight = true;
assert(!cir::MissingFeatures::msabi());
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index 8f421f723943f..4a06a47f67038 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -117,7 +117,7 @@ class CIRGenFunctionInfo final
RequiredArgs required, CanQualType resultType,
llvm::ArrayRef<CanQualType> argTypes) {
id.AddBoolean(info.getNoReturn());
- id.AddBoolean(required.getOpaqueData());
+ id.AddInteger(required.getOpaqueData());
resultType.Profile(id);
for (const CanQualType &arg : argTypes)
arg.Profile(id);
diff --git a/clang/test/CIR/CodeGen/no-prototype-variadic.c b/clang/test/CIR/CodeGen/no-prototype-variadic.c
new file mode 100644
index 0000000000000..3e6fda491ea8c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/no-prototype-variadic.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=gnu89 -Wno-implicit-function-declaration -fclangir -emit-cir %s -o - | FileCheck %s
+
+int foo(const char *);
+
+int bar(void) {
+ int t = foo("x");
+ printf("Hello %d\n", t);
+ printf("Hi\n");
+ return 0;
+}
+
+// CHECK: cir.func private @printf(!cir.ptr<!s8i>, ...) -> !s32i
\ No newline at end of file
diff --git a/clang/test/CIR/CodeGen/no-prototype.c b/clang/test/CIR/CodeGen/no-prototype.c
index d266ccb86448a..1ef1ab40bfb23 100644
--- a/clang/test/CIR/CodeGen/no-prototype.c
+++ b/clang/test/CIR/CodeGen/no-prototype.c
@@ -11,7 +11,8 @@ int noProto0(x) int x; { return x; }
int test0(int x) {
// CHECK: cir.func {{.*}} @test0
return noProto0(x); // We know the definition. Should be a direct call.
- // CHECK: %{{.+}} = cir.call @noProto0(%{{.+}})
+ // CHECK: %{{.+}} = cir.get_global @noProto0
+ // CHECK: %{{.+}} = cir.call {{.*}}(%{{.+}})
}
// Declaration without prototype followed by its definition, then a correct call.
``````````
</details>
https://github.com/llvm/llvm-project/pull/182718
More information about the cfe-commits
mailing list