https://github.com/mmha created https://github.com/llvm/llvm-project/pull/153834

Part of https://github.com/llvm/llvm-project/issues/153286.
Depends on https://github.com/llvm/llvm-project/pull/153819.

This patch adds support for __builtin_va_arg by adding the cir.va.arg operator. 
Unlike the incubator it doesn't depend on any target specific lowering (yet) 
but maps to llvm.va_arg.

>From dae7b978b994f5d160fae784fcd32bb3734f3208 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 17:18:19 +0200
Subject: [PATCH 1/2] [CIR] Upstream __builtin_va_start and __builtin_va_end

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 77 +++++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 21 +++++
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          |  7 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  6 ++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        | 13 ++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 22 ++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h   | 20 +++++
 clang/test/CIR/CodeGen/var_arg.c              | 48 ++++++++++++
 8 files changed, 209 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/var_arg.c

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a77e9199cdc96..86c8b59bf36f0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3415,4 +3415,81 @@ def CIR_FAbsOp : CIR_UnaryFPToFPBuiltinOp<"fabs", 
"FAbsOp"> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// Variadic Operations
+//===----------------------------------------------------------------------===//
+
+def CIR_VAStartOp : CIR_Op<"va.start"> {
+  let summary = "Starts a variable argument list";
+  let description = [{
+    The cir.va.start operation models the C/C++ va_start macro by
+    initializing a variable argument list at the given va_list storage
+    location.
+
+    The operand must be a pointer to the target's `va_list` representation.
+    This operation has no results and produces its effect by mutating the
+    storage referenced by the pointer operand.
+
+    Each `cir.va.start` must be paired with a corresponding `cir.va.end`
+    on the same logical `va_list` object along all control-flow paths. After
+    `cir.va.end`, the `va_list` must not be accessed unless reinitialized
+    with another `cir.va.start`.
+
+    Lowering typically maps this to the LLVM intrinsic `llvm.va_start`,
+    passing the appropriately decayed pointer to the underlying `va_list`
+    storage.
+
+    Example:
+
+    ```mlir
+    // %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
+    %p = cir.cast(array_to_ptrdecay, %args
+          : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
+        !cir.ptr<!rec___va_list_tag>
+    cir.va.start %p : !cir.ptr<!rec___va_list_tag>
+    ```
+  }];
+  let arguments = (ins CIR_PointerType:$arg_list);
+
+  let assemblyFormat = [{
+    $arg_list attr-dict `:` type(operands)
+  }];
+}
+
+def CIR_VAEndOp : CIR_Op<"va.end"> {
+  let summary = "Ends a variable argument list";
+  let description = [{
+    The `cir.va.end` operation models the C/C++ va_end macro by finalizing
+    and cleaning up a variable argument list previously initialized with
+    `cir.va.start`.
+
+    The operand must be a pointer to the target's `va_list` representation.
+    This operation has no results and produces its effect by mutating the
+    storage referenced by the pointer operand.
+
+    `cir.va.end` must only be called after a matching `cir.va.start` on the
+    same `va_list` along all control-flow paths. After `cir.va.end`, the
+    `va_list` is invalid and must not be accessed unless reinitialized.
+
+    Lowering typically maps this to the LLVM intrinsic `llvm.va_end`,
+    passing the appropriately decayed pointer to the underlying `va_list`
+    storage.
+
+    Example:
+    ```mlir
+    // %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
+    %p = cir.cast(array_to_ptrdecay, %args
+          : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
+        !cir.ptr<!rec___va_list_tag>
+    cir.va.end %p : !cir.ptr<!rec___va_list_tag>
+    ```
+  }];
+
+  let arguments = (ins CIR_PointerType:$arg_list);
+
+  let assemblyFormat = [{
+    $arg_list attr-dict `:` type(operands)
+  }];
+}
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 36aea4c1d39ce..25ca10e801194 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -125,6 +125,18 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
   default:
     break;
 
+  // C stdarg builtins.
+  case Builtin::BI__builtin_stdarg_start:
+  case Builtin::BI__builtin_va_start:
+  case Builtin::BI__va_start:
+  case Builtin::BI__builtin_va_end: {
+    emitVAStartEnd(builtinID == Builtin::BI__va_start
+                       ? emitScalarExpr(e->getArg(0))
+                       : emitVAListRef(e->getArg(0)).getPointer(),
+                   builtinID != Builtin::BI__builtin_va_end);
+    return {};
+  }
+
   case Builtin::BIfabs:
   case Builtin::BIfabsf:
   case Builtin::BIfabsl:
