https://github.com/folkertdev created 
https://github.com/llvm/llvm-project/pull/214993

None

>From ff88a95888af1ca521fbf8cb8ef60da1f4808047 Mon Sep 17 00:00:00 2001
From: Folkert de Vries <[email protected]>
Date: Sat, 8 Aug 2026 19:22:24 +0200
Subject: [PATCH] [Sparc][NFC] add sparc64 `va_arg` tests

---
 clang/test/CodeGen/Sparc/sparcv9-vaarg.c | 245 +++++++++++++++++++++++
 1 file changed, 245 insertions(+)
 create mode 100644 clang/test/CodeGen/Sparc/sparcv9-vaarg.c

diff --git a/clang/test/CodeGen/Sparc/sparcv9-vaarg.c 
b/clang/test/CodeGen/Sparc/sparcv9-vaarg.c
new file mode 100644
index 0000000000000..a23d9f1c6506f
--- /dev/null
+++ b/clang/test/CodeGen/Sparc/sparcv9-vaarg.c
@@ -0,0 +1,245 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple sparc64 -emit-llvm -o - %s | FileCheck %s
+#include <stdarg.h>
+
+// CHECK-LABEL: define dso_local signext i32 @get_int(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], 
i64 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+int get_int(va_list *args) {
+  return va_arg(*args, int);
+}
+
+enum RGB { R = 1, G = 2, B = 3 };
+
+// Enums are passed like integers.
+// CHECK-LABEL: define dso_local zeroext i32 @get_enum(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], 
i64 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 4
+// CHECK-NEXT:    ret i32 [[TMP2]]
+//
+enum RGB get_enum(va_list *args) {
+  return va_arg(*args, enum RGB);
+}
+
+// long long (i64) is passed directly, note how ARGP_CUR is advanced by 8.
+// CHECK-LABEL: define dso_local i64 @get_long_long(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr [[ARGP_CUR]], align 8
+// CHECK-NEXT:    ret i64 [[TMP1]]
+//
+long long get_long_long(va_list *args) {
+  return va_arg(*args, long long);
+}
+
+struct Tiny {
+  char x;
+};
+
+// Structs up to 16 bytes are passed directly. Note how a small struct is 
left-aligned within the slot,
+// hence the shl by 56.
+// CHECK-LABEL: define dso_local i64 @get_struct_char(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_TINY:%.*]], align 1
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[RETVAL]], ptr 
align 8 [[ARGP_CUR]], i64 1, i1 false)
+// CHECK-NEXT:    [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw 
[[STRUCT_TINY]], ptr [[RETVAL]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load i8, ptr [[COERCE_DIVE]], align 1
+// CHECK-NEXT:    [[COERCE_VAL_II:%.*]] = zext i8 [[TMP1]] to i64
+// CHECK-NEXT:    [[COERCE_HIGHBITS:%.*]] = shl i64 [[COERCE_VAL_II]], 56
+// CHECK-NEXT:    ret i64 [[COERCE_HIGHBITS]]
+//
+struct Tiny get_struct_char(va_list *args) {
+ return va_arg(*args, struct Tiny);
+}
+
+struct Large {
+  long long x;
+  long long y;
+  long long z;
+};
+
+// Larger aggregates are passed indirectly, note how ARGP_CUR is advanced by 8.
+// CHECK-LABEL: define dso_local %struct.Large @get_struct_long_long(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_LARGE:%.*]], align 8
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 8
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr 
align 8 [[TMP1]], i64 24, i1 false)
+// CHECK-NEXT:    [[TMP2:%.*]] = load [[STRUCT_LARGE]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    ret [[STRUCT_LARGE]] [[TMP2]]
+//
+struct Large get_struct_long_long(va_list *args) {
+ return va_arg(*args, struct Large);
+}
+
+// long double is passed directly, and aligned to 16.
+// CHECK-LABEL: define dso_local fp128 @get_long_double(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], 
i32 15
+// CHECK-NEXT:    [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr 
[[TMP1]], i64 -16)
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR_ALIGNED]], i64 16
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load fp128, ptr [[ARGP_CUR_ALIGNED]], align 16
+// CHECK-NEXT:    ret fp128 [[TMP2]]
+//
+long double get_long_double(va_list *args) {
+  return va_arg(*args, long double);
+}
+
+// __int128 is passed directly, and aligned to 16.
+// CHECK-LABEL: define dso_local i128 @get_int128(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], 
i32 15
+// CHECK-NEXT:    [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr 
[[TMP1]], i64 -16)
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR_ALIGNED]], i64 16
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load i128, ptr [[ARGP_CUR_ALIGNED]], align 16
+// CHECK-NEXT:    ret i128 [[TMP2]]
+//
+__int128 get_int128(va_list *args) {
+  return va_arg(*args, __int128);
+}
+
+_Complex char complex_char_sink;
+
+// FIXME: _Complex char should be passed in the right-most bytes of the slot, 
using a getelementptr with a value of 6.
+// CHECK-LABEL: define dso_local void @get_complex_char(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 
}, ptr [[ARGP_CUR]], i32 0, i32 0
+// CHECK-NEXT:    [[ARGP_CUR_REAL:%.*]] = load i8, ptr [[ARGP_CUR_REALP]], 
align 8
+// CHECK-NEXT:    [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 
}, ptr [[ARGP_CUR]], i32 0, i32 1
+// CHECK-NEXT:    [[ARGP_CUR_IMAG:%.*]] = load i8, ptr [[ARGP_CUR_IMAGP]], 
align 1
+// CHECK-NEXT:    store i8 [[ARGP_CUR_REAL]], ptr @complex_char_sink, align 1
+// CHECK-NEXT:    store i8 [[ARGP_CUR_IMAG]], ptr getelementptr inbounds nuw 
(i8, ptr @complex_char_sink, i64 1), align 1
+// CHECK-NEXT:    ret void
+//
+void get_complex_char(va_list *args) {
+  complex_char_sink = va_arg(*args, _Complex char);
+}
+
+_Complex int complex_int_sink;
+
+// _Complex int is passed directly.
+// CHECK-LABEL: define dso_local void @get_complex_int(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i32, 
i32 }, ptr [[ARGP_CUR]], i32 0, i32 0
+// CHECK-NEXT:    [[ARGP_CUR_REAL:%.*]] = load i32, ptr [[ARGP_CUR_REALP]], 
align 8
+// CHECK-NEXT:    [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i32, 
i32 }, ptr [[ARGP_CUR]], i32 0, i32 1
+// CHECK-NEXT:    [[ARGP_CUR_IMAG:%.*]] = load i32, ptr [[ARGP_CUR_IMAGP]], 
align 4
+// CHECK-NEXT:    store i32 [[ARGP_CUR_REAL]], ptr @complex_int_sink, align 4
+// CHECK-NEXT:    store i32 [[ARGP_CUR_IMAG]], ptr getelementptr inbounds nuw 
(i8, ptr @complex_int_sink, i64 4), align 4
+// CHECK-NEXT:    ret void
+//
+void get_complex_int(va_list *args) {
+  complex_int_sink = va_arg(*args, _Complex int);
+}
+
+_Complex long long complex_long_long_sink;
+
+// _Complex long long is passed directly, note how ARGP_CUR is advanced by 16.
+// CHECK-LABEL: define dso_local void @get_complex_long_long(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 16
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i64, 
i64 }, ptr [[ARGP_CUR]], i32 0, i32 0
+// CHECK-NEXT:    [[ARGP_CUR_REAL:%.*]] = load i64, ptr [[ARGP_CUR_REALP]], 
align 8
+// CHECK-NEXT:    [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i64, 
i64 }, ptr [[ARGP_CUR]], i32 0, i32 1
+// CHECK-NEXT:    [[ARGP_CUR_IMAG:%.*]] = load i64, ptr [[ARGP_CUR_IMAGP]], 
align 8
+// CHECK-NEXT:    store i64 [[ARGP_CUR_REAL]], ptr @complex_long_long_sink, 
align 8
+// CHECK-NEXT:    store i64 [[ARGP_CUR_IMAG]], ptr getelementptr inbounds nuw 
(i8, ptr @complex_long_long_sink, i64 8), align 8
+// CHECK-NEXT:    ret void
+//
+void get_complex_long_long (va_list *args) {
+  complex_long_long_sink = va_arg(*args, _Complex long long );
+}
+
+_Complex long double complex_long_double_sink;
+
+// _Complex long double is passed indirectly, note how ARGP_CUR is advanced by 
8.
+// CHECK-LABEL: define dso_local void @get_complex_long_double(
+// CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[ARGS_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 8
+// CHECK-NEXT:    [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr 
[[ARGP_CUR]], i64 8
+// CHECK-NEXT:    store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 8
+// CHECK-NEXT:    [[DOTREALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 
}, ptr [[TMP1]], i32 0, i32 0
+// CHECK-NEXT:    [[DOTREAL:%.*]] = load fp128, ptr [[DOTREALP]], align 16
+// CHECK-NEXT:    [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 
}, ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[DOTIMAG:%.*]] = load fp128, ptr [[DOTIMAGP]], align 16
+// CHECK-NEXT:    store fp128 [[DOTREAL]], ptr @complex_long_double_sink, 
align 16
+// CHECK-NEXT:    store fp128 [[DOTIMAG]], ptr getelementptr inbounds nuw (i8, 
ptr @complex_long_double_sink, i64 16), align 16
+// CHECK-NEXT:    ret void
+//
+void get_complex_long_double(va_list *args) {
+  complex_long_double_sink = va_arg(*args, _Complex long double);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to