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

>From d1245a1d3c0ac2d904049b3a6a9937f75da49eea Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Tue, 19 Aug 2025 12:23:12 +0200
Subject: [PATCH 1/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              | 190 +++++++++++++-----
 7 files changed, 246 insertions(+), 50 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 369bcb1ddb1bb..39533b0b604be 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3696,4 +3696,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 1635756143f55..a179653a882da 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -400,3 +400,13 @@ void CIRGenFunction::emitVAStart(mlir::Value vaList, 
mlir::Value count) {
 void CIRGenFunction::emitVAEnd(mlir::Value vaList) {
   cir::VAEndOp::create(builder, vaList.getLoc(), vaList);
 }
+
+// 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 a23bdfc02ad2d..b93849b490992 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -399,6 +399,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 6e3f5b1f4afac..c47b040f7020c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1465,6 +1465,17 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// \c emitVAListRef or \c emitMSVAListRef.
   void emitVAEnd(mlir::Value vaList);
 
+  /// 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 9ab7178e9ab12..511a17fc5f685 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2378,6 +2378,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMTrapOpLowering,
                CIRToLLVMUnaryOpLowering,
                CIRToLLVMUnreachableOpLowering,
+               CIRToLLVMVAArgOpLowering,
                CIRToLLVMVAEndOpLowering,
                CIRToLLVMVAStartOpLowering,
                CIRToLLVMVecCmpOpLowering,
@@ -3126,6 +3127,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 7356e93bf700c..7b109c5cef9d3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -745,6 +745,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 e79d87f542d9e..30a32054228c5 100644
--- a/clang/test/CIR/CodeGen/var_arg.c
+++ b/clang/test/CIR/CodeGen/var_arg.c
@@ -9,68 +9,158 @@
 // LLVM: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
 // OGCG: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
 
-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: cir.func dso_local @varargs(%[[COUNT:.+]]: !s32i{{.*}}, ...)
+// CIR-LABEL: cir.func dso_local @varargs(
 // CIR:   %[[COUNT_ADDR:.+]] = 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]], %[[COUNT_ADDR]] : !s32i, !cir.ptr<!s32i>
-// CIR:   %[[APTR:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   %[[C12345:.+]] = cir.const #cir.int<12345> : !s32i
-// CIR:   cir.va_start %[[APTR]] %[[C12345]] : !cir.ptr<!rec___va_list_tag>, 
!s32i
-// CIR:   %[[APTR2:.+]] = 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 %[[APTR2]] : !cir.ptr<!rec___va_list_tag>
-// CIR:   cir.return
+// CIR:   %[[RET_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[VAAREA:.+]] = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
+// CIR:   %[[RES_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["res", init]
+// CIR:   cir.store %arg0, %[[COUNT_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[VA_PTR0:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[COUNT_VAL:.+]] = cir.load{{.*}} %[[COUNT_ADDR]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:   cir.va_start %[[VA_PTR0]] %[[COUNT_VAL]] : 
!cir.ptr<!rec___va_list_tag>, !s32i
+// CIR:   %[[VA_PTR1:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[VA_ARG:.+]] = cir.va.arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
+// CIR:   cir.store{{.*}} %[[VA_ARG]], %[[RES_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[VA_PTR2:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.va_end %[[VA_PTR2]] : !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[RESULT:.+]] = cir.load{{.*}} %[[RES_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   cir.store %[[RESULT]], %[[RET_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[RETVAL:.+]] = cir.load{{.*}} %[[RET_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   cir.return %[[RETVAL]] : !s32i
 
-// LLVM: define dso_local void @varargs(
-// LLVM:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], i64 1, align 16
-// LLVM:   %[[ARGS_PTR:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[ARGS]], i32 0
-// LLVM:   call void @llvm.va_start.p0(ptr %[[ARGS_PTR]])
-// LLVM:   %[[ARGS_PTR2:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[ARGS]], i32 0
-// LLVM:   call void @llvm.va_end.p0(ptr %[[ARGS_PTR2]])
-// LLVM:   ret void
+// LLVM-LABEL: define dso_local i32 @varargs(
+// LLVM:   %[[COUNT_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[RET_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]{{.*}}
+// LLVM:   %[[RES_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[VA_PTR0:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   call void @llvm.va_start.p0(ptr %[[VA_PTR0]])
+// LLVM:   %[[VA_PTR1:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   %[[VA_ARG:.+]] = va_arg ptr %[[VA_PTR1]], i32
+// LLVM:   store i32 %[[VA_ARG]], ptr %[[RES_ADDR]], {{.*}}
+// LLVM:   %[[VA_PTR2:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   call void @llvm.va_end.p0(ptr %[[VA_PTR2]])
+// LLVM:   %[[TMP_LOAD:.+]] = load i32, ptr %[[RES_ADDR]], {{.*}}
+// LLVM:   store i32 %[[TMP_LOAD]], ptr %[[RET_ADDR]], {{.*}}
+// LLVM:   %[[RETVAL:.+]] = load i32, ptr %[[RET_ADDR]], {{.*}}
+// LLVM:   ret i32 %[[RETVAL]]
 
-// OGCG: define dso_local void @varargs(
-// OGCG:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], align 16
-// OGCG:   %[[ARGS_PTR:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_start.p0(ptr %[[ARGS_PTR]])
-// OGCG:   %[[ARGS_PTR2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_end.p0(ptr %[[ARGS_PTR2]])
-// OGCG:   ret void
+// OGCG-LABEL: define dso_local i32 @varargs
+// OGCG:   %[[COUNT_ADDR:.+]] = alloca i32
+// OGCG:   %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]
+// OGCG:   %[[RES_ADDR:.+]] = alloca i32
+// OGCG:   %[[DECAY:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], 
ptr %[[VAAREA]]
+// OGCG:   call void @llvm.va_start.p0(ptr %[[DECAY]])
+// OGCG:   %[[DECAY1:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[VAAREA]]
+// OGCG:   %[[GPOFFSET_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 0
+// OGCG:   %[[GPOFFSET:.+]] = load i32, ptr %[[GPOFFSET_PTR]]
+// OGCG:   %[[COND:.+]] = icmp ule i32 %[[GPOFFSET]], 40
+// OGCG:   br i1 %[[COND]], label %vaarg.in_reg, label %vaarg.in_mem
+//
+// OGCG: vaarg.in_reg:
+// OGCG:   %[[REGSAVE_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 3
+// OGCG:   %[[REGSAVE:.+]] = load ptr, ptr %[[REGSAVE_PTR]]
+// OGCG:   %[[VAADDR1:.+]] = getelementptr i8, ptr %[[REGSAVE]], i32 
%[[GPOFFSET]]
+// OGCG:   br label %vaarg.end
+//
+// OGCG: vaarg.in_mem:
+// OGCG:   %[[OVERFLOW_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 2
+// OGCG:   %[[OVERFLOW:.+]] = load ptr, ptr %[[OVERFLOW_PTR]]
+// OGCG:   br label %vaarg.end
+//
+// OGCG: vaarg.end:
+// OGCG:   %[[PHI:.+]] = phi ptr [ %[[VAADDR1]], %vaarg.in_reg ], [ 
%[[OVERFLOW]], %vaarg.in_mem ]
+// OGCG:   %[[LOADED:.+]] = load i32, ptr %[[PHI]]
+// OGCG:   store i32 %[[LOADED]], ptr %[[RES_ADDR]]
+// OGCG:   %[[DECAY2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[VAAREA]]
+// OGCG:   call void @llvm.va_end.p0(ptr %[[DECAY2]])
+// OGCG:   %[[VAL:.+]] = load i32, ptr %[[RES_ADDR]]
+// OGCG:   ret i32 %[[VAL]]
 
-void stdarg_start(int count, ...) {
+int stdarg_start(int count, ...) {
     __builtin_va_list args;
     __builtin_stdarg_start(args, 12345);
+    int res = __builtin_va_arg(args, int);
     __builtin_va_end(args);
+    return res;
 }
 
-// CIR: cir.func dso_local @stdarg_start(%[[COUNT2:.+]]: !s32i{{.*}}, ...)
-// CIR:   %[[COUNT2_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["count", 
init]
-// CIR:   %[[ARGS2:.+]] = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
-// CIR:   cir.store %[[COUNT2]], %[[COUNT2_ADDR]] : !s32i, !cir.ptr<!s32i>
-// CIR:   %[[APTR3:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS2]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   %[[C12345_2:.+]] = cir.const #cir.int<12345> : !s32i
-// CIR:   cir.va_start %[[APTR3]] %[[C12345_2]] : 
!cir.ptr<!rec___va_list_tag>, !s32i
-// CIR:   %[[APTR4:.+]] = cir.cast(array_to_ptrdecay, %[[ARGS2]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   cir.va_end %[[APTR4]] : !cir.ptr<!rec___va_list_tag>
-// CIR:   cir.return
+// CIR-LABEL: cir.func dso_local @stdarg_start(
+// CIR:   %[[COUNT_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["count", 
init]
+// CIR:   %[[RET_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"]
+// CIR:   %[[VAAREA:.+]] = cir.alloca !cir.array<!rec___va_list_tag x 1>, 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["args"]
+// CIR:   %[[RES_ADDR:.+]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["res", init]
+// CIR:   cir.store %arg0, %[[COUNT_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[VA_PTR0:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[C12345:.+]] = cir.const #cir.int<12345> : !s32i
+// CIR:   cir.va_start %[[VA_PTR0]] %[[C12345]] : 
!cir.ptr<!rec___va_list_tag>, !s32i
+// CIR:   %[[VA_PTR1:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[VA_ARG:.+]] = cir.va.arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
+// CIR:   cir.store{{.*}} %[[VA_ARG]], %[[RES_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[VA_PTR2:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
+// CIR:   cir.va_end %[[VA_PTR2]] : !cir.ptr<!rec___va_list_tag>
+// CIR:   %[[RESULT:.+]] = cir.load{{.*}} %[[RES_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   cir.store %[[RESULT]], %[[RET_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR:   %[[RETVAL:.+]] = cir.load{{.*}} %[[RET_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   cir.return %[[RETVAL]] : !s32i
 
