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
