[clang] [mlir] [CIR] Lower variadic calls in CallConvLowering for x86_64 (PR #213315)

Adam Smith via cfe-commits cfe-commits at lists.llvm.org
Sun Aug 2 21:39:52 PDT 2026


================
@@ -0,0 +1,133 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -clangir-enable-call-conv-lowering -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -clangir-enable-call-conv-lowering -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-CIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVM-OGCG --input-file=%t.ll %s
+
+typedef struct { int x; int y; } Pair2;
+typedef struct { long a; long b; } Pair16;
+typedef struct { long a, b, c, d; } Big;
+typedef struct { __int128 w; } Wide;
+typedef struct { __int128 w; char c; } WideChar;
+
+int vf(Pair2 p, ...);
+
+// CIR: cir.func private @vf(!u64i, ...) -> !s32i
+
+int call_scalar(Pair2 p, int a, double d) { return vf(p, a, d); }
+
+// CIR-LABEL: cir.func {{.*}}@call_scalar(%arg0: !u64i loc({{.+}}), %arg1: !s32i {llvm.noundef} loc({{.+}}), %arg2: !cir.double {llvm.noundef} loc({{.+}})) -> !s32i
+// CIR: cir.call @vf(%{{.+}}, %{{.+}}, %{{.+}}) : (!u64i, !s32i {llvm.noundef}, !cir.double {llvm.noundef}) -> !s32i
+
+// LLVM-LABEL: i32 @call_scalar(i64 %{{.+}}, i32 noundef %{{.+}}, double noundef %{{.+}})
+// LLVM: call i32 (i64, ...) @vf(i64 %{{.+}}, i32 noundef %{{.+}}, double noundef %{{.+}})
+
+// A two-eightbyte record at the ellipsis is flattened into two INTEGER
+// registers while registers remain.
+int call_small(Pair2 p, Pair16 q) { return vf(p, q); }
+
+// CIR-LABEL: cir.func {{.*}}@call_small(%arg0: !u64i loc({{.+}}), %arg1: !s64i loc({{.+}}), %arg2: !s64i loc({{.+}})) -> !s32i
----------------
adams381 wrote:

I reworked the whole file rather than just this function, since every check had the same hole.  `call_small` now captures the two incoming registers at the label and asserts that the

1) first is stored to member 0 and the second to member 1 on entry
2) call operands are reloaded from members 0 and 1 in that order
3) named parameter stays in position one

`ell_bitint96` gets the same treatment for its register pair.  The byval cases pin the slot alignment, and on the OGCG side they also pin that the incoming pointer is forwarded rather than copied into a fresh slot.

The previous version would have accepted the two halves arriving at the call in either order, which is what prompted the rewrite.

https://github.com/llvm/llvm-project/pull/213315


More information about the cfe-commits mailing list