-// LLVM: define dso_local void @stdarg_start(
-// LLVM:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], i64 1, align 16
-// LLVM:   %[[ARGS_PTR:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[ARGS]], i32 0
-// LLVM:   call void @llvm.va_start.p0(ptr %[[ARGS_PTR]])
-// LLVM:   %[[ARGS_PTR2:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[ARGS]], i32 0
-// LLVM:   call void @llvm.va_end.p0(ptr %[[ARGS_PTR2]])
-// LLVM:   ret void
+// LLVM-LABEL: define dso_local i32 @stdarg_start(
+// LLVM:   %[[COUNT_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[RET_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]{{.*}}
+// LLVM:   %[[RES_ADDR:.+]] = alloca i32{{.*}}
+// LLVM:   %[[VA_PTR0:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   call void @llvm.va_start.p0(ptr %[[VA_PTR0]])
+// LLVM:   %[[VA_PTR1:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   %[[VA_ARG:.+]] = va_arg ptr %[[VA_PTR1]], i32
+// LLVM:   store i32 %[[VA_ARG]], ptr %[[RES_ADDR]], {{.*}}
+// LLVM:   %[[VA_PTR2:.+]] = getelementptr %struct.__va_list_tag, ptr 
%[[VAAREA]], i32 0
+// LLVM:   call void @llvm.va_end.p0(ptr %[[VA_PTR2]])
+// LLVM:   %[[TMP_LOAD:.+]] = load i32, ptr %[[RES_ADDR]], {{.*}}
+// LLVM:   store i32 %[[TMP_LOAD]], ptr %[[RET_ADDR]], {{.*}}
+// LLVM:   %[[RETVAL:.+]] = load i32, ptr %[[RET_ADDR]], {{.*}}
+// LLVM:   ret i32 %[[RETVAL]]
 
