https://github.com/Shashwatpandey4 created https://github.com/llvm/llvm-project/pull/182718
- 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 >From aa10b4593eed289599edd2aefcf4116967874b19 Mon Sep 17 00:00:00 2001 From: Shashwat Pandey <[email protected]> Date: Sat, 21 Feb 2026 20:01:07 -0700 Subject: [PATCH] [CIR] Fix undeclared variadic function prototype and no-proto call handling Enable no-proto calls and variadic no-proto declarations when the target allows it; fix call-site type to use actual args; fix Profile for RequiredArgs; add/update tests. Fixes #182175 Co-authored-by: Cursor <[email protected]> --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 39 +++++++++++++------ clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 2 +- .../test/CIR/CodeGen/no-prototype-variadic.c | 12 ++++++ clang/test/CIR/CodeGen/no-prototype.c | 3 +- 4 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 clang/test/CIR/CodeGen/no-prototype-variadic.c diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 2039b439c783c..cea4ca393f76a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -782,14 +782,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()); @@ -920,12 +918,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); } @@ -1203,9 +1215,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()); @@ -1265,6 +1277,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. @@ -1275,8 +1291,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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
