Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -4704,6 +4704,40 @@ 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
```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.copy %p to %dst
: (!cir.ptr<!rec___va_list_tag>, !cir.ptr<!rec___va_list_tag>)
```

```
}];

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 = [{
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4034,6 +4034,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<mlir::LLVM::VaCopyOp>(op, dstList, srcList);
return mlir::success();
}

mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
cir::VAArgOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
Expand Down
103 changes: 102 additions & 1 deletion clang/test/CIR/CodeGen/var_arg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand All @@ -164,3 +164,104 @@ 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]]

int stdarg_copy(int count, ...) {
__builtin_va_list args;
__builtin_stdarg_start(args, 12345);
__builtin_va_list dup;
__builtin_va_copy(args, dup);
int res = __builtin_va_arg(dup, int);
__builtin_va_end(args);
__builtin_va_end(dup);
return res;
}

// CIR-LABEL: cir.func dso_local @stdarg_copy(
// 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: %[[DUP_AREA:.+]] = cir.alloca !cir.array<!rec___va_list_tag x 1>, !cir.ptr<!cir.array<!rec___va_list_tag x 1>>, ["dup"]
// 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_PTR_SRC:.+]] = cir.cast array_to_ptrdecay %[[VAAREA]] : !cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: %[[VA_PTR_DEST:.+]] = cir.cast array_to_ptrdecay %[[DUP_AREA]] : !cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: cir.va.copy %[[VA_PTR_DEST]] to %[[VA_PTR_SRC]] : !cir.ptr<!rec___va_list_tag>, !cir.ptr<!rec___va_list_tag>
// CIR: %[[VA_PTR_COPY:.+]] = cir.cast array_to_ptrdecay %[[DUP_AREA]] : !cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: %[[VA_ARG:.+]] = cir.va_arg %[[VA_PTR_COPY]] : (!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: %[[VA_PTR3:.+]] = cir.cast array_to_ptrdecay %[[DUP_AREA]] : !cir.ptr<!cir.array<!rec___va_list_tag x 1>> -> !cir.ptr<!rec___va_list_tag>
// CIR: cir.va_end %[[VA_PTR3]] : !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-LABEL: define dso_local i32 @stdarg_copy(
// LLVM: %[[COUNT_ADDR:.+]] = alloca i32{{.*}}
// LLVM: %[[RET_ADDR:.+]] = alloca i32{{.*}}
// LLVM: %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]{{.*}}
// LLVM: %[[DUP_AREA:.+]] = 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_PTR_DUP:.+]] = getelementptr %struct.__va_list_tag, ptr %[[DUP_AREA]], i32 0
// LLVM: call void @llvm.va_copy.p0(ptr %[[VA_PTR1]], ptr %[[VA_PTR_DUP]])
// LLVM: %[[VA_PTR_DUP2:.+]] = getelementptr %struct.__va_list_tag, ptr %[[DUP_AREA]], i32 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we've got something missing here that has nothing to do with this PR. Can you use a test that does just the vaarg copy? Maybe something like this (in a different test file):

void test_vaarg_copy() {
  __builtin_va_list args, dup;
  __builtin_va_copy(args, dup);    
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah lets use a simpler test case.

// LLVM: %[[VA_ARG:.+]] = va_arg ptr %[[VA_PTR_DUP2]], 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: %[[VA_PTR3:.+]] = getelementptr %struct.__va_list_tag, ptr %[[DUP_AREA]], i32 0
// LLVM: call void @llvm.va_end.p0(ptr %[[VA_PTR3]])
// 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-LABEL: define dso_local i32 @stdarg_copy
// OGCG: %[[COUNT_ADDR:.+]] = alloca i32
// OGCG: %[[VAAREA:.+]] = alloca [1 x %struct.__va_list_tag]
// OGCG: %[[DUP_AREA:.+]] = 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: %[[DECAY_DUP:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %[[DUP_AREA]], i64 0, i64 0
// OGCG: call void @llvm.va_copy.p0(ptr %[[DECAY1]], ptr %[[DECAY_DUP]])
// OGCG: %[[DECAY_DUP2:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %[[DUP_AREA]], i64 0, i64 0
// OGCG: %[[GPOFFSET_PTR:.+]] = getelementptr inbounds nuw %struct.__va_list_tag, ptr %[[DECAY_DUP2]], 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 %[[DECAY_DUP2]], 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 %[[DECAY_DUP2]], 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: %[[DECAY_DUP3:.+]] = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %[[DUP_AREA]], i64 0, i64 0
// OGCG: call void @llvm.va_end.p0(ptr %[[DECAY_DUP3]])
// OGCG: %[[VAL:.+]] = load i32, ptr %[[RES_ADDR]]
// OGCG: ret i32 %[[VAL]]