-// OGCG: define dso_local void @stdarg_start(
-// OGCG:   %[[ARGS:.+]] = alloca [1 x %struct.__va_list_tag], align 16
-// OGCG:   %[[ARGS_PTR:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_start.p0(ptr %[[ARGS_PTR]])
-// OGCG:   %[[ARGS_PTR2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[ARGS]], i64 0, i64 0
-// OGCG:   call void @llvm.va_end.p0(ptr %[[ARGS_PTR2]])
-// OGCG:   ret void
+// OGCG-LABEL: define dso_local i32 @stdarg_start
+// OGCG:   %[[COUNT_ADDR:.+]] = alloca i32
+// OGCG:   %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]
+// OGCG:   %[[RES_ADDR:.+]] = alloca i32
+// OGCG:   %[[DECAY:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], 
ptr %[[VAAREA]], i64 0, i64 0
+// OGCG:   call void @llvm.va_start.p0(ptr %[[DECAY]])
+// OGCG:   %[[DECAY1:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[VAAREA]], i64 0, i64 0
+// OGCG:   %[[GPOFFSET_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 0
+// OGCG:   %[[GPOFFSET:.+]] = load i32, ptr %[[GPOFFSET_PTR]]
+// OGCG:   %[[COND:.+]] = icmp ule i32 %[[GPOFFSET]], 40
+// OGCG:   br i1 %[[COND]], label %vaarg.in_reg, label %vaarg.in_mem
+//
+// OGCG: vaarg.in_reg:                                   
+// OGCG:   %[[REGSAVE_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 3
+// OGCG:   %[[REGSAVE:.+]] = load ptr, ptr %[[REGSAVE_PTR]]
+// OGCG:   %[[VAADDR1:.+]] = getelementptr i8, ptr %[[REGSAVE]], i32 
%[[GPOFFSET]]
+// OGCG:   %[[NEXT_GPOFFSET:.+]] = add i32 %[[GPOFFSET]], 8
+// OGCG:   store i32 %[[NEXT_GPOFFSET]], ptr %[[GPOFFSET_PTR]]
+// OGCG:   br label %vaarg.end
+//
+// OGCG: vaarg.in_mem:
+// OGCG:   %[[OVERFLOW_PTR:.+]] = getelementptr inbounds nuw 
%struct.__va_list_tag, ptr %[[DECAY1]], i32 0, i32 2
+// OGCG:   %[[OVERFLOW:.+]] = load ptr, ptr %[[OVERFLOW_PTR]]
+// OGCG:   %[[OVERFLOW_NEXT:.+]] = getelementptr i8, ptr %[[OVERFLOW]], i32 8
+// OGCG:   store ptr %[[OVERFLOW_NEXT]], ptr %[[OVERFLOW_PTR]]
+// OGCG:   br label %vaarg.end
+//
+// OGCG: vaarg.end:
+// OGCG:   %[[PHI:.+]] = phi ptr [ %[[VAADDR1]], %vaarg.in_reg ], [ 
%[[OVERFLOW]], %vaarg.in_mem ]
+// OGCG:   %[[LOADED:.+]] = load i32, ptr %[[PHI]]
+// OGCG:   store i32 %[[LOADED]], ptr %[[RES_ADDR]]
+// OGCG:   %[[DECAY2:.+]] = getelementptr inbounds [1 x 
%struct.__va_list_tag], ptr %[[VAAREA]], i64 0, i64 0
+// OGCG:   call void @llvm.va_end.p0(ptr %[[DECAY2]])
+// OGCG:   %[[VAL:.+]] = load i32, ptr %[[RES_ADDR]]
+// OGCG:   ret i32 %[[VAL]]

