https://github.com/valium007 updated https://github.com/llvm/llvm-project/pull/182626
>From 81b33c1d328e23ba5cd32e8b9cc1c0315b43949f Mon Sep 17 00:00:00 2001 From: valium007 <[email protected]> Date: Sat, 21 Feb 2026 05:28:26 +0530 Subject: [PATCH 1/2] [CIR] Fix verifier error for no-proto calls with multiple arguments When emitting a call through a FunctionNoProtoType, the callee type was built from FnInfo's inputs which are empty (RequiredArgs(0) produces no required params). This gave a zero-param non-variadic FuncType, causing the CIR verifier to fire with "incorrect number of operands for callee" whenever such a function was called with arguments (e.g. printf without a header under -fno-builtin or -std=c89). Fix by building the non-variadic callee type from the actual promoted argument types (convertType(arg.ty)) instead, matching what classic CodeGen does in CGCall.cpp. --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 13 ++++++++++--- clang/test/CIR/CodeGen/no-prototype.c | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 83d51bac01d1e..1989ead7e748a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -2039,9 +2039,16 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy, assert(!cir::MissingFeatures::opCallChain()); assert(!cir::MissingFeatures::addressSpace()); cir::FuncType calleeTy = getTypes().getFunctionType(funcInfo); - // get non-variadic function type - calleeTy = cir::FuncType::get(calleeTy.getInputs(), - calleeTy.getReturnType(), false); + + // For unprototyped functions, funcInfo was built with RequiredArgs(0) so + // calleeTy.getInputs() is empty. So we build the non-variadic callee type from + // the actual promoted argument types instead. + SmallVector<mlir::Type, 8> promotedArgTypes; + for (const CallArg &arg : args) + promotedArgTypes.push_back(convertType(arg.ty)); + calleeTy = cir::FuncType::get(promotedArgTypes, + calleeTy.getReturnType(), /*isVarArg=*/false); + auto calleePtrTy = cir::PointerType::get(calleeTy); mlir::Operation *fn = callee.getFunctionPointer(); diff --git a/clang/test/CIR/CodeGen/no-prototype.c b/clang/test/CIR/CodeGen/no-prototype.c index d266ccb86448a..e5d4f601aec9f 100644 --- a/clang/test/CIR/CodeGen/no-prototype.c +++ b/clang/test/CIR/CodeGen/no-prototype.c @@ -82,3 +82,15 @@ int test5(int x) { } int noProto5(int x) { return x; } // CHECK: cir.func {{.*}} no_proto {{.*}} @noProto5(%arg0: !s32i {{.+}}) -> !s32i + +// No-proto declaration without definition, called with multiple args of +// different types. This is the "printf without a header" case and exercises +// the promoted-arg-type path in emitCall. +int noProto6(); +int test6(int x) { +// CHECK: cir.func {{.*}} @test6 + return noProto6("hello", x); + // CHECK: [[GGO:%.*]] = cir.get_global @noProto6 : !cir.ptr<!cir.func<(...) -> !s32i>> + // CHECK: [[CAST:%.*]] = cir.cast bitcast [[GGO]] : !cir.ptr<!cir.func<(...) -> !s32i>> -> !cir.ptr<!cir.func<(!cir.ptr<!s8i>, !s32i) -> !s32i>> + // CHECK: {{%.*}} = cir.call [[CAST]](%{{.*}}, %{{.*}}) : (!cir.ptr<!cir.func<(!cir.ptr<!s8i>, !s32i) -> !s32i>>, !cir.ptr<!s8i>, !s32i) -> !s32i +} >From 2b6df2e09310a10ea27ee2b80a043007f766fe2f Mon Sep 17 00:00:00 2001 From: valium007 <[email protected]> Date: Sat, 21 Feb 2026 17:21:14 +0530 Subject: [PATCH 2/2] [CIR] Fix cir.call verifier error for implicitly declared variadic functions When a function like printf is called without a prior declaration, Clang implicitly declares it. The first call site may see a non-variadic type which gets used to create the cir.func declaration. Subsequent call sites with different argument counts then fail the cir.call operand count verifier. Fix by upgrading the cir.func declaration and existing get_global uses to the correct variadic type when a call site provides a variadic funcType for an existing non-variadic declaration. Fixes https://github.com/llvm/llvm-project/issues/182175 --- clang/lib/CIR/CodeGen/CIRGenCall.cpp | 12 ++++++++---- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 26 ++++++++++++++++++-------- clang/test/CIR/CodeGen/call-no-decl.c | 21 +++++++++++++++++++++ clang/test/CIR/CodeGen/no-prototype.c | 14 +------------- 4 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 clang/test/CIR/CodeGen/call-no-decl.c diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 2039b439c783c..589834480a78e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -1084,13 +1084,17 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, if (auto fnOp = dyn_cast<cir::FuncOp>(calleePtr)) { directFuncOp = fnOp; } else if (auto getGlobalOp = mlir::dyn_cast<cir::GetGlobalOp>(calleePtr)) { - // FIXME(cir): This peephole optimization avoids indirect calls for - // builtins. This should be fixed in the builtin declaration instead by - // not emitting an unecessary get_global in the first place. - // However, this is also used for no-prototype functions. mlir::Operation *globalOp = cgm.getGlobalValue(getGlobalOp.getName()); assert(globalOp && "undefined global function"); directFuncOp = mlir::cast<cir::FuncOp>(globalOp); + // If the existing declaration is non-variadic but the call-site funcInfo + // is variadic (e.g. implicit declaration of printf with no prior + // #include <stdio.h>), upgrade the declaration and get_global in-place. + if (!directFuncOp.getFunctionType().isVarArg() && cirFuncTy.isVarArg()) { + directFuncOp.setFunctionTypeAttr(mlir::TypeAttr::get(cirFuncTy)); + auto newPtrTy = cir::PointerType::get(cirFuncTy); + getGlobalOp.getResult().setType(newPtrTy); + } } else { [[maybe_unused]] mlir::ValueTypeRange<mlir::ResultRange> resultTypes = calleePtr->getResultTypes(); diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1989ead7e748a..6600f9d4aebf9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -796,21 +796,31 @@ static LValue emitFunctionDeclLValue(CIRGenFunction &cgf, const Expr *e, assert(!cir::MissingFeatures::sanitizers()); + // Use funcOp's type as it may have been upgraded to variadic + // (e.g. after seeing printf called with multiple arguments before + // any explicit declaration). convertType(fd->getType()) would return the + // stale non-variadic AST type in that case. mlir::Type fnTy = funcOp.getFunctionType(); mlir::Type ptrTy = cir::PointerType::get(fnTy); mlir::Value addr = cir::GetGlobalOp::create(cgf.getBuilder(), loc, ptrTy, funcOp.getSymName()); - if (funcOp.getFunctionType() != cgf.convertType(fd->getType())) { - fnTy = cgf.convertType(fd->getType()); - ptrTy = cir::PointerType::get(fnTy); - - addr = cir::CastOp::create(cgf.getBuilder(), addr.getLoc(), ptrTy, - cir::CastKind::bitcast, addr); + mlir::Type astFnTy = cgf.convertType(fd->getType()); + if (funcOp.getFunctionType() != astFnTy) { + // Only bitcast when the funcOp type differs from the AST type for a + // reason other than a variadic upgrade. If funcOp is variadic and the + // AST type is not, the funcOp is correct and no bitcast is needed. + auto funcOpTy = mlir::cast<cir::FuncType>(funcOp.getFunctionType()); + auto astTy = mlir::cast<cir::FuncType>(astFnTy); + if (!(funcOpTy.isVarArg() && !astTy.isVarArg())) { + fnTy = astFnTy; + ptrTy = cir::PointerType::get(fnTy); + addr = cir::CastOp::create(cgf.getBuilder(), addr.getLoc(), ptrTy, + cir::CastKind::bitcast, addr); + } } - return cgf.makeAddrLValue(Address(addr, fnTy, align), e->getType(), - AlignmentSource::Decl); + return cgf.makeAddrLValue(Address(addr, fnTy, align), e->getType()); } /// Determine whether we can emit a reference to \p vd from the current diff --git a/clang/test/CIR/CodeGen/call-no-decl.c b/clang/test/CIR/CodeGen/call-no-decl.c new file mode 100644 index 0000000000000..74a1a6e455987 --- /dev/null +++ b/clang/test/CIR/CodeGen/call-no-decl.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=gnu89 -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +// Regression test for https://github.com/llvm/llvm-project/issues/182175 +// A function called with no prior declaration must get a variadic CIR function +// type so that multiple call sites with different argument counts do not +// trigger a verifier error. + +char temp[] = "some str"; +int foo(const char *); + +int bar(void) { + int t = foo(temp); + printf("Hello %d!\n", t); + printf("Works!\n"); + return 0; +} + +// CHECK: cir.func private @printf(!cir.ptr<!s8i>, ...) -> !s32i +// CHECK: cir.call @printf({{.*}}, {{.*}}) : (!cir.ptr<!s8i>, !s32i) -> !s32i +// CHECK: cir.call @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 e5d4f601aec9f..ea256bb70c89e 100644 --- a/clang/test/CIR/CodeGen/no-prototype.c +++ b/clang/test/CIR/CodeGen/no-prototype.c @@ -81,16 +81,4 @@ int test5(int x) { // CHECK: {{%.*}} = cir.call [[CAST]]() : (!cir.ptr<!cir.func<() -> !s32i>>) -> !s32i } int noProto5(int x) { return x; } -// CHECK: cir.func {{.*}} no_proto {{.*}} @noProto5(%arg0: !s32i {{.+}}) -> !s32i - -// No-proto declaration without definition, called with multiple args of -// different types. This is the "printf without a header" case and exercises -// the promoted-arg-type path in emitCall. -int noProto6(); -int test6(int x) { -// CHECK: cir.func {{.*}} @test6 - return noProto6("hello", x); - // CHECK: [[GGO:%.*]] = cir.get_global @noProto6 : !cir.ptr<!cir.func<(...) -> !s32i>> - // CHECK: [[CAST:%.*]] = cir.cast bitcast [[GGO]] : !cir.ptr<!cir.func<(...) -> !s32i>> -> !cir.ptr<!cir.func<(!cir.ptr<!s8i>, !s32i) -> !s32i>> - // CHECK: {{%.*}} = cir.call [[CAST]](%{{.*}}, %{{.*}}) : (!cir.ptr<!cir.func<(!cir.ptr<!s8i>, !s32i) -> !s32i>>, !cir.ptr<!s8i>, !s32i) -> !s32i -} +// CHECK: cir.func {{.*}} no_proto {{.*}} @noProto5(%arg0: !s32i {{.+}}) -> !s32i \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
