diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 5f5fab6f12300..d27fcf27e143a 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4804,6 +4804,37 @@ def CIR_VAEndOp : CIR_Op<"va_end"> { }]; } +def CIR_VACopyOp : CIR_Op<"va.copy"> { + let summary = "Copied a variable argument list"; + let description = [{ + The `cir.copy` operation models the C/C++ va_copy macro. + The variable argument list passed as the `$src_list` is copied to an + unitialized `va_list` in the destination operand. The next argument that + can be extracted from the copied list is the same as the next argument in + the source list. The copied list must be destroyed with `va_end`. + + Example: + + ```mlir + // %args : !cir.ptr> + %p = cir.cast array_to_ptrdecay %args + : !cir.ptr> + -> !cir.ptr + cir.va.copy %p to %dst + : (!cir.ptr, !cir.ptr) + ``` + }]; + + let arguments = (ins + CIR_PointerType:$dst_list, + CIR_PointerType:$src_list + ); + + let assemblyFormat = [{ + $src_list `to` $dst_list attr-dict `:` type(operands) + }]; +} + def CIR_VAArgOp : CIR_Op<"va_arg"> { let summary = "Fetches next variadic element as a given type"; let description = [{ diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 7d4d13121d5e5..ec3875349a37e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -266,7 +266,12 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, case Builtin::BI__builtin_va_end: emitVAEnd(emitVAListRef(e->getArg(0)).getPointer()); return {}; - + case Builtin::BI__builtin_va_copy: { + mlir::Value dstPtr = emitVAListRef(e->getArg(0)).getPointer(); + mlir::Value srcPtr = emitVAListRef(e->getArg(1)).getPointer(); + cir::VACopyOp::create(builder, dstPtr.getLoc(), dstPtr, srcPtr); + return {}; + } case Builtin::BIcos: case Builtin::BIcosf: case Builtin::BIcosl: diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 0c34d87734c3e..811a1fe09a4eb 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -4052,6 +4052,18 @@ mlir::LogicalResult CIRToLLVMVAEndOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMVACopyOpLowering::matchAndRewrite( + cir::VACopyOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext()); + auto dstList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr, + adaptor.getDstList()); + auto srcList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr, + adaptor.getSrcList()); + rewriter.replaceOpWithNewOp(op, dstList, srcList); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite( cir::VAArgOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { diff --git a/clang/test/CIR/CodeGen/var_arg.c b/clang/test/CIR/CodeGen/var_arg.c index f5b92c61e11ad..6e59b9daba0f4 100644 --- a/clang/test/CIR/CodeGen/var_arg.c +++ b/clang/test/CIR/CodeGen/var_arg.c @@ -141,7 +141,7 @@ int stdarg_start(int count, ...) { // OGCG: %[[COND:.+]] = icmp ule i32 %[[GPOFFSET]], 40 // OGCG: br i1 %[[COND]], label %vaarg.in_reg, label %vaarg.in_mem // -// OGCG: vaarg.in_reg: +// 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]] @@ -164,3 +164,23 @@ int stdarg_start(int count, ...) { // OGCG: call void @llvm.va_end.p0(ptr %[[DECAY2]]) // OGCG: %[[VAL:.+]] = load i32, ptr %[[RES_ADDR]] // OGCG: ret i32 %[[VAL]] + +void stdarg_copy() { + __builtin_va_list src, dest; + __builtin_va_copy(src, dest); +} + +// CIR-LABEL: @stdarg_copy +// CIR: %{{.*}} = cir.cast array_to_ptrdecay %{{.*}} : !cir.ptr> -> !cir.ptr +// CIR: %{{.*}} = cir.cast array_to_ptrdecay %{{.*}} : !cir.ptr> -> !cir.ptr +// CIR: cir.va.copy %{{.*}} to %{{.*}} : !cir.ptr, !cir.ptr + +// LLVM-LABEL: @stdarg_copy +// LLVM: %{{.*}} = getelementptr %struct.__va_list_tag, ptr %{{.*}} +// LLVM: %{{.*}} = getelementptr %struct.__va_list_tag, ptr %{{.*}} +// LLVM: call void @llvm.va_copy.p0(ptr %{{.*}}, ptr %{{.*}} + +// OGCG-LABEL: @stdarg_copy +// OGCG: %{{.*}} = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %{{.*}} +// OGCG: %{{.*}} = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %{{.*}} +// OGCG: call void @llvm.va_copy.p0(ptr %{{.*}}, ptr %{{.*}}