@@ -361,3 +373,12 @@ mlir::Value CIRGenFunction::emitCheckedArgForAssume(const 
Expr *e) {
                "emitCheckedArgForAssume: sanitizers are NYI");
   return {};
 }
+
+void CIRGenFunction::emitVAStartEnd(mlir::Value argValue, bool isStart) {
+  // LLVM codegen casts to *i8, no real gain on doing this for CIRGen this
+  // early, defer to LLVM lowering.
+  if (isStart)
+    cir::VAStartOp::create(builder, argValue.getLoc(), argValue);
+  else
+    cir::VAEndOp::create(builder, argValue.getLoc(), argValue);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 8bcca6f5d1803..00f2a64281b8c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -90,11 +90,8 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr 
*expr,
     } break;
 
     // Array-to-pointer decay. TODO(cir): BaseInfo and TBAAInfo.
-    case CK_ArrayToPointerDecay: {
-      cgm.errorNYI(expr->getSourceRange(),
-                   "emitPointerWithAlignment: array-to-pointer decay");
-      return Address::invalid();
-    }
+    case CK_ArrayToPointerDecay:
+      return emitArrayToPointerDecay(ce->getSubExpr());
 
     case CK_UncheckedDerivedToBase:
     case CK_DerivedToBase: {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index d6a0792292604..917afa8e78021 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1080,4 +1080,10 @@ void CIRGenFunction::emitVariablyModifiedType(QualType 
type) {
   } while (type->isVariablyModifiedType());
 }
 
+Address CIRGenFunction::emitVAListRef(const Expr *e) {
+  if (getContext().getBuiltinVaListType()->isArrayType())
+    return emitPointerWithAlignment(e);
+  return emitLValue(e).getAddress();
+}
+
 } // namespace clang::CIRGen
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 9a887ec047f86..1c89c2c9d57d3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1411,6 +1411,19 @@ class CIRGenFunction : public CIRGenTypeCache {
                                     const clang::Stmt *thenS,
                                     const clang::Stmt *elseS);
 
+  /// Build a "reference" to a va_list; this is either the address or the value
+  /// of the expression, depending on how va_list is defined.
+  Address emitVAListRef(const Expr *e);
+
+  /// Emits a CIR variable-argument operation, either
+  /// \c cir.va.start or \c cir.va.end.
+  ///
+  /// \param argValue A reference to the \c va_list as emitted by either
+  /// \c emitVAListRef or \c emitMSVAListRef.
+  ///
+  /// \param isStart If \c true, emits \c cir.va.start, otherwise \c 
cir.va.end.
+  void emitVAStartEnd(mlir::Value argValue, bool isStart);
+
   /// ----------------------
   /// CIR build helpers
   /// -----------------
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 1ea296a6887ef..e51fbc9eadaf0 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2336,6 +2336,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMTrapOpLowering,
                CIRToLLVMUnaryOpLowering,
                CIRToLLVMUnreachableOpLowering,
+               CIRToLLVMVAEndOpLowering,
+               CIRToLLVMVAStartOpLowering,
                CIRToLLVMVecCmpOpLowering,
                CIRToLLVMVecCreateOpLowering,
                CIRToLLVMVecExtractOpLowering,
@@ -3035,6 +3037,26 @@ mlir::LogicalResult 
CIRToLLVMInlineAsmOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMVAStartOpLowering::matchAndRewrite(
+    cir::VAStartOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
+  auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
+                                              adaptor.getArgList());
+  rewriter.replaceOpWithNewOp<mlir::LLVM::VaStartOp>(op, vaList);
+  return mlir::success();
+}
+
+mlir::LogicalResult CIRToLLVMVAEndOpLowering::matchAndRewrite(
+    cir::VAEndOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
+  auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
+                                              adaptor.getArgList());
+  rewriter.replaceOpWithNewOp<mlir::LLVM::VaEndOp>(op, vaList);
+  return mlir::success();
+}
+
 std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
   return std::make_unique<ConvertCIRToLLVMPass>();
 }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index e32bf2d1bae0c..72315ac127e1b 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -684,6 +684,26 @@ class CIRToLLVMInlineAsmOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+class CIRToLLVMVAStartOpLowering