>From 73406b4605a85ac81dbcc27bc242d77572e85376 Mon Sep 17 00:00:00 2001
From: Morris Hafner <mhaf...@nvidia.com>
Date: Tue, 19 Aug 2025 13:02:19 +0200
Subject: [PATCH 2/2] Address review feedback

---
 clang/include/clang/CIR/Dialect/IR/CIROps.td        | 12 ++++--------
 clang/include/clang/CIR/MissingFeatures.h           |  1 +
 clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp             |  8 +++++---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp          | 12 +++++-------
 clang/lib/CIR/CodeGen/CIRGenFunction.h              |  2 +-
 clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  1 +
 clang/test/CIR/CodeGen/var_arg.c                    |  4 ++--
 7 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 39533b0b604be..0f7abe1748f5e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3696,10 +3696,10 @@ def CIR_VAEndOp : CIR_Op<"va_end"> {
   }];
 }
 
-def CIR_VAArgOp : CIR_Op<"va.arg"> {
+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
+    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.
 
@@ -3708,16 +3708,12 @@ def CIR_VAArgOp : CIR_Op<"va.arg"> {
     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
+    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>>
@@ -3727,7 +3723,7 @@ def CIR_VAArgOp : CIR_Op<"va.arg"> {
     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
+    %v = cir.va_arg %p : (!cir.ptr<!rec___va_list_tag>) -> !s32i
 
     cir.va.end %p : !cir.ptr<!rec___va_list_tag>
     ```
diff --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index 8626ed920b678..4c084d9a92cdd 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -279,6 +279,7 @@ struct MissingFeatures {
   static bool vtableInitialization() { return false; }
   static bool vtableRelativeLayout() { return false; }
   static bool msvcBuiltins() { return false; }
+  static bool vaArgABILowering() { return false; }
   static bool vlas() { return false; }
 
   // Missing types
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index a179653a882da..38c738ae2a62b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -401,10 +401,12 @@ void CIRGenFunction::emitVAEnd(mlir::Value vaList) {
   cir::VAEndOp::create(builder, vaList.getLoc(), vaList);
 }
 
-// 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) {
+// FIXME(cir): This completely abstracts away the ABI with a generic CIR Op. By
+// default this lowers to llvm.va_arg which is incomplete and not ABI-compliant
+// on most targets so cir.va_arg will need some ABI handling in LoweringPrepare
+mlir::Value CIRGenFunction::emitVAArg(VAArgExpr *ve) {
   assert(!cir::MissingFeatures::msabi());
+  assert(!cir::MissingFeatures::vlas());
   mlir::Location loc = cgm.getLoc(ve->getExprLoc());
   mlir::Type type = convertType(ve->getType());
   mlir::Value vaList = emitVAListRef(ve->getSubExpr()).getPointer();
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index b93849b490992..f6b2c88f2cfb4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -400,16 +400,14 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitVAArgExpr(VAArgExpr *ve) {
-    QualType Ty = ve->getType();
+    QualType ty = ve->getType();
 
-    if (Ty->isVariablyModifiedType()) {
-      cgf.cgm.errorNYI(ve->getSourceRange(), "variably modified types in 
varargs");
+    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;
+    return cgf.emitVAArg(ve);
   }
 
   mlir::Value VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *e);
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index c47b040f7020c..e03da287a3a8a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1474,7 +1474,7 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// either \c emitVAListRef or \c emitMSVAListRef.
   ///
   /// \returns SSA value with the argument.
-  mlir::Value emitVAArg(VAArgExpr *ve, Address &vaListAddr);
+  mlir::Value emitVAArg(VAArgExpr *ve);
 
   /// ----------------------
   /// CIR build helpers
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 511a17fc5f685..41ba019831b3a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3130,6 +3130,7 @@ mlir::LogicalResult 
CIRToLLVMVAEndOpLowering::matchAndRewrite(
 mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
     cir::VAArgOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
+  assert(!cir::MissingFeatures::vaArgABILowering());
   auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
   auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
                                               adaptor.getArgList());
diff --git a/clang/test/CIR/CodeGen/var_arg.c b/clang/test/CIR/CodeGen/var_arg.c
index 30a32054228c5..e9c4acb15d009 100644
--- a/clang/test/CIR/CodeGen/var_arg.c
+++ b/clang/test/CIR/CodeGen/var_arg.c
@@ -27,7 +27,7 @@ int varargs(int count, ...) {
 // CIR:   %[[COUNT_VAL:.+]] = cir.load{{.*}} %[[COUNT_ADDR]] : 
!cir.ptr<!s32i>, !s32i
 // CIR:   cir.va_start %[[VA_PTR0]] %[[COUNT_VAL]] : 
!cir.ptr<!rec___va_list_tag>, !s32i
 // CIR:   %[[VA_PTR1:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   %[[VA_ARG:.+]] = cir.va.arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
+// CIR:   %[[VA_ARG:.+]] = cir.va_arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
 // CIR:   cir.store{{.*}} %[[VA_ARG]], %[[RES_ADDR]] : !s32i, !cir.ptr<!s32i>
 // CIR:   %[[VA_PTR2:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
 // CIR:   cir.va_end %[[VA_PTR2]] : !cir.ptr<!rec___va_list_tag>
@@ -103,7 +103,7 @@ int stdarg_start(int count, ...) {
 // CIR:   %[[C12345:.+]] = cir.const #cir.int<12345> : !s32i
 // CIR:   cir.va_start %[[VA_PTR0]] %[[C12345]] : 
!cir.ptr<!rec___va_list_tag>, !s32i
 // CIR:   %[[VA_PTR1:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
-// CIR:   %[[VA_ARG:.+]] = cir.va.arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
+// CIR:   %[[VA_ARG:.+]] = cir.va_arg %[[VA_PTR1]] : 
(!cir.ptr<!rec___va_list_tag>) -> !s32i
 // CIR:   cir.store{{.*}} %[[VA_ARG]], %[[RES_ADDR]] : !s32i, !cir.ptr<!s32i>
 // CIR:   %[[VA_PTR2:.+]] = cir.cast(array_to_ptrdecay, %[[VAAREA]] : 
!cir.ptr<!cir.array<!rec___va_list_tag x 1>>), !cir.ptr<!rec___va_list_tag>
 // CIR:   cir.va_end %[[VA_PTR2]] : !cir.ptr<!rec___va_list_tag>

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

Reply via email to