diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -554,6 +554,8 @@ - Patchable function entry (``-fpatchable-function-entry``) is now supported on LoongArch. +- Fixed incorrect ABI lowering related to the handling of empty structs in C++ + parameter passing. RISC-V Support ^^^^^^^^^^^^^^ diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -11927,9 +11927,9 @@ llvm::Type *&Field2Ty, CharUnits &Field2Off) const { bool IsInt = Ty->isIntegralOrEnumerationType(); bool IsFloat = Ty->isRealFloatingType(); + uint64_t Size = getContext().getTypeSize(Ty); if (IsInt || IsFloat) { - uint64_t Size = getContext().getTypeSize(Ty); if (IsInt && Size > GRLen) return false; // Can't be eligible if larger than the FP registers. Half precision isn't @@ -11954,6 +11954,24 @@ return false; } + if (isEmptyRecord(getContext(), Ty, true)) { + // Ignore the empty record in C mode. + if (Size == 0) + return true; + // Treat the empty record as char in C++ mode. + if (!Field1Ty) { + Field1Ty = llvm::Type::getInt8Ty(getVMContext()); + Field1Off = CurOff; + return true; + } + if (!Field2Ty) { + Field2Ty = llvm::Type::getInt8Ty(getVMContext()); + Field2Off = CurOff; + return true; + } + return false; + } + if (auto CTy = Ty->getAs()) { if (Field1Ty) return false; @@ -11985,8 +12003,6 @@ // copy constructor are not eligible for the FP calling convention. if (getRecordArgABI(Ty, CGT.getCXXABI())) return false; - if (isEmptyRecord(getContext(), Ty, true)) - return true; const RecordDecl *RD = RTy->getDecl(); // Unions aren't eligible unless they're empty (which is caught above). if (RD->isUnion()) @@ -12121,12 +12137,12 @@ CGCXXABI::RAA_DirectInMemory); } - // Ignore empty structs/unions. - if (isEmptyRecord(getContext(), Ty, true)) - return ABIArgInfo::getIgnore(); - uint64_t Size = getContext().getTypeSize(Ty); + // Empty records of size 0 are always ignored. + if (isEmptyRecord(getContext(), Ty, true) && Size == 0) + return ABIArgInfo::getIgnore(); + // Pass floating point values via FARs if possible. if (IsFixed && Ty->isFloatingType() && !Ty->isComplexType() && FRLen >= Size && FARsLeft) { diff --git a/clang/test/CodeGenCXX/LoongArch/abi-lp64d-empty-struct.cpp b/clang/test/CodeGenCXX/LoongArch/abi-lp64d-empty-struct.cpp --- a/clang/test/CodeGenCXX/LoongArch/abi-lp64d-empty-struct.cpp +++ b/clang/test/CodeGenCXX/LoongArch/abi-lp64d-empty-struct.cpp @@ -6,14 +6,13 @@ /// If the empty struct is a struct's member, will be treated as /// fixed-point member. /// WOA: Bit width of the argument. -/// FIXME: Empty struct is always passed or returned in C++ mode. /// 1. 0 < WOA <= GRLEN /// 1.a. Only fixed-point members. struct empty_s {}; -// CHECK-LABEL: define dso_local void @_Z12empty_struct7empty_s -// CHECK-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local i64 @_Z12empty_struct7empty_s +// CHECK-SAME: (i64 [[E_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_s empty_struct(struct empty_s e) { return e; } @@ -39,8 +38,8 @@ char c; float f32; }; -// CHECK-LABEL: define dso_local { i8, float } @_Z23empty_char_float_struct18empty_char_float_s -// CHECK-SAME: (i8 [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local i64 @_Z23empty_char_float_struct18empty_char_float_s +// CHECK-SAME: (i64 [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_char_float_s empty_char_float_struct(struct empty_char_float_s s) { return s; } @@ -51,8 +50,8 @@ struct empty_s e; float f32; }; -// CHECK-LABEL: define dso_local float @_Z18empty_float_struct13empty_float_s -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local { i8, float } @_Z18empty_float_struct13empty_float_s +// CHECK-SAME: (i8 [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_float_s empty_float_struct(struct empty_float_s s) { return s; } @@ -62,8 +61,8 @@ struct empty_s e; }; -// CHECK-LABEL: define dso_local float @_Z18float_empty_struct13float_empty_s -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local { float, i8 } @_Z18float_empty_struct13float_empty_s +// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { struct float_empty_s float_empty_struct(struct float_empty_s s) { return s; } @@ -92,8 +91,8 @@ struct empty_s e; double f64; }; -// CHECK-LABEL: define dso_local double @_Z19empty_double_struct14empty_double_s -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local { i8, double } @_Z19empty_double_struct14empty_double_s +// CHECK-SAME: (i8 [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_double_s empty_double_struct(struct empty_double_s s) { return s; } @@ -102,8 +101,8 @@ double f64; struct empty_s e; }; -// CHECK-LABEL: define dso_local double @_Z19double_empty_struct14double_empty_s -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local { double, i8 } @_Z19double_empty_struct14double_empty_s +// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { struct double_empty_s double_empty_struct(struct double_empty_s s) { return s; } @@ -115,8 +114,8 @@ float f32; char c; }; -// CHECK-LABEL: define dso_local { float, i8 } @_Z23empty_float_char_struct18empty_float_char_s -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local [2 x i64] @_Z23empty_float_char_struct18empty_float_char_s +// CHECK-SAME: ([2 x i64] [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_float_char_s empty_float_char_struct(struct empty_float_char_s s) { return s; } @@ -126,8 +125,8 @@ float f32; double f64; }; -// CHECK-LABEL: define dso_local { float, double } @_Z25empty_float_double_struct20empty_float_double_s -// CHECK-SAME: (float [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local [2 x i64] @_Z25empty_float_double_struct20empty_float_double_s +// CHECK-SAME: ([2 x i64] [[S_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_float_double_s empty_float_double_struct(struct empty_float_double_s s) { return s; } @@ -150,8 +149,8 @@ double f64; struct empty_s e2; }; -// CHECK-LABEL: define dso_local double @_Z25empty_double_empty_struct20empty_double_empty_s -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-LABEL: define dso_local void @_Z25empty_double_empty_struct20empty_double_empty_s +// CHECK-SAME: (ptr noalias sret([[STRUCT_EMPTY_DOUBLE_EMPTY_S:%.*]]) align 8 [[AGG_RESULT:%.*]], ptr noundef [[S:%.*]]) #[[ATTR0:[0-9]+]] { struct empty_double_empty_s empty_double_empty_struct(struct empty_double_empty_s s) { return s; }