+    : public mlir::OpConversionPattern<cir::VAStartOp> {
+public:
+  using mlir::OpConversionPattern<cir::VAStartOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::VAStartOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
+class CIRToLLVMVAEndOpLowering
+    : public mlir::OpConversionPattern<cir::VAEndOp> {
+public:
+  using mlir::OpConversionPattern<cir::VAEndOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::VAEndOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 } // namespace direct
 } // namespace cir
 
diff --git a/clang/test/CIR/CodeGen/var_arg.c b/clang/test/CIR/CodeGen/var_arg.c
new file mode 100644
index 0000000000000..7cd10e1ca42d2
--- /dev/null
+++ b/clang/test/CIR/CodeGen/var_arg.c
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+void varargs(int count, ...) {
+    __builtin_va_list args;
+    __builtin_va_start(args, 12345);
+    __builtin_va_end(args);
+}
+
+// CIR: !rec___va_list_tag = !cir.record<struct "__va_list_tag" {!u32i, !u32i, 
!cir.ptr<!void>, !cir.ptr<!void>}
+
+// CIR: cir.func dso_local @varargs(%[[COUNT_ARG:.+]]: !s32i {{.*}}, ...) 
{{.*}}
+// CIR:   %[[COUNT:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["count", init]
+// CIR:   %[[ARGS:.+]]  = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
+// CIR:   cir.store %[[COUNT_ARG]], %[[COUNT]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[ARGS_DECAY1:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.va.start %[[ARGS_DECAY1]] : !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[ARGS_DECAY2:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.va.end %[[ARGS_DECAY2]] : !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.return
+
+// LLVM: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
+
+// LLVM: define dso_local void @varargs(i32 %[[ARG0:.+]], ...)
+// LLVM:   %[[COUNT_ADDR:.+]] = alloca i32, i64 1
+// LLVM:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], i64 1
+// LLVM:   store i32 %[[ARG0]], ptr %[[COUNT_ADDR]]
+// LLVM:   %[[GEP1:.+]] = getelementptr %struct.__va_list_tag, ptr %[[ARGS]], 
i32 0
+// LLVM:   call void @llvm.va_start.p0(ptr %[[GEP1]])
+// LLVM:   %[[GEP2:.+]] = getelementptr %struct.__va_list_tag, ptr %[[ARGS]], 
i32 0
+// LLVM:   call void @llvm.va_end.p0(ptr %[[GEP2]])
+// LLVM:   ret void
+
+// OGCG: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
+
+// OGCG: define dso_local void @varargs(i32 noundef %[[COUNT:.+]], ...)
+// OGCG:   %[[COUNT_ADDR:.+]] = alloca i32
+// OGCG:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag]
+// OGCG:   store i32 %[[COUNT]], ptr %[[COUNT_ADDR]]
+// OGCG:   %[[ARRDECAY1:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
+// OGCG:   call void @llvm.va_start.p0(ptr %[[ARRDECAY1]])
+// OGCG:   %[[ARRDECAY2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
+// OGCG:   call void @llvm.va_end.p0(ptr %[[ARRDECAY2]])
+// OGCG:   ret void

>From a67a819dbb6338e0353583492ec8b1eb30faeffa Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Fri, 15 Aug 2025 19:02:47 +0200
Subject: [PATCH 2/2] [CIR] Implement__builtin_va_arg

Part of #153286.
Depends on #153819.

This patch adds support for __builtin_va_arg by adding the cir.va.arg operator. 
Unlike the incubator it doesn't depend on any target specific lowering (yet) 
but maps to llvm.va_arg.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 45 ++++++++++
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp       | 10 +++
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    | 13 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        | 11 +++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 17 ++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h   | 10 +++
 clang/test/CIR/CodeGen/var_arg.c              | 83 ++++++++++++++-----
 7 files changed, 168 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 86c8b59bf36f0..34d716a6b963c 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3492,4 +3492,49 @@ def CIR_VAEndOp : CIR_Op<"va.end"> {
   }];
 }
 
