Author: Morris Hafner
Date: 2025-08-19T09:16:11+02:00
New Revision: b44e47a68f9b49a6283b1beaab3af55fa39e8907

URL: 
https://github.com/llvm/llvm-project/commit/b44e47a68f9b49a6283b1beaab3af55fa39e8907
DIFF: 
https://github.com/llvm/llvm-project/commit/b44e47a68f9b49a6283b1beaab3af55fa39e8907.diff

LOG: [CIR] Upstream __builtin_va_start and __builtin_va_end (#153819)

Part of #153286

Added: 
    clang/test/CIR/CodeGen/var_arg.c

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
    clang/lib/CIR/CodeGen/CIRGenExpr.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
    clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 6264d23d52bd5..369bcb1ddb1bb 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3614,4 +3614,86 @@ 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 first 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. The second operand
+    must be an integer value that contains the expected number of arguments in
+    that list.
+
+    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 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>
+    %count = cir.load %0 : !cir.ptr<!s32i>, !s32i
+    cir.va_start %p %count : !cir.ptr<!rec___va_list_tag>, !s32i
+    ```
+  }];
+  let arguments = (ins
+    CIR_PointerType:$arg_list,
+    CIR_AnyFundamentalIntType:$count
+  );
+
+  let assemblyFormat = [{
+    $arg_list $count 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 dcd00696f335d..1635756143f55 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -125,6 +125,21 @@ 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: {
+    emitVAStart(builtinID == Builtin::BI__va_start
+                    ? emitScalarExpr(e->getArg(0))
+                    : emitVAListRef(e->getArg(0)).getPointer(),
+                emitScalarExpr(e->getArg(1)));
+    return {};
+  }
+
+  case Builtin::BI__builtin_va_end:
+    emitVAEnd(emitVAListRef(e->getArg(0)).getPointer());
+    return {};
+
   case Builtin::BIfabs:
   case Builtin::BIfabsf:
   case Builtin::BIfabsl:
@@ -375,3 +390,13 @@ mlir::Value CIRGenFunction::emitCheckedArgForAssume(const 
Expr *e) {
                "emitCheckedArgForAssume: sanitizers are NYI");
   return {};
 }
+
+void CIRGenFunction::emitVAStart(mlir::Value vaList, mlir::Value count) {
+  // LLVM codegen casts to *i8, no real gain on doing this for CIRGen this
+  // early, defer to LLVM lowering.
+  cir::VAStartOp::create(builder, vaList.getLoc(), vaList, count);
+}
+
+void CIRGenFunction::emitVAEnd(mlir::Value vaList) {
+  cir::VAEndOp::create(builder, vaList.getLoc(), vaList);
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 8bcca6f5d1803..ffbdcd4e1d8de 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(), baseInfo);
 
     case CK_UncheckedDerivedToBase:
     case CK_DerivedToBase: {
@@ -1626,7 +1623,9 @@ void CIRGenFunction::emitIgnoredExpr(const Expr *e) {
   emitLValue(e);
 }
 
-Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) {
+Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e,
+                                                LValueBaseInfo *baseInfo) {
+  assert(!cir::MissingFeatures::opTBAA());
   assert(e->getType()->isArrayType() &&
          "Array to pointer decay must have array source type!");
 

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 9d6d1939a5bbc..122989a26b4a8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -952,7 +952,8 @@ class CIRGenFunction : public CIRGenTypeCache {
                               QualType &baseType, Address &addr);
   LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e);
 
-  Address emitArrayToPointerDecay(const Expr *array);
+  Address emitArrayToPointerDecay(const Expr *e,
+                                  LValueBaseInfo *baseInfo = nullptr);
 
   mlir::LogicalResult emitAsmStmt(const clang::AsmStmt &s);
 
@@ -1420,6 +1421,24 @@ 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 the start of a CIR variable-argument operation (`cir.va_start`)
+  ///
+  /// \param vaList A reference to the \c va_list as emitted by either
+  /// \c emitVAListRef or \c emitMSVAListRef.
+  ///
+  /// \param count The number of arguments in \c vaList
+  void emitVAStart(mlir::Value vaList, mlir::Value count);
+
+  /// Emits the end of a CIR variable-argument operation (`cir.va_start`)
+  ///
+  /// \param vaList A reference to the \c va_list as emitted by either
+  /// \c emitVAListRef or \c emitMSVAListRef.
+  void emitVAEnd(mlir::Value vaList);
+
   /// ----------------------
   /// CIR build helpers
   /// -----------------

diff  --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 6f07f61a4d842..9ab7178e9ab12 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2378,6 +2378,8 @@ void ConvertCIRToLLVMPass::runOnOperation() {
                CIRToLLVMTrapOpLowering,
                CIRToLLVMUnaryOpLowering,
                CIRToLLVMUnreachableOpLowering,
+               CIRToLLVMVAEndOpLowering,
+               CIRToLLVMVAStartOpLowering,
                CIRToLLVMVecCmpOpLowering,
                CIRToLLVMVecCreateOpLowering,
                CIRToLLVMVecExtractOpLowering,
@@ -3104,6 +3106,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 a2890c88d1393..7356e93bf700c 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -725,6 +725,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..e79d87f542d9e
--- /dev/null
+++ b/clang/test/CIR/CodeGen/var_arg.c
@@ -0,0 +1,76 @@
+// 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
+
+// CIR: !rec___va_list_tag = !cir.record<struct "__va_list_tag" {!u32i, !u32i, 
!cir.ptr<!void>, !cir.ptr<!void>}
+// LLVM: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
+// OGCG: %struct.__va_list_tag = type { i32, i32, ptr, ptr }
+
+void varargs(int count, ...) {
+    __builtin_va_list args;
+    __builtin_va_start(args, 12345);
+    __builtin_va_end(args);
+}
+
+// CIR: cir.func dso_local @varargs(%[[COUNT:.+]]: !s32i{{.*}}, ...)
+// 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
+
+// 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
+
+// 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
+
+void stdarg_start(int count, ...) {
+    __builtin_va_list args;
+    __builtin_stdarg_start(args, 12345);
+    __builtin_va_end(args);
+}
+
+// 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
+
+// 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
+
+// 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


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

Reply via email to