-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[CIR] Upstream __builtin_va_start and __builtin_va_end #153819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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: { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add test cases that use |
||
| __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 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically? Are there cases where it doesn't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember some issues with va_arg (where OG might not lower to the LLVM intrinsic - llvm/clangir#862), I'm not sure about
va.startthough.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mind was at "What if we don't lower to LLVM?". I removed the "typically".
The LLVM intrinsic has a few problems because it's not a complete solution and sometimes generates worse code which is why classic codegen only uses it as a default in its ABI lowering. See here or here.
In #153834 I added lowering to
llvm.va_argas a default in LowerToLLVM. Once we get proper ABI lowering in LoweringPrepare (or at a similar stage) upstream a target can replacecir.va_argwith something else. But for now this is a good stopgap to enable things like scalar varargs on most platforms.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good to me