+def CIR_VAArgOp : CIR_Op<"va.arg"> {
+  let summary = "Fetches next variadic element as a given type";
+  let description = [{
+    The `cir.va.arg` operation models the C/C++ `va_arg` macro by reading the
+    next argument from an active variable argument list and producing it as a
+    value of a specified result type.
+
+    The operand must be a pointer to the target's `va_list` representation.
+    The operation advances the `va_list` state as a side effect and returns
+    the fetched value as the result, whose type is chosen by the user of the
+    operation.
+
+    A `cir.va.arg` must only be used on a `va_list` that has been initialized
+    with `cir.va.start` and not yet finalized by `cir.va.end`. The semantics
+    (including alignment and promotion rules) follow the platform ABI; the
+    frontend is responsible for providing a `va_list` pointer that matches the
+    target representation.
+
+    Unless replaced by LoweringPrepare for the chosen target ABI this maps to
+    LLVM's `va_arg` instruction, yielding a value of the requested result type
+    and updating the underlying `va_list` pointer.
+
+    Example:
+    ```mlir
+    // %args : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>
+    %p = cir.cast(array_to_ptrdecay, %args
+            : !cir.ptr<!cir.array<!rec___va_list_tag x 1>>),
+          !cir.ptr<!rec___va_list_tag>
+    cir.va.start %p : !cir.ptr<!rec___va_list_tag>
+
+    // Fetch an `int` from the vararg list.
+    %v = cir.va.arg %p : (!cir.ptr<!rec___va_list_tag>) -> !s32i
+
+    cir.va.end %p : !cir.ptr<!rec___va_list_tag>
+    ```
+  }];
+
+  let arguments = (ins CIR_PointerType:$arg_list);
+  let results = (outs CIR_AnyType:$result);
+
+  let assemblyFormat = [{
+    $arg_list attr-dict `:` functional-type(operands, $result)
+  }];
+}
+
 #endif // CLANG_CIR_DIALECT_IR_CIROPS_TD
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 25ca10e801194..391e2e12947ce 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -382,3 +382,13 @@ void CIRGenFunction::emitVAStartEnd(mlir::Value argValue, 
bool isStart) {
   else
     cir::VAEndOp::create(builder, argValue.getLoc(), argValue);
 }
+
+// FIXME(cir): This completely abstracts away the ABI with a generic CIR Op. We
+// need to decide how to handle va_arg target-specific codegen.
+mlir::Value CIRGenFunction::emitVAArg(VAArgExpr *ve, Address &vaListAddr) {
+  assert(!cir::MissingFeatures::msabi());
+  mlir::Location loc = cgm.getLoc(ve->getExprLoc());
+  mlir::Type type = convertType(ve->getType());
+  mlir::Value vaList = emitVAListRef(ve->getSubExpr()).getPointer();
+  return cir::VAArgOp::create(builder, loc, type, vaList);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 8649bab91ce8e..7fb5c020dd5f7 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -384,6 +384,19 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
     return Visit(e->getReplacement());
   }
 
+  mlir::Value VisitVAArgExpr(VAArgExpr *ve) {
+    QualType Ty = ve->getType();
+
+    if (Ty->isVariablyModifiedType()) {
+      cgf.cgm.errorNYI(ve->getSourceRange(), "variably modified types in 
varargs");
+    }
+
+    Address argValue = Address::invalid();
+    mlir::Value val = cgf.emitVAArg(ve, argValue);
+
+    return val;
+  }
+
   mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e);
   mlir::Value
   VisitAbstractConditionalOperator(const AbstractConditionalOperator *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 1c89c2c9d57d3..5bd59e6a84813 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1424,6 +1424,17 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// \param isStart If \c true, emits \c cir.va.start, otherwise \c 
cir.va.end.
   void emitVAStartEnd(mlir::Value argValue, bool isStart);
 
+  /// Generate code to get an argument from the passed in pointer
+  /// and update it accordingly.
+  ///
+  /// \param ve The \c VAArgExpr for which to generate code.
+  ///
+  /// \param vaListAddr Receives a reference to the \c va_list as emitted by
+  /// either \c emitVAListRef or \c emitMSVAListRef.
+  ///
+  /// \returns SSA value with the argument.
+  mlir::Value emitVAArg(VAArgExpr *ve, Address &vaListAddr);
+
   /// ----------------------
   /// CIR build helpers
   /// -----------------
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e51fbc9eadaf0..45fb30128c50e 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2336,6 +2336,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMTrapOpLowering,
                CIRToLLVMUnaryOpLowering,
                CIRToLLVMUnreachableOpLowering,
+               CIRToLLVMVAArgOpLowering,
                CIRToLLVMVAEndOpLowering,
                CIRToLLVMVAStartOpLowering,
                CIRToLLVMVecCmpOpLowering,
@@ -3057,6 +3058,22 @@ mlir::LogicalResult 
CIRToLLVMVAEndOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
+    cir::VAArgOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
+  auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
+                                              adaptor.getArgList());
+
+  mlir::Type llvmType =
+      getTypeConverter()->convertType(op->getResultTypes().front());
+  if (!llvmType)
+    return mlir::failure();
+
+  rewriter.replaceOpWithNewOp<mlir::LLVM::VaArgOp>(op, llvmType, vaList);
+  return mlir::success();
+}
+
 std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
   return std::make_unique<ConvertCIRToLLVMPass>();
 }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 72315ac127e1b..5263ff9910cbe 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -704,6 +704,16 @@ class CIRToLLVMVAEndOpLowering
                   mlir::ConversionPatternRewriter &) const override;
 };
 
