https://github.com/folkertdev updated https://github.com/llvm/llvm-project/pull/214981
>From d09ba50e82f94ea448f75d68f88405346240a748 Mon Sep 17 00:00:00 2001 From: Folkert de Vries <[email protected]> Date: Sat, 8 Aug 2026 17:39:12 +0200 Subject: [PATCH] [Sparc][clang] implement `va_arg` in the clang frontend --- clang/lib/CodeGen/Targets/Sparc.cpp | 21 +++++ clang/test/CodeGen/Sparc/sparc-vaarg.c | 117 +++++++++++++++++-------- 2 files changed, 102 insertions(+), 36 deletions(-) diff --git a/clang/lib/CodeGen/Targets/Sparc.cpp b/clang/lib/CodeGen/Targets/Sparc.cpp index 25f0009fe7a09..875fadf330ebd 100644 --- a/clang/lib/CodeGen/Targets/Sparc.cpp +++ b/clang/lib/CodeGen/Targets/Sparc.cpp @@ -34,6 +34,8 @@ class SparcV8ABIInfo : public DefaultABIInfo { ABIArgInfo classifyComplexType(const ComplexType *Ty, bool IsRet) const; ABIArgInfo classifyReturnType(QualType RetTy) const; ABIArgInfo classifyArgumentType(QualType Ty) const; + RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty, + AggValueSlot Slot) const override; void computeInfo(CGFunctionInfo &FI) const override; }; } // end anonymous namespace @@ -94,6 +96,25 @@ void SparcV8ABIInfo::computeInfo(CGFunctionInfo &FI) const { Arg.info = classifyArgumentType(Arg.type); } +RValue SparcV8ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty, AggValueSlot Slot) const { + CharUnits SlotSize = CharUnits::fromQuantity(4); + auto TInfo = getContext().getTypeInfoInChars(Ty); + + // E.g. long double, larger _Complex and aggregate values are indirect. + bool IsIndirect = classifyArgumentType(Ty).isIndirect(); + + // An alignment higher than the slot size is not respected. + bool AllowHigherAlign = false; + + // Force values smaller than a slot (e.g. _Complex char) + // into the right-most bytes. + bool ForceRightAdjust = true; + + return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect, TInfo, SlotSize, + AllowHigherAlign, Slot, ForceRightAdjust); +} + namespace { class SparcV8TargetCodeGenInfo : public TargetCodeGenInfo { public: diff --git a/clang/test/CodeGen/Sparc/sparc-vaarg.c b/clang/test/CodeGen/Sparc/sparc-vaarg.c index 53d215f479fae..f322229e20c43 100644 --- a/clang/test/CodeGen/Sparc/sparc-vaarg.c +++ b/clang/test/CodeGen/Sparc/sparc-vaarg.c @@ -6,13 +6,13 @@ // CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0:[0-9]+]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4 -// CHECK-NEXT: [[VARET:%.*]] = alloca i32, align 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], i32 -// CHECK-NEXT: store i32 [[TMP1]], ptr [[VARET]], align 4 -// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[VARET]], align 4 -// CHECK-NEXT: ret i32 [[TMP2]] +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] // int get_int(va_list *args) { return va_arg(*args, int); @@ -20,68 +20,98 @@ int get_int(va_list *args) { enum RGB { R = 1, G = 2, B = 3 }; +// Enums are passed like integers. // CHECK-LABEL: define dso_local i32 @get_enum( // CHECK-SAME: ptr noundef [[ARGS:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4 -// CHECK-NEXT: [[VARET:%.*]] = alloca i32, align 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], i32 -// CHECK-NEXT: store i32 [[TMP1]], ptr [[VARET]], align 4 -// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[VARET]], align 4 -// CHECK-NEXT: ret i32 [[TMP2]] +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: ret i32 [[TMP1]] // enum RGB get_enum(va_list *args) { return va_arg(*args, enum RGB); } +// long long is passed directly, note how ARGP_CUR is advanced by 8. +// The read is under-aligned however, the ARGP_CUR is only aligned to a slot. // 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 4 -// CHECK-NEXT: [[VARET:%.*]] = alloca i64, align 8 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], i64 -// CHECK-NEXT: store i64 [[TMP1]], ptr [[VARET]], align 8 -// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[VARET]], align 8 -// CHECK-NEXT: ret i64 [[TMP2]] +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: ret i64 [[TMP1]] // long long get_long_long(va_list *args) { return va_arg(*args, long long); } -struct Foo { +struct Large { long long x; }; -// CHECK-LABEL: define dso_local void @get_struct( -// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_FOO:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARGS:%.*]]) #[[ATTR0]] { +// Aggregates are passed indirectly, note how ARGP_CUR is advanced by 4. +// CHECK-LABEL: define dso_local void @get_struct_long_long( +// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARGS:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[AGG_RESULT]], ptr align 8 [[TMP1]], i32 8, i1 false) // CHECK-NEXT: ret void // -struct Foo get_struct(va_list *args) { - return va_arg(*args, struct Foo); +struct Large get_struct_long_long(va_list *args) { + return va_arg(*args, struct Large); } +struct Tiny { + char x; +}; + +// CHECK-LABEL: define dso_local void @get_struct_char( +// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TINY:%.*]]) align 1 [[AGG_RESULT:%.*]], ptr noundef [[ARGS:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[AGG_RESULT]], ptr align 1 [[TMP1]], i32 1, i1 false) +// CHECK-NEXT: ret void +// +struct Tiny get_struct_char(va_list *args) { + return va_arg(*args, struct Tiny); +} + +// long double is passed indirectly, note how ARGP_CUR is advanced by 4. // CHECK-LABEL: define dso_local void @get_long_double( // CHECK-SAME: ptr dead_on_unwind noalias writable sret(fp128) align 8 [[AGG_RESULT:%.*]], ptr noundef [[ARGS:%.*]]) #[[ATTR0]] { // CHECK-NEXT: [[ENTRY:.*:]] // CHECK-NEXT: [[RESULT_PTR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[ARGS_ADDR:%.*]] = alloca ptr, align 4 -// CHECK-NEXT: [[VARET:%.*]] = alloca fp128, align 8 // CHECK-NEXT: store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], fp128 -// CHECK-NEXT: store fp128 [[TMP1]], ptr [[VARET]], align 8 -// CHECK-NEXT: [[TMP2:%.*]] = load fp128, ptr [[VARET]], align 8 +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load fp128, ptr [[TMP1]], align 8 // CHECK-NEXT: store fp128 [[TMP2]], ptr [[AGG_RESULT]], align 8 // CHECK-NEXT: [[TMP3:%.*]] = load fp128, ptr [[AGG_RESULT]], align 8 // CHECK-NEXT: store fp128 [[TMP3]], ptr [[AGG_RESULT]], align 8 @@ -93,15 +123,19 @@ long double get_long_double(va_list *args) { _Complex char complex_char_sink; +// _Complex char is passed in the right-most bytes of the slot, note the getelementptr with a value of 2. // 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 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 2 // CHECK-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 0 -// CHECK-NEXT: [[DOTREAL:%.*]] = load i8, ptr [[DOTREALP]], align 1 +// CHECK-NEXT: [[DOTREAL:%.*]] = load i8, ptr [[DOTREALP]], align 2 // CHECK-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i8, i8 }, ptr [[TMP1]], i32 0, i32 1 // CHECK-NEXT: [[DOTIMAG:%.*]] = load i8, ptr [[DOTIMAGP]], align 1 // CHECK-NEXT: store i8 [[DOTREAL]], ptr @complex_char_sink, align 1 @@ -114,19 +148,22 @@ void get_complex_char(va_list *args) { _Complex int complex_int_sink; +// _Complex int is passed directly, note how ARGP_CUR is advanced by 8. // 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 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr -// CHECK-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 0 -// CHECK-NEXT: [[DOTREAL:%.*]] = load i32, ptr [[DOTREALP]], align 4 -// CHECK-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i32, i32 }, ptr [[TMP1]], i32 0, i32 1 -// CHECK-NEXT: [[DOTIMAG:%.*]] = load i32, ptr [[DOTIMAGP]], align 4 -// CHECK-NEXT: store i32 [[DOTREAL]], ptr @complex_int_sink, align 4 -// CHECK-NEXT: store i32 [[DOTIMAG]], ptr getelementptr inbounds nuw (i8, ptr @complex_int_sink, i32 4), align 4 +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 8 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// 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 4 +// 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, i32 4), align 4 // CHECK-NEXT: ret void // void get_complex_int(va_list *args) { @@ -135,13 +172,17 @@ void get_complex_int(va_list *args) { _Complex long long complex_long_long_sink; +// _Complex long long is passed indirectly, note how ARGP_CUR is advanced by 4. // 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 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[TMP1]], i32 0, i32 0 // CHECK-NEXT: [[DOTREAL:%.*]] = load i64, ptr [[DOTREALP]], align 8 // CHECK-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { i64, i64 }, ptr [[TMP1]], i32 0, i32 1 @@ -156,13 +197,17 @@ void get_complex_long_long (va_list *args) { _Complex long double complex_long_double_sink; +// _Complex long double is passed indirectly, note how ARGP_CUR is advanced by 4. // 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 4 // CHECK-NEXT: store ptr [[ARGS]], ptr [[ARGS_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGS_ADDR]], align 4 -// CHECK-NEXT: [[TMP1:%.*]] = va_arg ptr [[TMP0]], ptr +// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[TMP0]], align 4 +// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 +// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[TMP0]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: [[DOTREALP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 0 // CHECK-NEXT: [[DOTREAL:%.*]] = load fp128, ptr [[DOTREALP]], align 8 // CHECK-NEXT: [[DOTIMAGP:%.*]] = getelementptr inbounds nuw { fp128, fp128 }, ptr [[TMP1]], i32 0, i32 1 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
