https://github.com/folkertdev created https://github.com/llvm/llvm-project/pull/216524
https://godbolt.org/z/o1nGYEM33 ```c extern void sink(int, ...); void variadic_cld(_Complex float z) { sink(0, z); } ``` Current clang just uses float registers ```asm variadic_cld: mov %g0, %o0 fmovs %f0, %f2 fmovs %f1, %f3 mov %o7, %g1 call sink mov %g1, %o7 ``` GCC packs a complex float into a single register ```asm variadic_cld: save %sp, -192, %sp st %f0, [%fp+2043] lduw [%fp+2043], %g1 sllx %g1, 32, %g1 srl %o1, 0, %o1 or %o1, %g1, %o1 st %f1, [%fp+2043] lduw [%fp+2043], %g2 mov -1, %g1 sllx %g1, 32, %g1 and %o1, %g1, %o1 or %o1, %g2, %o1 call sink, 0 mov 0, %o0 return %i7+8 nop ``` >From 8e0c52d5af73a6240b1ba7129d3df51991dff1e8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Sun, 16 Aug 2026 01:31:43 +0200 Subject: [PATCH 1/2] [Sparc][NFC] test sparc variadic aggregate handling --- clang/test/CodeGen/Sparc/variadic-aggregate.c | 1015 +++++++++++++++++ 1 file changed, 1015 insertions(+) create mode 100644 clang/test/CodeGen/Sparc/variadic-aggregate.c diff --git a/clang/test/CodeGen/Sparc/variadic-aggregate.c b/clang/test/CodeGen/Sparc/variadic-aggregate.c new file mode 100644 index 0000000000000..92093e1133b8a --- /dev/null +++ b/clang/test/CodeGen/Sparc/variadic-aggregate.c @@ -0,0 +1,1015 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -triple sparc -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=SPARC +// +// RUN: %clang_cc1 -triple sparcv9-unknown-unknown -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefixes=SPARC64 + +#include <stdarg.h> + +extern void sink(int, ...); + +struct char_char { + char x, y; +}; + +struct short_short { + short x, y; +}; + +struct int_int { + int x, y; +}; + +struct long_long { + long x, y; +}; + +struct long_long_long_long { + long long x, y; +}; + +struct float_float { + float x, y; +}; + +struct double_double { + double x, y; +}; + +struct long_double_long_double { + long double x, y; +}; + +struct aligned_int { + int x; +} __attribute__((aligned(16))); + +struct aligned_float { + float x; +} __attribute__((aligned(16))); + +struct int_int_int_int { + int a, b, c, d; +}; + +struct float_float_float_float { + float a, b, c, d; +}; + +// SPARC-LABEL: define dso_local void @test_complex_char( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0:[0-9]+]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { i8, i8 }, align 1 +// SPARC-NEXT: [[COERCE:%.*]] = alloca { i8, i8 }, align 1 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load i8, ptr [[DOTREALP]], align 1 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load i8, ptr [[DOTIMAGP]], align 1 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store i8 [[DOTREAL]], ptr [[X_REALP]], align 1 +// SPARC-NEXT: store i8 [[DOTIMAG]], ptr [[X_IMAGP]], align 1 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load i8, ptr [[X_REALP1]], align 1 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load i8, ptr [[X_IMAGP2]], align 1 +// SPARC-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC-NEXT: store i8 [[X_REAL]], ptr [[COERCE_REALP]], align 1 +// SPARC-NEXT: store i8 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 1 +// SPARC-NEXT: [[TMP2:%.*]] = load i16, ptr [[COERCE]], align 1 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, i16 noundef [[TMP2]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_char( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0:[0-9]+]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { i8, i8 }, align 1 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { i8, i8 }, align 1 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 6 +// SPARC64-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC64-NEXT: [[DOTREAL:%.*]] = load i8, ptr [[DOTREALP]], align 2 +// SPARC64-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC64-NEXT: [[DOTIMAG:%.*]] = load i8, ptr [[DOTIMAGP]], align 1 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store i8 [[DOTREAL]], ptr [[X_REALP]], align 1 +// SPARC64-NEXT: store i8 [[DOTIMAG]], ptr [[X_IMAGP]], align 1 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load i8, ptr [[X_REALP1]], align 1 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load i8, ptr [[X_IMAGP2]], align 1 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store i8 [[X_REAL]], ptr [[COERCE_REALP]], align 1 +// SPARC64-NEXT: store i8 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 1 +// SPARC64-NEXT: [[TMP2:%.*]] = load i16, ptr [[COERCE]], align 1 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i16 noundef [[TMP2]]) +// SPARC64-NEXT: ret void +// +void test_complex_char(va_list *ap) { + _Complex char x = va_arg(*ap, _Complex char); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_char_char( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_CHAR_CHAR:%.*]], align 1 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[X]], ptr align 1 [[TMP1]], i32 2, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_CHAR_CHAR]]) align 1 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_char_char( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_CHAR_CHAR:%.*]], align 1 +// SPARC64-NEXT: [[X_COERCE:%.*]] = alloca i64, align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[X]], ptr align 8 [[ARGP_CUR]], i64 2, i1 false) +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X_COERCE]], ptr align 1 [[X]], i64 2, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[X_COERCE]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP1]]) +// SPARC64-NEXT: ret void +// +void test_char_char(va_list *ap) { + struct char_char x = va_arg(*ap, struct char_char); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_short( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { i16, i16 }, align 2 +// SPARC-NEXT: [[COERCE:%.*]] = alloca { i16, i16 }, align 2 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load i16, ptr [[DOTREALP]], align 2 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load i16, ptr [[DOTIMAGP]], align 2 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store i16 [[DOTREAL]], ptr [[X_REALP]], align 2 +// SPARC-NEXT: store i16 [[DOTIMAG]], ptr [[X_IMAGP]], align 2 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load i16, ptr [[X_REALP1]], align 2 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load i16, ptr [[X_IMAGP2]], align 2 +// SPARC-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC-NEXT: store i16 [[X_REAL]], ptr [[COERCE_REALP]], align 2 +// SPARC-NEXT: store i16 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 2 +// SPARC-NEXT: [[TMP2:%.*]] = load i32, ptr [[COERCE]], align 2 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, i32 noundef [[TMP2]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_short( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { i16, i16 }, align 2 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { i16, i16 }, align 2 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 4 +// SPARC64-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC64-NEXT: [[DOTREAL:%.*]] = load i16, ptr [[DOTREALP]], align 4 +// SPARC64-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC64-NEXT: [[DOTIMAG:%.*]] = load i16, ptr [[DOTIMAGP]], align 2 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store i16 [[DOTREAL]], ptr [[X_REALP]], align 2 +// SPARC64-NEXT: store i16 [[DOTIMAG]], ptr [[X_IMAGP]], align 2 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load i16, ptr [[X_REALP1]], align 2 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load i16, ptr [[X_IMAGP2]], align 2 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store i16 [[X_REAL]], ptr [[COERCE_REALP]], align 2 +// SPARC64-NEXT: store i16 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 2 +// SPARC64-NEXT: [[TMP2:%.*]] = load i32, ptr [[COERCE]], align 2 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i32 noundef [[TMP2]]) +// SPARC64-NEXT: ret void +// +void test_complex_short(va_list *ap) { + _Complex short x = va_arg(*ap, _Complex short); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_short_short( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_SHORT_SHORT:%.*]], align 2 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[X]], ptr align 2 [[TMP1]], i32 4, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_SHORT_SHORT]]) align 2 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_short_short( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_SHORT_SHORT:%.*]], align 2 +// SPARC64-NEXT: [[X_COERCE:%.*]] = alloca i64, align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 2 [[X]], ptr align 8 [[ARGP_CUR]], i64 4, i1 false) +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X_COERCE]], ptr align 2 [[X]], i64 4, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[X_COERCE]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP1]]) +// SPARC64-NEXT: ret void +// +void test_short_short(va_list *ap) { + struct short_short x = va_arg(*ap, struct short_short); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_int( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { i32, i32 }, align 4 +// SPARC-NEXT: [[COERCE:%.*]] = alloca { i32, i32 }, align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load i32, ptr [[DOTREALP]], align 4 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load i32, ptr [[DOTIMAGP]], align 4 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store i32 [[DOTREAL]], ptr [[X_REALP]], align 4 +// SPARC-NEXT: store i32 [[DOTIMAG]], ptr [[X_IMAGP]], align 4 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load i32, ptr [[X_REALP1]], align 4 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load i32, ptr [[X_IMAGP2]], align 4 +// SPARC-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC-NEXT: store i32 [[X_REAL]], ptr [[COERCE_REALP]], align 4 +// SPARC-NEXT: store i32 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 4 +// SPARC-NEXT: [[TMP2:%.*]] = load i64, ptr [[COERCE]], align 4 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, i64 noundef [[TMP2]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_int( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { i32, i32 }, align 4 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { i32, i32 }, align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[ARGP_CUR]], i32 0, i32 0 +// SPARC64-NEXT: [[ARGP_CUR_REAL:%.*]] = load i32, ptr [[ARGP_CUR_REALP]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[ARGP_CUR]], i32 0, i32 1 +// SPARC64-NEXT: [[ARGP_CUR_IMAG:%.*]] = load i32, ptr [[ARGP_CUR_IMAGP]], align 4 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store i32 [[ARGP_CUR_REAL]], ptr [[X_REALP]], align 4 +// SPARC64-NEXT: store i32 [[ARGP_CUR_IMAG]], ptr [[X_IMAGP]], align 4 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load i32, ptr [[X_REALP1]], align 4 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load i32, ptr [[X_IMAGP2]], align 4 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store i32 [[X_REAL]], ptr [[COERCE_REALP]], align 4 +// SPARC64-NEXT: store i32 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 4 +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[COERCE]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 noundef [[TMP1]]) +// SPARC64-NEXT: ret void +// +void test_complex_int(va_list *ap) { + _Complex int x = va_arg(*ap, _Complex int); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_int_int( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_INT_INT:%.*]], align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[X]], ptr align 4 [[TMP1]], i32 8, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_INT_INT]]) align 4 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_int_int( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_INT_INT:%.*]], align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 8, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[X]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP1]]) +// SPARC64-NEXT: ret void +// +void test_int_int(va_list *ap) { + struct int_int x = va_arg(*ap, struct int_int); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_long( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { i32, i32 }, align 4 +// SPARC-NEXT: [[COERCE:%.*]] = alloca { i32, i32 }, align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load i32, ptr [[DOTREALP]], align 4 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load i32, ptr [[DOTIMAGP]], align 4 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store i32 [[DOTREAL]], ptr [[X_REALP]], align 4 +// SPARC-NEXT: store i32 [[DOTIMAG]], ptr [[X_IMAGP]], align 4 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load i32, ptr [[X_REALP1]], align 4 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load i32, ptr [[X_IMAGP2]], align 4 +// SPARC-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC-NEXT: store i32 [[X_REAL]], ptr [[COERCE_REALP]], align 4 +// SPARC-NEXT: store i32 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 4 +// SPARC-NEXT: [[TMP2:%.*]] = load i64, ptr [[COERCE]], align 4 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, i64 noundef [[TMP2]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_long( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { i64, i64 }, align 8 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { i64, i64 }, align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[ARGP_CUR]], i32 0, i32 0 +// SPARC64-NEXT: [[ARGP_CUR_REAL:%.*]] = load i64, ptr [[ARGP_CUR_REALP]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[ARGP_CUR]], i32 0, i32 1 +// SPARC64-NEXT: [[ARGP_CUR_IMAG:%.*]] = load i64, ptr [[ARGP_CUR_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store i64 [[ARGP_CUR_REAL]], ptr [[X_REALP]], align 8 +// SPARC64-NEXT: store i64 [[ARGP_CUR_IMAG]], ptr [[X_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load i64, ptr [[X_REALP1]], align 8 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load i64, ptr [[X_IMAGP2]], align 8 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store i64 [[X_REAL]], ptr [[COERCE_REALP]], align 8 +// SPARC64-NEXT: store i64 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 noundef [[TMP2]], i64 noundef [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_complex_long(va_list *ap) { + _Complex long x = va_arg(*ap, _Complex long); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_long_long( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_LONG:%.*]], align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[X]], ptr align 4 [[TMP1]], i32 8, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_LONG_LONG]]) align 4 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_long_long( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_LONG:%.*]], align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_LONG_LONG]], ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_LONG_LONG]], ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP2]], i64 [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_long_long(va_list *ap) { + struct long_long x = va_arg(*ap, struct long_long); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_long_long( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { i64, i64 }, align 8 +// SPARC-NEXT: [[BYVAL_TEMP:%.*]] = alloca { i64, i64 }, align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load i64, ptr [[DOTREALP]], align 8 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load i64, ptr [[DOTIMAGP]], align 8 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store i64 [[DOTREAL]], ptr [[X_REALP]], align 8 +// SPARC-NEXT: store i64 [[DOTIMAG]], ptr [[X_IMAGP]], align 8 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load i64, ptr [[X_REALP1]], align 8 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load i64, ptr [[X_IMAGP2]], align 8 +// SPARC-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[BYVAL_TEMP]], i32 0, i32 0 +// SPARC-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[BYVAL_TEMP]], i32 0, i32 1 +// SPARC-NEXT: store i64 [[X_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8 +// SPARC-NEXT: store i64 [[X_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval({ i64, i64 }) align 8 [[BYVAL_TEMP]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_long_long( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { i64, i64 }, align 8 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { i64, i64 }, align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[ARGP_CUR]], i32 0, i32 0 +// SPARC64-NEXT: [[ARGP_CUR_REAL:%.*]] = load i64, ptr [[ARGP_CUR_REALP]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[ARGP_CUR]], i32 0, i32 1 +// SPARC64-NEXT: [[ARGP_CUR_IMAG:%.*]] = load i64, ptr [[ARGP_CUR_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store i64 [[ARGP_CUR_REAL]], ptr [[X_REALP]], align 8 +// SPARC64-NEXT: store i64 [[ARGP_CUR_IMAG]], ptr [[X_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load i64, ptr [[X_REALP1]], align 8 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load i64, ptr [[X_IMAGP2]], align 8 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store i64 [[X_REAL]], ptr [[COERCE_REALP]], align 8 +// SPARC64-NEXT: store i64 [[X_IMAG]], ptr [[COERCE_IMAGP]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 noundef [[TMP2]], i64 noundef [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_complex_long_long(va_list *ap) { + _Complex long long x = va_arg(*ap, _Complex long long); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_long_long_long_long( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_LONG_LONG_LONG:%.*]], align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[X]], ptr align 8 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_LONG_LONG_LONG_LONG]]) align 8 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_long_long_long_long( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_LONG_LONG_LONG:%.*]], align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_LONG_LONG_LONG_LONG]], ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_LONG_LONG_LONG_LONG]], ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP2]], i64 [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_long_long_long_long(va_list *ap) { + struct long_long_long_long x = + va_arg(*ap, struct long_long_long_long); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_float( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { float, float }, align 4 +// SPARC-NEXT: [[BYVAL_TEMP:%.*]] = alloca { float, float }, align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load float, ptr [[DOTREALP]], align 4 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load float, ptr [[DOTIMAGP]], align 4 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store float [[DOTREAL]], ptr [[X_REALP]], align 4 +// SPARC-NEXT: store float [[DOTIMAG]], ptr [[X_IMAGP]], align 4 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load float, ptr [[X_REALP1]], align 4 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP2]], align 4 +// SPARC-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[BYVAL_TEMP]], i32 0, i32 0 +// SPARC-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[BYVAL_TEMP]], i32 0, i32 1 +// SPARC-NEXT: store float [[X_REAL]], ptr [[BYVAL_TEMP_REALP]], align 4 +// SPARC-NEXT: store float [[X_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 4 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval({ float, float }) align 4 [[BYVAL_TEMP]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_float( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { float, float }, align 4 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { float, float }, align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[ARGP_CUR]], i32 0, i32 0 +// SPARC64-NEXT: [[ARGP_CUR_REAL:%.*]] = load float, ptr [[ARGP_CUR_REALP]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[ARGP_CUR]], i32 0, i32 1 +// SPARC64-NEXT: [[ARGP_CUR_IMAG:%.*]] = load float, ptr [[ARGP_CUR_IMAGP]], align 4 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store float [[ARGP_CUR_REAL]], ptr [[X_REALP]], align 4 +// SPARC64-NEXT: store float [[ARGP_CUR_IMAG]], ptr [[X_IMAGP]], align 4 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load float, ptr [[X_REALP1]], align 4 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load float, ptr [[X_IMAGP2]], align 4 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store float [[X_REAL]], ptr [[COERCE_REALP]], align 4 +// SPARC64-NEXT: store float [[X_IMAG]], ptr [[COERCE_IMAGP]], align 4 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg noundef [[TMP2]], float inreg noundef [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_complex_float(va_list *ap) { + _Complex float x = va_arg(*ap, _Complex float); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_float_float( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_FLOAT_FLOAT:%.*]], align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[X]], ptr align 4 [[TMP1]], i32 8, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_FLOAT_FLOAT]]) align 4 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_float_float( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_FLOAT_FLOAT:%.*]], align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 8, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg [[TMP2]], float inreg [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_float_float(va_list *ap) { + struct float_float x = va_arg(*ap, struct float_float); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_double( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { double, double }, align 8 +// SPARC-NEXT: [[BYVAL_TEMP:%.*]] = alloca { double, double }, align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load double, ptr [[DOTREALP]], align 8 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load double, ptr [[DOTIMAGP]], align 8 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store double [[DOTREAL]], ptr [[X_REALP]], align 8 +// SPARC-NEXT: store double [[DOTIMAG]], ptr [[X_IMAGP]], align 8 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load double, ptr [[X_REALP1]], align 8 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP2]], align 8 +// SPARC-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 0 +// SPARC-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[BYVAL_TEMP]], i32 0, i32 1 +// SPARC-NEXT: store double [[X_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8 +// SPARC-NEXT: store double [[X_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval({ double, double }) align 8 [[BYVAL_TEMP]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_double( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { double, double }, align 8 +// SPARC64-NEXT: [[COERCE:%.*]] = alloca { double, double }, align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[ARGP_CUR]], i32 0, i32 0 +// SPARC64-NEXT: [[ARGP_CUR_REAL:%.*]] = load double, ptr [[ARGP_CUR_REALP]], align 8 +// SPARC64-NEXT: [[ARGP_CUR_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[ARGP_CUR]], i32 0, i32 1 +// SPARC64-NEXT: [[ARGP_CUR_IMAG:%.*]] = load double, ptr [[ARGP_CUR_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store double [[ARGP_CUR_REAL]], ptr [[X_REALP]], align 8 +// SPARC64-NEXT: store double [[ARGP_CUR_IMAG]], ptr [[X_IMAGP]], align 8 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load double, ptr [[X_REALP1]], align 8 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load double, ptr [[X_IMAGP2]], align 8 +// SPARC64-NEXT: [[COERCE_REALP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: store double [[X_REAL]], ptr [[COERCE_REALP]], align 8 +// SPARC64-NEXT: store double [[X_IMAG]], ptr [[COERCE_IMAGP]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load double, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, double noundef [[TMP2]], double noundef [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_complex_double(va_list *ap) { + _Complex double x = va_arg(*ap, _Complex double); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_double_double( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_DOUBLE_DOUBLE:%.*]], align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[X]], ptr align 8 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_DOUBLE_DOUBLE]]) align 8 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_double_double( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_DOUBLE_DOUBLE:%.*]], align 8 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_DOUBLE]], ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_DOUBLE]], ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load double, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, double [[TMP2]], double [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_double_double(va_list *ap) { + struct double_double x = va_arg(*ap, struct double_double); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_complex_long_double( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca { fp128, fp128 }, align 8 +// SPARC-NEXT: [[BYVAL_TEMP:%.*]] = alloca { fp128, fp128 }, align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC-NEXT: [[DOTREAL:%.*]] = load fp128, ptr [[DOTREALP]], align 8 +// SPARC-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC-NEXT: [[DOTIMAG:%.*]] = load fp128, ptr [[DOTIMAGP]], align 8 +// SPARC-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: store fp128 [[DOTREAL]], ptr [[X_REALP]], align 8 +// SPARC-NEXT: store fp128 [[DOTIMAG]], ptr [[X_IMAGP]], align 8 +// SPARC-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 0 +// SPARC-NEXT: [[X_REAL:%.*]] = load fp128, ptr [[X_REALP1]], align 8 +// SPARC-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 1 +// SPARC-NEXT: [[X_IMAG:%.*]] = load fp128, ptr [[X_IMAGP2]], align 8 +// SPARC-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[BYVAL_TEMP]], i32 0, i32 0 +// SPARC-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[BYVAL_TEMP]], i32 0, i32 1 +// SPARC-NEXT: store fp128 [[X_REAL]], ptr [[BYVAL_TEMP_REALP]], align 8 +// SPARC-NEXT: store fp128 [[X_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 8 +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval({ fp128, fp128 }) align 8 [[BYVAL_TEMP]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_complex_long_double( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca { fp128, fp128 }, align 16 +// SPARC64-NEXT: [[BYVAL_TEMP:%.*]] = alloca { fp128, fp128 }, align 16 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 8 +// SPARC64-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 0 +// SPARC64-NEXT: [[DOTREAL:%.*]] = load fp128, ptr [[DOTREALP]], align 16 +// SPARC64-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 1 +// SPARC64-NEXT: [[DOTIMAG:%.*]] = load fp128, ptr [[DOTIMAGP]], align 16 +// SPARC64-NEXT: [[X_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: store fp128 [[DOTREAL]], ptr [[X_REALP]], align 16 +// SPARC64-NEXT: store fp128 [[DOTIMAG]], ptr [[X_IMAGP]], align 16 +// SPARC64-NEXT: [[X_REALP1:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[X_REAL:%.*]] = load fp128, ptr [[X_REALP1]], align 16 +// SPARC64-NEXT: [[X_IMAGP2:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[X_IMAG:%.*]] = load fp128, ptr [[X_IMAGP2]], align 16 +// SPARC64-NEXT: [[BYVAL_TEMP_REALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[BYVAL_TEMP]], i32 0, i32 0 +// SPARC64-NEXT: [[BYVAL_TEMP_IMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[BYVAL_TEMP]], i32 0, i32 1 +// SPARC64-NEXT: store fp128 [[X_REAL]], ptr [[BYVAL_TEMP_REALP]], align 16 +// SPARC64-NEXT: store fp128 [[X_IMAG]], ptr [[BYVAL_TEMP_IMAGP]], align 16 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, ptr nofree noundef align 16 dead_on_return dereferenceable(32) [[BYVAL_TEMP]]) +// SPARC64-NEXT: ret void +// +void test_complex_long_double(va_list *ap) { + _Complex long double x = va_arg(*ap, _Complex long double); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_long_double_long_double( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_DOUBLE_LONG_DOUBLE:%.*]], align 8 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[X]], ptr align 8 [[TMP1]], i32 32, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_LONG_DOUBLE_LONG_DOUBLE]]) align 8 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_long_double_long_double( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_LONG_DOUBLE_LONG_DOUBLE:%.*]], align 16 +// SPARC64-NEXT: [[BYVAL_TEMP:%.*]] = alloca [[STRUCT_LONG_DOUBLE_LONG_DOUBLE]], align 16 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[X]], ptr align 16 [[TMP1]], i64 32, i1 false) +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[BYVAL_TEMP]], ptr align 16 [[X]], i64 32, i1 false) +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, ptr nofree noundef align 16 dead_on_return dereferenceable(32) [[BYVAL_TEMP]]) +// SPARC64-NEXT: ret void +// +void test_long_double_long_double(va_list *ap) { + struct long_double_long_double x = + va_arg(*ap, struct long_double_long_double); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_aligned_int( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_ALIGNED_INT:%.*]], align 16 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[X]], ptr align 16 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_ALIGNED_INT]]) align 16 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_aligned_int( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_ALIGNED_INT:%.*]], align 16 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 15 +// SPARC64-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP1]], i64 -16) +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[X]], ptr align 16 [[ARGP_CUR_ALIGNED]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP3:%.*]] = load i64, ptr [[TMP2]], align 16 +// SPARC64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP5:%.*]] = load i64, ptr [[TMP4]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 undef, i64 [[TMP3]], i64 [[TMP5]]) +// SPARC64-NEXT: ret void +// +void test_aligned_int(va_list *ap) { + struct aligned_int x = va_arg(*ap, struct aligned_int); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_aligned_float( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_ALIGNED_FLOAT:%.*]], align 16 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 16 [[X]], ptr align 16 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_ALIGNED_FLOAT]]) align 16 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_aligned_float( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_ALIGNED_FLOAT:%.*]], align 16 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 15 +// SPARC64-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[TMP1]], i64 -16) +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[X]], ptr align 16 [[ARGP_CUR_ALIGNED]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP3:%.*]] = load float, ptr [[TMP2]], align 16 +// SPARC64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4 +// SPARC64-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 2 +// SPARC64-NEXT: [[TMP7:%.*]] = load i64, ptr [[TMP6]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 undef, float inreg [[TMP3]], i32 inreg [[TMP5]], i64 inreg [[TMP7]]) +// SPARC64-NEXT: ret void +// +void test_aligned_float(va_list *ap) { + struct aligned_float x = va_arg(*ap, struct aligned_float); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_int_int_int_int( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_INT_INT_INT_INT:%.*]], align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[X]], ptr align 4 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_INT_INT_INT_INT]]) align 4 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_int_int_int_int( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_INT_INT_INT_INT:%.*]], align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 4 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP2]], i64 [[TMP4]]) +// SPARC64-NEXT: ret void +// +void test_int_int_int_int(va_list *ap) { + struct int_int_int_int x = va_arg(*ap, struct int_int_int_int); + sink(0, x); +} + +// SPARC-LABEL: define dso_local void @test_float_float_float_float( +// SPARC-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC-NEXT: [[ENTRY:.*:]] +// SPARC-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 4 +// SPARC-NEXT: [[X:%.*]] = alloca [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT:%.*]], align 4 +// SPARC-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 4 +// SPARC-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// SPARC-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[X]], ptr align 4 [[TMP1]], i32 16, i1 false) +// SPARC-NEXT: call void (i32, ...) @sink(i32 noundef 0, ptr noundef byval([[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]]) align 4 [[X]]) +// SPARC-NEXT: ret void +// +// SPARC64-LABEL: define dso_local void @test_float_float_float_float( +// SPARC64-SAME: ptr noundef [[AP:%.*]]) #[[ATTR0]] { +// SPARC64-NEXT: [[ENTRY:.*:]] +// SPARC64-NEXT: [[AP_ADDR:%.*]] = alloca ptr, align 8 +// SPARC64-NEXT: [[X:%.*]] = alloca [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT:%.*]], align 4 +// SPARC64-NEXT: store ptr [[AP]], ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[TMP0:%.*]] = load ptr, ptr [[AP_ADDR]], align 8 +// SPARC64-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 8 +// SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 +// SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 +// SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 +// SPARC64-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 2 +// SPARC64-NEXT: [[TMP6:%.*]] = load float, ptr [[TMP5]], align 4 +// SPARC64-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 3 +// SPARC64-NEXT: [[TMP8:%.*]] = load float, ptr [[TMP7]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg [[TMP2]], float inreg [[TMP4]], float inreg [[TMP6]], float inreg [[TMP8]]) +// SPARC64-NEXT: ret void +// +void test_float_float_float_float(va_list *ap) { + struct float_float_float_float x = + va_arg(*ap, struct float_float_float_float); + sink(0, x); +} >From 38d918afa0eda2f62817b7f965a06d8930fa215f Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Sun, 16 Aug 2026 02:22:56 +0200 Subject: [PATCH 2/2] [Sparc]: don't pass variadic arguments in float registers --- clang/lib/CodeGen/Targets/Sparc.cpp | 22 ++++--- clang/test/CodeGen/Sparc/variadic-aggregate.c | 60 ++++++++----------- 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp index 25f0009fe7a09..ed9decb8d8726 100644 --- a/clang/lib/CodeGen/Targets/Sparc.cpp +++ b/clang/lib/CodeGen/Targets/Sparc.cpp @@ -163,7 +163,7 @@ class SparcV9ABIInfo : public ABIInfo { bool IsComplexGnuABI; ABIArgInfo classifyType(QualType RetTy, unsigned SizeLimit, - unsigned &RegOffset) const; + unsigned &RegOffset, bool IsVarArg) const; void computeInfo(CGFunctionInfo &FI) const override; RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, AggValueSlot Slot) const override; @@ -185,9 +185,10 @@ class SparcV9ABIInfo : public ABIInfo { SmallVector<llvm::Type*, 8> Elems; uint64_t Size; bool InReg; + bool IsVarArg; - CoerceBuilder(llvm::LLVMContext &c, const llvm::DataLayout &dl) - : Context(c), DL(dl), Size(0), InReg(false) {} + CoerceBuilder(llvm::LLVMContext &c, const llvm::DataLayout &dl, bool IsVarArg) + : Context(c), DL(dl), Size(0), InReg(false), IsVarArg(IsVarArg) {} // Pad Elems with integers until Size is ToSize. void pad(uint64_t ToSize) { @@ -217,6 +218,9 @@ class SparcV9ABIInfo : public ABIInfo { // Add a floating point element at Offset. void addFloat(uint64_t Offset, llvm::Type *Ty, unsigned Bits) { + // Varargs are treated as integers. + if (IsVarArg) + return; // Unaligned floats are treated as integers. if (Offset % Bits) return; @@ -277,7 +281,7 @@ class SparcV9ABIInfo : public ABIInfo { } // end anonymous namespace ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit, - unsigned &RegOffset) const { + unsigned &RegOffset, bool IsVarArg) const { if (Ty->isVoidType()) return ABIArgInfo::getIgnore(); @@ -369,7 +373,7 @@ ABIArgInfo SparcV9ABIInfo::classifyType(QualType Ty, unsigned SizeLimit, return ABIArgInfo::getDirect(/*T=*/nullptr, /*Offset=*/0, Padding); } - CoerceBuilder CB(VMContext, getDataLayout()); + CoerceBuilder CB(VMContext, getDataLayout(), IsVarArg); CB.addStruct(0, StrTy); // All structs, even empty ones, should take up a register argument slot, // so pin the minimum struct size to one bit. @@ -406,13 +410,15 @@ RValue SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, void SparcV9ABIInfo::computeInfo(CGFunctionInfo &FI) const { unsigned RetOffset = 0; - ABIArgInfo RetType = classifyType(FI.getReturnType(), 32 * 8, RetOffset); + ABIArgInfo RetType = classifyType(FI.getReturnType(), 32 * 8, RetOffset, /*IsVarArg=*/false); FI.getReturnInfo() = RetType; // Indirect returns will have its pointer passed as an argument. unsigned ArgOffset = RetType.isIndirect() ? RetOffset : 0; - for (auto &I : FI.arguments()) - I.info = classifyType(I.type, 16 * 8, ArgOffset); + for (auto [ArgNo, I] : llvm::enumerate(FI.arguments())) { + bool IsVarArg = ArgNo >= FI.getNumRequiredArgs(); + I.info = classifyType(I.type, 16 * 8, ArgOffset, IsVarArg); + } } namespace { diff --git a/clang/test/CodeGen/Sparc/variadic-aggregate.c b/clang/test/CodeGen/Sparc/variadic-aggregate.c index 92093e1133b8a..fb7524b887980 100644 --- a/clang/test/CodeGen/Sparc/variadic-aggregate.c +++ b/clang/test/CodeGen/Sparc/variadic-aggregate.c @@ -615,11 +615,8 @@ void test_long_long_long_long(va_list *ap) { // SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 1 // SPARC64-NEXT: store float [[X_REAL]], ptr [[COERCE_REALP]], align 4 // SPARC64-NEXT: store float [[X_IMAG]], ptr [[COERCE_IMAGP]], align 4 -// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 -// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { float, float }, ptr [[COERCE]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg noundef [[TMP2]], float inreg noundef [[TMP4]]) +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[COERCE]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 noundef [[TMP1]]) // SPARC64-NEXT: ret void // void test_complex_float(va_list *ap) { @@ -650,11 +647,8 @@ void test_complex_float(va_list *ap) { // SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 8 // SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 // SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 8, i1 false) -// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 -// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg [[TMP2]], float inreg [[TMP4]]) +// SPARC64-NEXT: [[TMP1:%.*]] = load i64, ptr [[X]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP1]]) // SPARC64-NEXT: ret void // void test_float_float(va_list *ap) { @@ -717,11 +711,11 @@ void test_float_float(va_list *ap) { // SPARC64-NEXT: [[COERCE_IMAGP:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 1 // SPARC64-NEXT: store double [[X_REAL]], ptr [[COERCE_REALP]], align 8 // SPARC64-NEXT: store double [[X_IMAG]], ptr [[COERCE_IMAGP]], align 8 -// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8 -// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { double, double }, ptr [[COERCE]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP4:%.*]] = load double, ptr [[TMP3]], align 8 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, double noundef [[TMP2]], double noundef [[TMP4]]) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[COERCE]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 noundef [[TMP2]], i64 noundef [[TMP4]]) // SPARC64-NEXT: ret void // void test_complex_double(va_list *ap) { @@ -752,11 +746,11 @@ void test_complex_double(va_list *ap) { // SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 // SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 // SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) -// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_DOUBLE]], ptr [[X]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP2:%.*]] = load double, ptr [[TMP1]], align 8 -// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_DOUBLE_DOUBLE]], ptr [[X]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP4:%.*]] = load double, ptr [[TMP3]], align 8 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, double [[TMP2]], double [[TMP4]]) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP2]], i64 [[TMP4]]) // SPARC64-NEXT: ret void // void test_double_double(va_list *ap) { @@ -925,13 +919,11 @@ void test_aligned_int(va_list *ap) { // SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i64 16 // SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 // SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[X]], ptr align 16 [[ARGP_CUR_ALIGNED]], i64 16, i1 false) -// SPARC64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP3:%.*]] = load float, ptr [[TMP2]], align 16 -// SPARC64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4 -// SPARC64-NEXT: [[TMP6:%.*]] = getelementptr inbounds nuw { float, i32, i64 }, ptr [[X]], i32 0, i32 2 -// SPARC64-NEXT: [[TMP7:%.*]] = load i64, ptr [[TMP6]], align 8 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 undef, float inreg [[TMP3]], i32 inreg [[TMP5]], i64 inreg [[TMP7]]) +// SPARC64-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP3:%.*]] = load i64, ptr [[TMP2]], align 16 +// SPARC64-NEXT: [[TMP4:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP5:%.*]] = load i64, ptr [[TMP4]], align 8 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 undef, i64 [[TMP3]], i64 [[TMP5]]) // SPARC64-NEXT: ret void // void test_aligned_float(va_list *ap) { @@ -997,15 +989,11 @@ void test_int_int_int_int(va_list *ap) { // SPARC64-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i64 16 // SPARC64-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 8 // SPARC64-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[X]], ptr align 8 [[ARGP_CUR]], i64 16, i1 false) -// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 0 -// SPARC64-NEXT: [[TMP2:%.*]] = load float, ptr [[TMP1]], align 4 -// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 1 -// SPARC64-NEXT: [[TMP4:%.*]] = load float, ptr [[TMP3]], align 4 -// SPARC64-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 2 -// SPARC64-NEXT: [[TMP6:%.*]] = load float, ptr [[TMP5]], align 4 -// SPARC64-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOAT_FLOAT_FLOAT_FLOAT]], ptr [[X]], i32 0, i32 3 -// SPARC64-NEXT: [[TMP8:%.*]] = load float, ptr [[TMP7]], align 4 -// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, float inreg [[TMP2]], float inreg [[TMP4]], float inreg [[TMP6]], float inreg [[TMP8]]) +// SPARC64-NEXT: [[TMP1:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 0 +// SPARC64-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 4 +// SPARC64-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[X]], i32 0, i32 1 +// SPARC64-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 4 +// SPARC64-NEXT: call void (i32, ...) @sink(i32 noundef signext 0, i64 [[TMP2]], i64 [[TMP4]]) // SPARC64-NEXT: ret void // void test_float_float_float_float(va_list *ap) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