+class CIRToLLVMVAArgOpLowering
+    : public mlir::OpConversionPattern<cir::VAArgOp> {
+public:
+  using mlir::OpConversionPattern<cir::VAArgOp>::OpConversionPattern;
+
+  mlir::LogicalResult
+  matchAndRewrite(cir::VAArgOp op, OpAdaptor,
+                  mlir::ConversionPatternRewriter &) const override;
+};
+
 } // namespace direct
 } // namespace cir
 
diff --git a/clang/test/CIR/CodeGen/var_arg.c b/clang/test/CIR/CodeGen/var_arg.c
index 7cd10e1ca42d2..647e8f15fa27f 100644
--- a/clang/test/CIR/CodeGen/var_arg.c
+++ b/clang/test/CIR/CodeGen/var_arg.c
@@ -5,44 +5,85 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-emit-llvm %s -o %t.ll
 // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
 
-void varargs(int count, ...) {
+int varargs(int count, ...) {
     __builtin_va_list args;
-    __builtin_va_start(args, 12345);
+    __builtin_va_start(args, count);
+    int res = __builtin_va_arg(args, int);
     __builtin_va_end(args);
+    return res;
 }
 
 // CIR: !rec___va_list_tag = !cir.record<struct "__va_list_tag" {!u32i, !u32i, 
!cir.ptr<!void>, !cir.ptr<!void>}
 
-// CIR: cir.func dso_local @varargs(%[[COUNT_ARG:.+]]: !s32i {{.*}}, ...) 
{{.*}}
-// CIR:   %[[COUNT:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["count", init]
-// CIR:   %[[ARGS:.+]]  = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
-// CIR:   cir.store %[[COUNT_ARG]], %[[COUNT]] : !s32i, !cir.ptr<!s32i>
+// CIR: cir.func dso_local @varargs(%[[COUNT:.+]]: !s32i {{.*}}, ...) -> !s32i
+// CIR:   %[[COUNT_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["count", 
init]
+// CIR:   %[[RETVAL:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[ARGS:.+]] = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
+// CIR:   %[[RES:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["res", init]
+// CIR:   cir.store %[[COUNT]], %[[COUNT_ADDR]] : !s32i, !cir.ptr<!s32i>
 // CIR:   %[[ARGS_DECAY1:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
 // CIR:   cir.va.start %[[ARGS_DECAY1]] : !cir.ptr<!rec___va_list_tag>
 // CIR:   %[[ARGS_DECAY2:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   cir.va.end %[[ARGS_DECAY2]] : !cir.ptr<!rec___va_list_tag>
-// CIR:   cir.return
+// CIR:   %[[ARGVAL:.+]] = cir.va.arg %[[ARGS_DECAY2]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
+// CIR:   cir.store {{.*}} %[[ARGVAL]], %[[RES]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[ARGS_DECAY3:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.va.end %[[ARGS_DECAY3]] : !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[RES_VAL:.+]] = cir.load {{.*}} %[[RES]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.store %[[RES_VAL]], %[[RETVAL]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[RET:.+]] = cir.load %[[RETVAL]] : !cir.ptr<!s32i>, !s32i
+// CIR:   cir.return %[[RET]] : !s32i
 
 // LLVM: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
 
-// LLVM: define dso_local void @varargs(i32 %[[ARG0:.+]], ...)
+// LLVM: define dso_local i32 @varargs(i32 %[[ARG0:.+]], ...)
 // LLVM:   %[[COUNT_ADDR:.+]] = alloca i32, i64 1
-// LLVM:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], i64 1
+// LLVM:   %[[RET_SLOT:.+]] = alloca i32, i64 1
+// LLVM:   %[[VASTORAGE:.+]] = alloca [1 x %struct.__va_list_tag], i64 1
+// LLVM:   %[[RES:.+]] = alloca i32, i64 1
 // LLVM:   store i32 %[[ARG0]], ptr %[[COUNT_ADDR]]
