[clang] [Sparc][clang] make `_Complex` ABI GCC-compatible (PR #212340)

Sergei Barannikov via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 1 16:07:54 PDT 2026


================
@@ -0,0 +1,585 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple sparc-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN:   | FileCheck %s --check-prefix=V8
+// RUN: %clang_cc1 -triple sparcv9-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN:   | FileCheck %s --check-prefix=V9
+
+// RUN: %clang_cc1 -triple sparc-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN:   -emit-llvm -o - %s | FileCheck %s --check-prefix=COMPAT23-V8
+// RUN: %clang_cc1 -triple sparcv9-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN:   -emit-llvm -o - %s | FileCheck %s --check-prefix=COMPAT23-V9
+
+// Test how SPARC passes and returns `_Complex` values.
+
+// A `_Complex` value with an integer element type is returned packed into whole
+// integer registers. Clang 23 and before instead gave each part a register of
+// its own on v8, and on v9 left-justified a value narrower than a register the
+// way a small struct is returned. The new behavior matches GCC.
+
+// V8-LABEL: define dso_local i16 @complex_char(
+// V8-SAME: i16 noundef [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// V8-NEXT:  [[ENTRY:.*:]]
+// V8-NEXT:    [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// V8-NEXT:    [[C:%.*]] = alloca { i8, i8 }, align 1
+// V8-NEXT:    store i16 [[C_COERCE]], ptr [[C]], align 1
+// V8-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT:    [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// V8-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT:    [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// V8-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT:    store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// V8-NEXT:    store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// V8-NEXT:    [[TMP0:%.*]] = load i16, ptr [[RETVAL]], align 1
+// V8-NEXT:    ret i16 [[TMP0]]
+//
+// V9-LABEL: define dso_local i16 @complex_char(
+// V9-SAME: i16 noundef [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// V9-NEXT:  [[ENTRY:.*:]]
+// V9-NEXT:    [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// V9-NEXT:    [[C:%.*]] = alloca { i8, i8 }, align 1
+// V9-NEXT:    store i16 [[C_COERCE]], ptr [[C]], align 1
+// V9-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT:    [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// V9-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT:    [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// V9-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT:    store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// V9-NEXT:    store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// V9-NEXT:    [[TMP0:%.*]] = load i16, ptr [[RETVAL]], align 1
+// V9-NEXT:    ret i16 [[TMP0]]
+//
+// COMPAT23-V8-LABEL: define dso_local { i8, i8 } @complex_char(
+// COMPAT23-V8-SAME: ptr noundef byval({ i8, i8 }) align 1 [[C:%.*]]) #[[ATTR0:[0-9]+]] {
+// COMPAT23-V8-NEXT:  [[ENTRY:.*:]]
+// COMPAT23-V8-NEXT:    [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V8-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V8-NEXT:    [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// COMPAT23-V8-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V8-NEXT:    [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// COMPAT23-V8-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V8-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V8-NEXT:    store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// COMPAT23-V8-NEXT:    store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// COMPAT23-V8-NEXT:    [[TMP0:%.*]] = load { i8, i8 }, ptr [[RETVAL]], align 1
+// COMPAT23-V8-NEXT:    ret { i8, i8 } [[TMP0]]
+//
+// COMPAT23-V9-LABEL: define dso_local i64 @complex_char(
+// COMPAT23-V9-SAME: i64 [[C_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
+// COMPAT23-V9-NEXT:  [[ENTRY:.*:]]
+// COMPAT23-V9-NEXT:    [[RETVAL:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V9-NEXT:    [[C:%.*]] = alloca { i8, i8 }, align 1
+// COMPAT23-V9-NEXT:    [[RETVAL_COERCE:%.*]] = alloca i64, align 8
+// COMPAT23-V9-NEXT:    [[COERCE_HIGHBITS:%.*]] = lshr i64 [[C_COERCE]], 48
+// COMPAT23-V9-NEXT:    [[COERCE_VAL_II:%.*]] = trunc i64 [[COERCE_HIGHBITS]] to i16
+// COMPAT23-V9-NEXT:    store i16 [[COERCE_VAL_II]], ptr [[C]], align 1
+// COMPAT23-V9-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 0
+// COMPAT23-V9-NEXT:    [[C_REAL:%.*]] = load i8, ptr [[C_REALP]], align 1
+// COMPAT23-V9-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[C]], i32 0, i32 1
+// COMPAT23-V9-NEXT:    [[C_IMAG:%.*]] = load i8, ptr [[C_IMAGP]], align 1
+// COMPAT23-V9-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 0
+// COMPAT23-V9-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[RETVAL]], i32 0, i32 1
+// COMPAT23-V9-NEXT:    store i8 [[C_REAL]], ptr [[RETVAL_REALP]], align 1
+// COMPAT23-V9-NEXT:    store i8 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 1
+// COMPAT23-V9-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 1 [[RETVAL]], i64 2, i1 false)
+// COMPAT23-V9-NEXT:    [[TMP0:%.*]] = load i64, ptr [[RETVAL_COERCE]], align 8
+// COMPAT23-V9-NEXT:    ret i64 [[TMP0]]
+//
+_Complex char complex_char(_Complex char c) { return c; }
+
+// V8-LABEL: define dso_local i32 @complex_short(
+// V8-SAME: i32 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V8-NEXT:  [[ENTRY:.*:]]
+// V8-NEXT:    [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// V8-NEXT:    [[C:%.*]] = alloca { i16, i16 }, align 2
+// V8-NEXT:    store i32 [[C_COERCE]], ptr [[C]], align 2
+// V8-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// V8-NEXT:    [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// V8-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// V8-NEXT:    [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// V8-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// V8-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// V8-NEXT:    store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// V8-NEXT:    store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
+// V8-NEXT:    [[TMP0:%.*]] = load i32, ptr [[RETVAL]], align 2
+// V8-NEXT:    ret i32 [[TMP0]]
+//
+// V9-LABEL: define dso_local i32 @complex_short(
+// V9-SAME: i32 noundef [[C_COERCE:%.*]]) #[[ATTR0]] {
+// V9-NEXT:  [[ENTRY:.*:]]
+// V9-NEXT:    [[RETVAL:%.*]] = alloca { i16, i16 }, align 2
+// V9-NEXT:    [[C:%.*]] = alloca { i16, i16 }, align 2
+// V9-NEXT:    store i32 [[C_COERCE]], ptr [[C]], align 2
+// V9-NEXT:    [[C_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 0
+// V9-NEXT:    [[C_REAL:%.*]] = load i16, ptr [[C_REALP]], align 2
+// V9-NEXT:    [[C_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[C]], i32 0, i32 1
+// V9-NEXT:    [[C_IMAG:%.*]] = load i16, ptr [[C_IMAGP]], align 2
+// V9-NEXT:    [[RETVAL_REALP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 0
+// V9-NEXT:    [[RETVAL_IMAGP:%.*]] = getelementptr inbounds nuw { i16, i16 }, ptr [[RETVAL]], i32 0, i32 1
+// V9-NEXT:    store i16 [[C_REAL]], ptr [[RETVAL_REALP]], align 2
+// V9-NEXT:    store i16 [[C_IMAG]], ptr [[RETVAL_IMAGP]], align 2
----------------
s-barannikov wrote:

I suspect the order of parts should be inverted on little-endian SPARC

[offtopic]
but is sparc-el even real?.. gcc doesn't think so. I wonder if sparc-el support in clang/llvm is actually functional and whether we can drop it.


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


More information about the cfe-commits mailing list