[clang] [CIR] Call to variadic functions (PR #141942)

Sirui Mu via cfe-commits cfe-commits at lists.llvm.org
Thu May 29 06:33:06 PDT 2025


https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/141942

This PR adds support for calling variadic functions in CIR.

>From 800e4b1a02520e4c3137a02e284cc8406cf36602 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Thu, 29 May 2025 17:21:06 +0800
Subject: [PATCH] [CIR] Call to variadic functions

---
 clang/include/clang/CIR/MissingFeatures.h  |  1 -
 clang/lib/CIR/CodeGen/CIRGenCall.cpp       | 20 +++++++++++++++++---
 clang/lib/CIR/CodeGen/CIRGenFunction.h     |  4 ++++
 clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h | 20 ++++++++++++++++++++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp    |  6 +++---
 clang/test/CIR/CodeGen/call.cpp            | 14 ++++++++++++++
 clang/test/CIR/IR/invalid-call.cir         | 12 ++++++++++++
 7 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 56bf9b1130f12..f7385a672f6af 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -100,7 +100,6 @@ struct MissingFeatures {
   static bool opCallAttrs() { return false; }
   static bool opCallSurroundingTry() { return false; }
   static bool opCallASTAttr() { return false; }
-  static bool opCallVariadic() { return false; }
   static bool opCallObjCMethod() { return false; }
   static bool opCallExtParameterInfo() { return false; }
   static bool opCallCIRGenFuncInfoParamInfo() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 1317f8c6c073a..ea76ddb7f40ec 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -122,7 +122,7 @@ arrangeFreeFunctionLikeCall(CIRGenTypes &cgt, CIRGenModule &cgm,
 
   if (const auto *proto = dyn_cast<FunctionProtoType>(fnType)) {
     if (proto->isVariadic())
-      cgm.errorNYI("call to variadic function");
+      required = RequiredArgs::forPrototypePlus(proto, 0);
     if (proto->hasExtParameterInfos())
       cgm.errorNYI("call to functions with extra parameter info");
   } else if (cgm.getTargetCIRGenInfo().isNoProtoCallVariadic(
@@ -409,6 +409,18 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
   args.add(emitAnyExprToTemp(e), argType);
 }
 
+QualType CIRGenFunction::getVarArgType(const Expr *arg) {
+  // System headers on Windows define NULL to 0 instead of 0LL on Win64. MSVC
+  // implicitly widens null pointer constants that are arguments to varargs
+  // functions to pointer-sized ints.
+  if (!getTarget().getTriple().isOSWindows())
+    return arg->getType();
+
+  assert(!cir::MissingFeatures::msabi());
+  cgm.errorNYI(arg->getSourceRange(), "getVarArgType: NYI for Windows target");
+  return {};
+}
+
 /// Similar to emitAnyExpr(), however, the result will always be accessible
 /// even if no aggregate location is provided.
 RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
@@ -429,18 +441,20 @@ void CIRGenFunction::emitCallArgs(
   assert(!cir::MissingFeatures::opCallCallConv());
 
   // First, if a prototype was provided, use those argument types.
-  assert(!cir::MissingFeatures::opCallVariadic());
+  bool isVariadic = false;
   if (prototype.p) {
     assert(!cir::MissingFeatures::opCallObjCMethod());
 
     const auto *fpt = cast<const FunctionProtoType *>(prototype.p);
+    isVariadic = fpt->isVariadic();
+    assert(!cir::MissingFeatures::opCallCallConv());
     argTypes.assign(fpt->param_type_begin() + paramsToSkip,
                     fpt->param_type_end());
   }
 
   // If we still have any arguments, emit them using the type of the argument.
   for (const clang::Expr *a : llvm::drop_begin(argRange, argTypes.size()))
-    argTypes.push_back(a->getType());
+    argTypes.push_back(isVariadic ? getVarArgType(a) : a->getType());
   assert(argTypes.size() == (size_t)(argRange.end() - argRange.begin()));
 
   // We must evaluate arguments from right to left in the MS C++ ABI, because
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 74f2e4043933d..0badde024b166 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -106,6 +106,7 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   CIRGenTypes &getTypes() const { return cgm.getTypes(); }
 
+  const TargetInfo &getTarget() const { return cgm.getTarget(); }
   mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
 
 private:
@@ -791,6 +792,9 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitOpenACCDeclare(const OpenACCDeclareDecl &d);
   void emitOpenACCRoutine(const OpenACCRoutineDecl &d);
+
+private:
+  QualType getVarArgType(const Expr *arg);
 };
 
 } // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
index 9886574fd463b..759d019de52f0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunctionInfo.h
@@ -39,6 +39,26 @@ class RequiredArgs {
 
   bool allowsOptionalArgs() const { return numRequired != ~0U; }
 
+  /// Compute the arguments required by the given formal prototype, given that
+  /// there may be some additional, non-formal arguments in play.
+  ///
+  /// If FD is not null, this will consider pass_object_size params in FD.
+  static RequiredArgs
+  forPrototypePlus(const clang::FunctionProtoType *prototype,
+                   unsigned additional) {
+    if (!prototype->isVariadic())
+      return All;
+
+    if (prototype->hasExtParameterInfos())
+      additional += llvm::count_if(
+          prototype->getExtParameterInfos(),
+          [](const clang::FunctionProtoType::ExtParameterInfo &extInfo) {
+            return extInfo.hasPassObjectSize();
+          });
+
+    return RequiredArgs(prototype->getNumParams() + additional);
+  }
+
   /// Compute the arguments required by the given formal prototype, given that
   /// there may be some additional, non-formal arguments in play.
   ///
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 9e2b2908b22d8..3e6d22879a78c 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -601,10 +601,10 @@ verifyCallCommInSymbolUses(mlir::Operation *op,
     unsigned numCallOperands = callIf.getNumArgOperands();
     unsigned numFnOpOperands = fnType.getNumInputs();
 
-    assert(!cir::MissingFeatures::opCallVariadic());
-
-    if (numCallOperands != numFnOpOperands)
+    if (!fnType.isVarArg() && numCallOperands != numFnOpOperands)
       return op->emitOpError("incorrect number of operands for callee");
+    if (fnType.isVarArg() && numCallOperands < numFnOpOperands)
+      return op->emitOpError("too few operands for callee");
 
     for (unsigned i = 0, e = numFnOpOperands; i != e; ++i)
       if (callIf.getArgOperand(i).getType() != fnType.getInput(i))
diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp
index 792f57afd6bd1..741cadeb5c764 100644
--- a/clang/test/CIR/CodeGen/call.cpp
+++ b/clang/test/CIR/CodeGen/call.cpp
@@ -56,3 +56,17 @@ int f7(int (*ptr)(int, int)) {
 // LLVM-LABEL: define i32 @_Z2f7PFiiiE
 // LLVM:         %[[#ptr:]] = load ptr, ptr %{{.+}}
 // LLVM-NEXT:    %{{.+}} = call i32 %[[#ptr]](i32 1, i32 2)
+
+void f8(int a, ...);
+void f9() {
+  f8(1);
+  f8(1, 2, 3, 4);
+}
+
+// CIR-LABEL: cir.func @_Z2f9v()
+// CIR:         cir.call @_Z2f8iz(%{{.+}}) : (!s32i) -> ()
+// CIR:         cir.call @_Z2f8iz(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}) : (!s32i, !s32i, !s32i, !s32i) -> ()
+
+// LLVM-LABEL: define void @_Z2f9v()
+// LLVM:         call void (i32, ...) @_Z2f8iz(i32 1)
+// LLVM:         call void (i32, ...) @_Z2f8iz(i32 1, i32 2, i32 3, i32 4)
diff --git a/clang/test/CIR/IR/invalid-call.cir b/clang/test/CIR/IR/invalid-call.cir
index 8a584bae70878..3ebb771ed72e7 100644
--- a/clang/test/CIR/IR/invalid-call.cir
+++ b/clang/test/CIR/IR/invalid-call.cir
@@ -68,3 +68,15 @@ cir.func @f11() {
   cir.call @f10(%0, %1) : (!s32i, !u32i) -> ()
   cir.return
 }
+
+// -----
+
+!s32i = !cir.int<s, 32>
+
+cir.func @f12(!s32i, !s32i, ...)
+cir.func @f13() {
+  %0 = cir.const #cir.int<1> : !s32i
+  // expected-error @below {{too few operands for callee}}
+  cir.call @f12(%0) : (!s32i) -> ()
+  cir.return
+}



More information about the cfe-commits mailing list