-// LLVM:   %[[GEP1:.+]] = getelementptr %struct.__va_list_tag, ptr %[[ARGS]], 
i32 0
-// LLVM:   call void @llvm.va_start.p0(ptr %[[GEP1]])
-// LLVM:   %[[GEP2:.+]] = getelementptr %struct.__va_list_tag, ptr %[[ARGS]], 
i32 0
-// LLVM:   call void @llvm.va_end.p0(ptr %[[GEP2]])
-// LLVM:   ret void
+// LLVM:   %[[G1:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VASTORAGE]], i32 0
+// LLVM:   call void @llvm.va_start.p0(ptr %[[G1]])
+// LLVM:   %[[G2:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VASTORAGE]], i32 0
+// LLVM:   %[[NEXT:.+]] = va_arg ptr %[[G2]], i32
+// LLVM:   store i32 %[[NEXT]], ptr %[[RES]]
+// LLVM:   %[[G3:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VASTORAGE]], i32 0
+// LLVM:   call void @llvm.va_end.p0(ptr %[[G3]])
+// LLVM:   %[[RVAL:.+]] = load i32, ptr %[[RES]]
+// LLVM:   store i32 %[[RVAL]], ptr %[[RET_SLOT]]
+// LLVM:   %[[RET:.+]] = load i32, ptr %[[RET_SLOT]]
+// LLVM:   ret i32 %[[RET]]
 
 // OGCG: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
 
-// OGCG: define dso_local void @varargs(i32 noundef %[[COUNT:.+]], ...)
+// OGCG: define dso_local i32 @varargs(i32 noundef %[[COUNT:.+]], ...)
 // OGCG:   %[[COUNT_ADDR:.+]] = alloca i32
 // OGCG:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag]
+// OGCG:   %[[RES:.+]] = alloca i32
 // OGCG:   store i32 %[[COUNT]], ptr %[[COUNT_ADDR]]
-// OGCG:   %[[ARRDECAY1:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_start.p0(ptr %[[ARRDECAY1]])
-// OGCG:   %[[ARRDECAY2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_end.p0(ptr %[[ARRDECAY2]])
-// OGCG:   ret void
+// OGCG:   %[[DEC1:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], 
ptr %[[ARGS]], i64 0, i64 0
+// OGCG:   call void @llvm.va_start.p0(ptr %[[DEC1]])
+// OGCG:   %[[DEC2:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], 
ptr %[[ARGS]], i64 0, i64 0
+// OGCG:   {{.*}} = getelementptr inbounds nuw %struct.__va_list_tag, ptr 
%[[DEC2]], i32 0, i32 0
+// OGCG:   {{.*}} = load i32, ptr {{.*}}
+// OGCG:   br i1 {{.*}}, label %[[INREG:.+]], label %[[INMEM:.+]]
+// OGCG: [[INREG]]:
+// OGCG:   {{.*}} = getelementptr inbounds nuw %struct.__va_list_tag, ptr 
%[[DEC2]], i32 0, i32 3
+// OGCG:   {{.*}} = load ptr, ptr {{.*}}
+// OGCG:   {{.*}} = getelementptr i8, ptr {{.*}}, i32 {{.*}}
+// OGCG:   {{.*}} = add i32 {{.*}}, 8
+// OGCG:   store i32 {{.*}}, ptr {{.*}}
+// OGCG:   br label %[[END:.+]]
+// OGCG: [[INMEM]]:
+// OGCG:   {{.*}} = getelementptr inbounds nuw %struct.__va_list_tag, ptr 
%[[DEC2]], i32 0, i32 2
+// OGCG:   {{.*}} = load ptr, ptr {{.*}}
+// OGCG:   {{.*}} = getelementptr i8, ptr {{.*}}, i32 8
+// OGCG:   store ptr {{.*}}, ptr {{.*}}
+// OGCG:   br label %[[END]]
+// OGCG: [[END]]:
+// OGCG:   %[[ARGPTR:.+]] = phi ptr [ {{.*}}, %[[INREG]] ], [ {{.*}}, 
%[[INMEM]] ]
+// OGCG:   %[[V:.+]] = load i32, ptr %[[ARGPTR]]
+// OGCG:   store i32 %[[V]], ptr %[[RES]]
+// OGCG:   %[[DEC3:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], 
ptr %[[ARGS]], i64 0, i64 0
+// OGCG:   call void @llvm.va_end.p0(ptr %[[DEC3]])
+// OGCG:   %[[RET:.+]] = load i32, ptr %[[RES]]
+// OGCG:   ret i32 %[[RET]]

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to